DEV Community

Cover image for Set up a Wire-Hole server on a free-tier Oracle Cloud instance
Lucas Placentino
Lucas Placentino

Posted on • Originally published at l0cache.substack.com

Set up a Wire-Hole server on a free-tier Oracle Cloud instance

How to create a Wireguard VPN + PiHole ad-blocking DNS instance on a forever-free Oracle Cloud VM

Want to get a forever free VPN (at a single location) including an ad-blocking DNS server? In this tutorial, we will get advantage of Oracle Cloud’s forever-free tier to get a VM instance at your choice of a datacenter location.

1. Get your VM instance

Oracle’s Cloud Infrastructure (OCI) allows you to get access to multiple Virtual Machine (VM) instances for free, for a lifetime (as opposed to AWS or GCP only offering a single year for free), this also includes IPv4 addresses for each instance.

To get your free tier OCI account, go to:

https://www.oracle.com/cloud/free/

You will also have access to some paid features for 30 days, but don’t worry, your free-tier VM will remain forever-free.

You have to choose which datacenter location you want your account to reside at, where your free instances will be running. In this tutorial, I will be using Oracle’s Amsterdam datacenter (eu-amsterdam-1).

In the OCI menu, head to Compute > Instances

Click on Create Instance.

Name your instance, for example: wirehole-server.

Under Image and Shape, click Edit.

Under Shape, click Change Shape.

Two architectures are available :

  • x86: the AMD VM.Standard.E2.1.Micro shape
    2 instances with each 1/4 vCPU and 1GB RAM.

  • ARM: the ARM Ampere VM.Standard.A1.Flex shape
    flexible: multiple instances or 1 bigger instance, so 4 vCPUs and 24GB RAM to share or in a single instance.

You can choose any one of the two you prefer. Just be aware that they might require different versions of some software you might install later while experimenting on your own.
I personally used a single, big ARM Ampere VM (with the full 4 vCPUs and 24GB of RAM on it), because I also use this instance for other purposes while running Wire-Hole.

Select your chosen shape configuration.

Next, under Image, select Change Image

Under Image source select Platform images

Select Canonical Ubuntu 22.04 (or another OS of your choice, though they are not guaranteed to work with this tutorial).

Then, scroll down do Add SSH keys

Select Generate a key pair for me. (Or put your own public key if you already have an ssh key pair generated from your computer.)

Save the private key (the .key file) on your computer, you WILL NEED it later and this is the ONLY time you will be able to download it, so keep it safe and absolutely do not share it. You can also download the public key, but we won’t need it here.

Then click on Create.

You will have to wait a couple of minutes to have your VM instance up and running. Once the yellow square turns green, it means everything is running.

Done! Your VM instance is created and running.

2. Opening up ports

In order to get access to the Wireguard server that will be running on your VM, you need to open up its port(s) in your OCI network.

In your instance page, head to Attached VNIC or Primary VNIC > Subnet > Default Security List

Click Add Ingress Rules

Then put:

  • Source CIDR: 0.0.0.0/0

  • IP Protocol: UDP

  • Destination Port Range: 51820

  • Description: WireGuard Port (or whatever you prefer)

All done! Your instance’s ports that are needed are open.

3. Setting up the Wire-Hole docker container

In order to set Wire-Hole up in your VM, we need to access its Linux console. To do that, we will use SSH to connect to it.

SSHing with your computer is as simple as just going to your Terminal (MacOS, Linux) or PowerShell (Windows) and put:

ssh -i path/to/private/key ubuntu@IPADDRESS
Enter fullscreen mode Exit fullscreen mode

Where path/to/private/key is where you saved the SSH private key on your computer, and IPADDRESS is the Public IP address of your instance (you can find it at the instance’s information, under Instance access, looking like 150.432.34.346).

Here, ubuntu is the user.

When connecting to it for the first time, your computer will ask to trust the fingerprint of the ssh server, write yes to not get this message again on future ssh sessions.

You are now remotely connected to your instance’s terminal!

We will be using docker-compose to set up the Wire-Hole docker container.

Create a directory to put our docker-compose file and head into it:

mkdir wirehole-docker && cd wirehole-docker
Enter fullscreen mode Exit fullscreen mode

Create a file named docker-compose.yml :

touch docker-compose.yml
Enter fullscreen mode Exit fullscreen mode

To edit the file, you can use any in-terminal editor like vim or nano.

Let’s use nano:

nano docker-compose.yml
Enter fullscreen mode Exit fullscreen mode

What you need to put into this YML file depends on what architecture you chose for your instance (which shape you selected: ARM Ampere or AMD x86).

For ARM Ampere:

version: "3"
networks:
  private_network:
    ipam:
      driver: default
      config:
        - subnet: 10.2.0.0/24
services:
  unbound:
    image: "mvance/unbound-rpi:latest" # or use "pedantic/unbound:latest" (which supports arm64)
    container_name: unbound
    restart: unless-stopped # or "always"
    hostname: "unbound"
    volumes:
      - "./unbound:/opt/unbound/etc/unbound/"
    networks:
      private_network:
        ipv4_address: 10.2.0.200
