A few months ago I signed up for the free-tier of Oracle cloud to give it a shot, because I needed VPN egress from a Canadian datacenter in order to access some Canada-only services, god save the queen. My bank for example is stuck in the stone-age and would constantly hassle me for SMS 2FA which is a joke.
If you want to see how I deploy Wireguard on an arbitrary VPS, and whether to tax your sanity with Oracle cloud then read on.
Deploying wireguard was mostly easy, but as with every cloud provider out there they opt to name and structure all their services completely differently so I had to waste my time learning their snowflake stack. What is Oracle linux? Hell if I know or care.
Relevant variables
There are a couple of values you need from your vps. The rest are up to you. These key generation steps should be in a playbook too. Maybe later. You’ll need keypairs for each server and client(s).
$ umask 077 && wg genkey | tee privatekey | wg pubkey > publickey
~/projects/gwt/host_vars/secret_oracle_tunnel.yml
---
# ipv4 on which your VPS is reachable from the internet.
# In Oracle's case it was NOT on the ens3 subnet, but still routed.
external_server_ipv4: ...
# Could be eth0 or something else. Check with ip a.
public_network_interface: ens3
# Subnet on ens3 that is used for external connection
public_subnet: 10.0.0.0/16
# Up to you
wg_subnet_ipv4: 192.168.178.0/24
wg_subnet_ipv6: fd42:42:42::/64
wg_port: 55107
wg_interface: wg0
wg_server_ipv4: 192.168.178.1
wg_server_ipv6: fd42:42:42::1
wg_server_private_key: "{{ vault_wg_oracle_server_private_key }}"
wg_server_public_key: "{{ vault_wg_oracle_server_public_key }}"
Firewall
Open port 55107 on the VPS.
Ansible playbooks for wg0.conf and client.conf
~/projects/gwt/wg-oracle-server.yml
---
- hosts: secret_oracle_tunnel
gather_facts: false
become: true
tasks:
- name: Setup a wireguard server
include_role:
name: wireguard
tasks_from: server
~/projects/gwt/roles/wireguard/tasks/server.yml
---
- name: Update apt cache
ansible.builtin.apt:
update_cache: true
cache_valid_time: 600
changed_when: false
- name: Install packages
ansible.builtin.apt:
name:
- wireguard
state: present
- name: Create helpers directory
ansible.builtin.file:
path: /etc/wireguard/helpers
state: directory
owner: root
group: root
mode: "770"
- name: Copy NAT helper scripts
ansible.builtin.template:
src: "{{ item }}.j2"
dest: "/etc/wireguard/helpers/{{ item }}"
mode: "770"
loop:
- add-nat-rules.sh
- remove-nat-rules.sh
- name: Copy wg0.conf
ansible.builtin.template:
src: "wg0.conf.j2"
dest: "/etc/wireguard/wg0.conf"
mode: "770"
notify:
- enable systemd daemon
- restart wireguard interface
- name: Enable packet forwarding between internet NIC and wg0
become: true
ansible.builtin.lineinfile:
dest: /etc/sysctl.conf
state: present
line: "{{ item }}"
loop:
- net.ipv4.ip_forward=1
- net.ipv6.conf.all.forwarding=1
notify: reload sysctl config
~/projects/gwt/roles/wireguard/templates/wg0.conf.j2
[Interface]
Address = {{ wg_server_ipv4 }}/24, {{ wg_server_ipv6 }}/64
ListenPort = {{ wg_port }}
PrivateKey = {{ wg_server_private_key }}
PostUp = /etc/wireguard/helpers/add-nat-rules.sh
PostDown = /etc/wireguard/helpers/remove-nat-rules.sh
~/projects/gwt/wg-client.yml
- hosts: secret_oracle_tunnel
gather_facts: false
vars:
wg_peer_ipv4: 192.168.178.2
wg_peer_ipv6: fd42:42:42::2
wg_peer_public_key: "{{ vault_wg_peer_public_key }}"
tasks:
- name: Append peer publickey to wg0 server conf
include_role:
name: wireguard
tasks_from: client
- hosts: 127.0.0.1
connection: local
vars_files:
- host_vars/secret_oracle_tunnel.yml
vars:
wg_peer_private_key: "{{ vault_wg_peer_private_key }}"
wg_peer_ipv4: 192.168.178.2
wg_peer_ipv6: fd42:42:42::2
tasks:
- name: Generate a client.conf in the playbook dir
ansible.builtin.template:
src: roles/wireguard/templates/client.conf.j2
dest: "{{ playbook_dir }}/client.conf"
~/projects/gwt/roles/wireguard/tasks/client.yml
- hosts: secret_oracle_tunnel
gather_facts: false
vars:
wg_peer_ipv4: 192.168.178.2
wg_peer_ipv6: fd42:42:42::2
wg_peer_public_key: "{{ vault_wg_peer_public_key }}"
tasks:
- name: Append peer publickey to wg0 server conf
include_role:
name: wireguard
tasks_from: client
- hosts: 127.0.0.1
connection: local
vars_files:
- host_vars/secret_oracle_tunnel.yml
vars:
wg_peer_private_key: "{{ vault_wg_peer_private_key }}"
wg_peer_ipv4: 192.168.178.2
wg_peer_ipv6: fd42:42:42::2
tasks:
- name: Generate a client.conf in the playbook dir
ansible.builtin.template:
src: roles/wireguard/templates/client.conf.j2
dest: "{{ playbook_dir }}/client.conf"
[Interface]
Address = {{ wg_server_ipv4 }}/24, {{ wg_server_ipv6 }}/64
ListenPort = {{ wg_port }}
PrivateKey = {{ wg_server_private_key }}
PostUp = /etc/wireguard/helpers/add-nat-rules.sh
PostDown = /etc/wireguard/helpers/remove-nat-rules.sh
Wireguard will execute any shell scripts you place in /etc/wireguard/helpers
so that’s a good spot for all the iptables PostUp and PostDown stuff below.
~/projects/gwt/roles/wireguard/templates/add-nat-rules.sh.j2
#!/bin/bash
## IPv4
iptables -t nat -I POSTROUTING 1 -s {{ public_subnet }} -o {{ public_network_interface }} -j MASQUERADE
iptables -t nat -I POSTROUTING 1 -s {{ wg_subnet_ipv4 }} -o {{ public_network_interface }} -j MASQUERADE
#Allow all traffic on {{ wg_interface }} interface:
iptables -I INPUT 1 -i {{ wg_interface }} -j ACCEPT
iptables -I FORWARD 1 -i {{ public_network_interface }} -o {{ wg_interface }} -j ACCEPT
iptables -I FORWARD 1 -i {{ wg_interface }} -o {{ public_network_interface }} -j ACCEPT
iptables -I INPUT 1 -i {{ public_network_interface }} -p udp --dport {{ wg_port }} -j ACCEPT
## IPv6
ip6tables -t nat -I POSTROUTING 1 -s {{ wg_subnet_ipv6 }} -o {{ public_network_interface }} -j MASQUERADE
ip6tables -I INPUT 1 -i {{ wg_interface }} -j ACCEPT
ip6tables -I FORWARD 1 -i {{ public_network_interface }} -o {{ wg_interface }} -j ACCEPT
ip6tables -I FORWARD 1 -i {{ wg_interface }} -o {{ public_network_interface }} -j ACCEPT
remove-nat-rules.sh.j2
is just the reverse, with -D
instead of -I
Success so clearly in view, or is it merely a trick of the light?
After starting the tunnel with wg-quick up client.conf
and verifying that ping 192.168.178.1
succeeds and curl -4 icanhazip.com
reports the vps ip, I was ready to chalk up another victory.
Except then the emails started coming in a few days later:


Very confusing. I thought my account was locked and the VPS deleted, but that was not the case. Just some really weirdly threatening copy.