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.
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 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
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
Create a file named docker-compose.yml :
touch docker-compose.yml
To edit the file, you can use any in-terminal editor like vim or nano.
Let’s use nano:
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:
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):
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):
cd ..
Create an unbound directory and head into it.
mkdir unbound && cd unbound
Create the file and edit it:
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:
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.
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, 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 Medium 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 2022-07-28, 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
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.
I updated it to be compatible with ARM instances, that can be found at my wirehole-arm GitHub Repository