wireguard:
    depends_on: [unbound, pihole]
    image: linuxserver/wireguard
    container_name: wireguard
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Amsterdam # change this to your VM Timezone
      - SERVERPORT=51820
      #- SERVERURL=ddns.example.com #optional - For use with DDNS (Uncomment to use)
      - PEERS=10 # How many peers to generate for you (clients)
      - PEERDNS=10.2.0.100 # Set it to point to pihole
      - INTERNAL_SUBNET=10.6.0.0
      #- ALLOWEDIPS=10.2.0.0/24 # Split tunnel. Remove/comment if you want full tunnel (or put 0.0.0.0/0)

    volumes:
      - ./wireguard:/config
      - /lib/modules:/lib/modules
    ports:
      - "51820:51820/udp"
    dns:
      - 10.2.0.100 # Points to pihole
      - 10.2.0.200 # Points to unbound
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1
    restart: unless-stopped # or "always"
    networks:
      private_network:
        ipv4_address: 10.2.0.3
pihole:
    depends_on: [unbound]
    container_name: pihole
    image: pihole/pihole:latest
    restart: unless-stopped # or "always"
    hostname: pihole
    dns:
      - 127.0.0.1
      - 10.2.0.200 # Points to unbound
    environment:
      TZ: "Europe/Amsterdam" # change this to your VM Timezone
      WEBPASSWORD: "" # Blank password - Can be whatever you want.
      ServerIP: 10.2.0.100 # Internal IP of pihole
      DNS1: 10.2.0.200 # Unbound IP
      DNS2: 10.2.0.200 # If we don't specify two, it will auto pick  google.
    # Volumes store your data between container upgrades
    volumes:
      - "./etc-pihole/:/etc/pihole/"
      - "./etc-dnsmasq.d/:/etc/dnsmasq.d/"
    # Recommended but not required (DHCP needs NET_ADMIN)
    #   https://github.com/pi-hole/docker-pi-hole#note-on-capabilities
    cap_add:
      - NET_ADMIN
    networks:
      private_network:
        ipv4_address: 10.2.0.100
Enter fullscreen mode Exit fullscreen mode

For AMD (x86):

version: "3"
networks:
  private_network:
    ipam:
      driver: default
      config:
        - subnet: 10.2.0.0/24
services:
  unbound:
    image: "mvance/unbound:latest" # for x86 arch
    container_name: unbound
    restart: unless-stopped # or "always"
    hostname: "unbound"
    volumes:
      - "./unbound:/opt/unbound/etc/unbound/"
    networks:
      private_network:
        ipv4_address: 10.2.0.200
wireguard:
    depends_on: [unbound, pihole]
    image: linuxserver/wireguard
    container_name: wireguard
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Amsterdam # change this to your VM Timezone
      - SERVERPORT=51820
      #- SERVERURL=ddns.example.com #optional - For use with DDNS (Uncomment to use)
      - PEERS=10 # How many peers to generate for you (clients)
      - PEERDNS=10.2.0.100 # Set it to point to pihole
      - INTERNAL_SUBNET=10.6.0.0
      #- ALLOWEDIPS=10.2.0.0/24 # Split tunnel. Remove/comment if you want full tunnel (or put 0.0.0.0/0)

    volumes:
      - ./wireguard:/config
      - /lib/modules:/lib/modules
    ports:
      - "51820:51820/udp"
    dns:
      - 10.2.0.100 # Points to pihole
      - 10.2.0.200 # Points to unbound
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1
    restart: unless-stopped # or "always"
    networks:
      private_network:
        ipv4_address: 10.2.0.3
pihole:
    depends_on: [unbound]
    container_name: pihole
    image: pihole/pihole:latest
    restart: unless-stopped # or "always"
    hostname: pihole
    dns:
      - 127.0.0.1
      - 10.2.0.200 # Points to unbound
    environment:
      TZ: "Europe/Amsterdam" # change this to your VM Timezone
      WEBPASSWORD: "" # Blank password - Can be whatever you want.
      ServerIP: 10.2.0.100 # Internal IP of pihole
      DNS1: 10.2.0.200 # Unbound IP
      DNS2: 10.2.0.200 # If we don't specify two, it will auto pick google.
    # Volumes store your data between container upgrades
    volumes:
      - "./etc-pihole/:/etc/pihole/"
      - "./etc-dnsmasq.d/:/etc/dnsmasq.d/"
    # Recommended but not required (DHCP needs NET_ADMIN)
    #   https://github.com/pi-hole/docker-pi-hole#note-on-capabilities
    cap_add:
      - NET_ADMIN
    networks:
      private_network:
        ipv4_address: 10.2.0.100
Enter fullscreen mode Exit fullscreen mode

All you have to do is change TZ= to your Time Zone.

You can then save the file. Using nano (used here), to save the file you have to press CTRL-X, then press Y, then press ENTER to confirm.

After this, we need to create the unbound config file.

Go back to the previous directory (wirehole-docker):

cd ..
Enter fullscreen mode Exit fullscreen mode

