Set up a Wire-Hole server on a free-tier Oracle Cloud instance
Tags: wireguard, pihole, oraclecloud, foreverfre
Published: 2023-07-26 21:05:03

### 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/](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:  

```bash
ssh -i path/to/private/key ubuntu@IPADDRESS
```

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:  

```bash
mkdir wirehole-docker && cd wirehole-docker
```

Create a file named docker-compose.yml :  

```bash
touch docker-compose.yml
```

To edit the file, you can use any in-terminal editor like vim or nano.  
Let’s use _nano_:

```bash
nano docker-compose.yml
```

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:
```yaml
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
```

### For AMD (x86):
```yaml
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
```

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`):  

```bash
cd ..
```

Create an unbound directory and head into it.  

```bash
mkdir unbound && cd unbound
```

Create the file and edit it:

```bash
touch unbound.conf && nano unbound.conf
```

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
```

Then save it. Using nano: CTRL-X, then Y, then ENTER.  
Go back to the parent directory:  

```bash
cd ..
```

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.

```bash
docker-compose up -d
```

**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**_](https://apps.apple.com/app/wireguard/id1441195209), [_**Android Play Store**_](https://play.google.com/store/apps/details?id=com.wireguard.android)) 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**](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](https://github.com/LucasPlacentino) 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:  
> 
> ```yaml
> #- ALLOWEDIPS=10.2.0.0/24
> ```
> 
> to  
> 
> ```yaml
> - 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
```

to  

```
AllowedIPs = 10.2.0.0/24
```

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](https://github.com/IAmStoxe/wirehole).
I updated it to be compatible with ARM instances, that can be found at [my wirehole-arm GitHub Repository](https://github.com/LucasPlacentino/wirehole-arm).

🚀