Create an unbound directory and head into it.

mkdir unbound && cd unbound
Enter fullscreen mode Exit fullscreen mode

Create the file and edit it:

touch unbound.conf && nano unbound.conf
Enter fullscreen mode Exit fullscreen mode

Put this into the file:

server:
    cache-max-ttl: 86400
    cache-min-ttl: 60
    directory: "/opt/unbound/etc/unbound"
    edns-buffer-size: 1472
    interface: 0.0.0.0@53
    rrset-roundrobin: yes
    username: "_unbound"
    log-local-actions: no
    log-queries: no
    log-replies: no
    log-servfail: no
    logfile: /dev/null
    verbosity: 0
    aggressive-nsec: yes
    delay-close: 10000
    do-daemonize: no
    do-not-query-localhost: no
    neg-cache-size: 4M
    qname-minimisation: yes
    access-control: 127.0.0.1/32 allow
    access-control: 192.168.0.0/16 allow
    access-control: 172.16.0.0/12 allow
    access-control: 10.0.0.0/8 allow
    auto-trust-anchor-file: "var/root.key"
    chroot: "/opt/unbound/etc/unbound"
    harden-algo-downgrade: yes
    harden-below-nxdomain: yes
    harden-dnssec-stripped: yes
    harden-glue: yes
    harden-large-queries: yes
    harden-referral-path: no
    harden-short-bufsize: yes
    hide-identity: yes
    hide-version: yes
    identity: "DNS"
    private-address: 10.0.0.0/8
    private-address: 172.16.0.0/12
    private-address: 192.168.0.0/16
    private-address: 169.254.0.0/16
    private-address: fd00::/8
    private-address: fe80::/10
    private-address: ::ffff:0:0/96
    tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt
    unwanted-reply-threshold: 10000000
    val-clean-additional: yes
    msg-cache-size: 260991658
    num-queries-per-thread: 4096
    outgoing-range: 8192
    rrset-cache-size: 260991658
    minimal-responses: yes
    prefetch: yes
    prefetch-key: yes
    serve-expired: yes
    so-reuseport: yes
    so-rcvbuf: 1m
    remote-control:
        control-enable: no
Enter fullscreen mode Exit fullscreen mode

Then save it. Using nano: CTRL-X, then Y, then ENTER.

Go back to the parent directory:

cd ..
Enter fullscreen mode Exit fullscreen mode

And voilà, that’s configured!

4. Running your Wire-Hole

To start the Wire-Hole docker-container, just run:

Make sure you are in the wirehole-docker directory.

docker-compose up -d
Enter fullscreen mode Exit fullscreen mode

All done! Your Wire-Hole should be running smoothly.

You will get in the console a QR code that you can use to set up the VPN connection on your phone using the Wireguard app (available on Android and iOS).

5. Setting up your device to connect to your VPN

For a phone, simply scan the QR code you saw with the Wireguard app (iOS App Store, Android Play Store) to set up the device’s VPN connection.

6. Accessing Pi-Hole’s interface

While connected to the Wireguard VPN, go to http://10.2.0.100/admin to access Pi-Hole’s web interface. The password should be blank.

You can then change the password to the interface and configure your blocklist.

That’s it! You are using your very own VPN and ad-blocking DNS server. If you appreciated this tutorial, please follow my Dev.to to see other blog posts, and my GitHub where I keep pretty much everything I do.

[OPTIONAL] Linking a domain to your instance

If you own a domain, you can configure a subdomain to use for your VPN.

Add a type “A” entry to your domain’s DNS management pointing to your VM instance’s IPv4 address, with any subdomain you want.
For example:

A : wireguard(.yourdomain.com) > 153.452.56.143

You can now connect to your VPN via your domain, rather than the public IP address.

[OPTIONAL] Setting up a half-tunnel connection

[NOTE]: As of 2023-03-26, this is not working properly, any suggestion for a fix is welcome.

Using a half-tunnel connection to your VPN allows you to only pass your DNS request to your server (thus only using Pi-Hole ad-blocking DNS) and still route all other traffic without a VPN, allowing for faster speeds.

You will get all the ad-blocking you were getting with the full tunnel, while retaining your full internet speed.

Note: this does not encrypt your traffic and does not change your public IP, so no security advantages from using a VPN.

In your docker-compose.yml, uncomment:

#- ALLOWEDIPS=10.2.0.0/24

to

- ALLOWEDIPS=10.2.0.0/24

And in your device’s Wireguard VPN configuration, you need to change

AllowedIPs = 0.0.0.0/0, ::/0
Enter fullscreen mode Exit fullscreen mode

to

AllowedIPs = 10.2.0.0/24
Enter fullscreen mode Exit fullscreen mode

Save it, and there is your half-tunnel set up!

For more in-depth informations and further configurations, go to https://github.com/IAmStoxe/wirehole.

This tutorial is based on IAmStoxe’s wirehole GitHub Repository.
I updated it to be compatible with ARM instances, that can be found at my wirehole-arm GitHub Repository.

🚀

Top comments (0)