Disney just started rolling out their new streaming service, Disney+. The service is currently only available in the Netherlands for free, with an official launch later in September. The catalogue is quite limited at the moment, but seeing as the service is free, we really have no reason to complain.
With all the excitement building around Disney+, I’m sure many people would love to get their hands on the streaming service and start watching a timeless Disney Classic.
In the following guide, we’ll set up a Google Cloud instance running in the Netherlands region, install boringtun, a VPN implementation of Wireguard protocol, and tweak the config to route all traffic through the VPN.
As a side note, I should mention that the Disney+ Streaming Service is currently solely aimed at Dutch users.
I assume that you already have access to Google Cloud Console with a dummy project set up for this.
If you don’t, feel free to try their Free Tier program: https://cloud.google.com/free
# Set the GCP Project ID
$ export GCP_PROJECT_ID=vpn
# Netherlands Region
$ export GCP_ZONE=europe-west4-a
# Change this to your public IP Address
$ export CLIENT_IP_ADDR='<MY-IP-ADDRESS>'
# Log on GCP
$ gcloud auth login
$ gcloud config set project $GCP_PROJECT_ID
$ gcloud config set compute/zone $GCP_ZONE
Create a Service Account that allows SSH access
$ gcloud iam service-accounts create ssh-account \
--project $GCP_PROJECT_ID \
--display-name "ssh-account"
Create a network to run the workload on
$ gcloud compute networks create vpn-net \
--bgp-routing-mode=regional \
--project $GCP_PROJECT_ID
Add firewall rules to access the instance
# Allow SSH access
$ gcloud compute firewall-rules create allow-ssh \
--project $GCP_PROJECT_ID \
--network vpn-net --allow tcp:22 \
--source-ranges "$CLIENT_IP_ADDR/32"
# Allow VPN tunneling (wireguard)
$ gcloud compute firewall-rules create allow-wireguard \
--project $GCP_PROJECT_ID \
--network vpn-net --allow udp:51820 \
--source-ranges "$CLIENT_IP_ADDR/32"
Spin up an f1-micro
instance running on vpn-net
network, with ubuntu bionic
distribution. The ssh-account
service account previously created gets attached to the instance in order for us to SSH on the box
$ gcloud compute instances create vpn \
--project $GCP_PROJECT_ID \
--network vpn-net \
--service-account ssh-account@$PROJECT_ID.iam.gserviceaccount.com \
--scopes https://www.googleapis.com/auth/cloud-platform \
--zone $GCP_ZONE \
--can-ip-forward \
--image ubuntu-minimal-1804-bionic-v20190911 \
--image-project ubuntu-os-cloud \
--machine-type f1-micro
Wait a few seconds for the instance to become reachable, then you can SSH on it
$ gcloud compute ssh vpn \
--project $GCP_PROJECT_ID \
--zone $GCP_ZONE
Now that we’re connected on the instance, let’s install all the necessary tools to create a VPN tunnel
Wireguard
is an extremely fast and modern VPN solution with a focus on security.BoringTun
is is a Rust userspace implementation of Wireguard’s protocol# All following commands as run as sudo
vpn:~$ apt update
# Essential for Cargo crates
vpn:~$ apt install -y \
vim \
gcc \
build-essential \
software-properties-common
# If you, like me, need to do some network debugging
vpn:~$ apt install -y \
dns-utils \
iputils-ping
# Install wiregard
vpn:~$ add-apt-repository ppa:wireguard/wireguard -y
vpn:~$ apt update && apt install -y wireguard
# Install rust
vpn:~$ curl https://sh.rustup.rs -sSf | sh -s -- -y
vpn:~$ source $HOME/.cargo/env
# Let's fetch the latest version of BoringTun
# The current release (v0.2.0) throws `mismatched types` errors
vpn:~$ cargo install \
--git https://github.com/cloudflare/boringtun
Use wireguard keytools to generate keys
vpn:~$ wg genkey | tee privatekey | wg pubkey > publickey
vpn:~$ ls -ll
-rw------- 1 root root 45 Sep 13 11:26 privatekey
-rw------- 1 root root 45 Sep 13 11:26 publickey
Create a wireguard config with the following
vpn:~$ cat /etc/wireguard/wg0.conf
[Interface]
Address = 172.168.2.1 # server ip address
PrivateKey = 4IXGgjoTa/HJJVoFS1ofmGZTTt5c3Q= # server public key
ListenPort = 51820 # server port
[Peer]
PublicKey = MOhoJcQaWggUeZ6nbYorIBTTV64ZvVY= # client public key
AllowedIPs = 172.168.2.3/32 # client ip address
Great, now that we have a config in place, bring up the tunnel, using boringtun
vpn:~$ WG_QUICK_USERSPACE_IMPLEMENTATION=boringtun WG_SUDO=1 wg-quick up wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 172.168.2.1 dev wg0
[#] ip link set mtu 1380 up dev wg0
[#] ip -4 route add 172.168.2.3/32 dev wg0
Let’s verify that Wireguard runs as expected
vpn:~$ wg show
interface: wg0
public key: 5bjWGUZ5hI...
private key: (hidden)
listening port: 51820
peer: MOhoJcQaWgx...
allowed ips: 172.168.2.3/32
I will use macOS’s client as an example, but you can download the client that suits you the best, the steps should be pretty similar. https://www.wireguard.com/install/
As well as the GUI, I’ve also installed the CLI toolkit
$ brew install wireguard-tools
Create a new Empty Tunnel and populate with the following settings
[Interface]
PrivateKey = 4MfFvqx8egPq... # Client private key
ListenPort = 21841
Address = 172.168.2.3/32 # Client IP address
[Peer]
PublicKey = 5bjWGUZ5hjX... # Server public key
AllowedIPs = 172.168.2.0/24 # Traffic for these CIDRS will go over the VPN
Endpoint = xxx.xxx.xxx.xxx:51820 # Server public key
Activate the tunnel, and make sure that the connection is established
$ ping 172.168.2.1
PING 172.168.2.1 (172.168.2.1): 56 data bytes
64 bytes from 172.168.2.1: icmp_seq=0 ttl=64 time=14.222 ms
64 bytes from 172.168.2.1: icmp_seq=1 ttl=64 time=16.634 ms
64 bytes from 172.168.2.1: icmp_seq=2 ttl=64 time=17.173 ms
3 packets transmitted, 3 packets received, 0.0% packet loss
Even though the tunnel is fully set up, we still can’t reach other websites via Wireguard
$ nslookup www.google.com. 172.168.2.1
;; connection timed out; no servers could be reached
...
Let’s instruct Wireguard to NAT all traffic on the main interface.
Add the following PostUp
and PostDown
commands to the Server config so that it looks like:
# Bring the VPN down
vpn:~$ WG_QUICK_USERSPACE_IMPLEMENTATION=boringtun WG_SUDO=1 wg-quick down wg0
# Full Server side config
vpn:~$ cat /etc/wireguard/wg0.conf
[Interface]
Address = 172.168.2.1 # server ip address
PrivateKey = 4IXGgjoT... # server public key
ListenPort = 51820 # server port
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o ens4 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o ens4 -j MASQUERADE
[Peer]
PublicKey = MOhoJcQaWg... # client public key
AllowedIPs = 172.168.2.3/32 # client ip address
# Bring the VPN back up
vpn:~$ WG_QUICK_USERSPACE_IMPLEMENTATION=boringtun WG_SUDO=1 wg-quick up wg0
NB: I use ens4
as this is the default interface on ubuntu 18.04 instances, make sure you set the correct interface
Ensure IP forwarding in set up
vpn:~$ echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf && \
echo "net.ipv4.conf.all.proxy_arp = 1" >> /etc/sysctl.conf
vpn:~$ sysctl -p /etc/sysctl.conf
Last but not least, update AllowedIPs
parameter in the Client’s config to route all traffic through the VPN. Here is what the full config looks like:
[Interface]
PrivateKey = 4MfFvqx8egPq...
ListenPort = 21841
Address = 172.168.2.3/32
DNS = 172.168.2.1 # Set the DNS server as the Wireguard instance, needed for later
MTU = 1300 # I use a smaller value as I had issues with the default MTU
[Peer]
PublicKey = 5bjWGUZ5hjX...
AllowedIPs = 0.0.0.0/0, ::/0 # Route all traffic through
Endpoint = xxx.xxx.xxx.xxx:51820
PersistentKeepalive = 25 # Keep the connection up
Re-activate the connection to take changes into account.
Everything looks fine, but we are still unable to resolve DNS. Let’s install unbound
, an excellenet lightweight DNS resolver.
vpn:~$ apt-get install unbound unbound-host
# Fetch latest hints instead of using the builtin ones
vpn:~$ curl --output /var/lib/unbound/root.hints https://www.internic.net/domain/named.cache
# Edit unbound's config
vpn:~$ cat /etc/unbound/unbound.conf
server:
num-threads: 4
root-hints: "/var/lib/unbound/root.hints"
# Use the root servers key for DNSSEC
auto-trust-anchor-file: "/var/lib/unbound/root.key"
# Respond to DNS requests on all interfaces
interface: 0.0.0.0
max-udp-size: 3072
# ACLS
access-control: 0.0.0.0/0 refuse
access-control: 127.0.0.1 allow
access-control: 172.168.2.0/24 allow
private-address: 172.168.2.0/24
# Hide DNS Server info
hide-identity: yes
hide-version: yes
# Limit DNS Fraud and use DNSSEC
harden-glue: yes
harden-dnssec-stripped: yes
harden-referral-path: yes
# Add an unwanted reply threshold to clean the cache and avoid when possible a DNS Poisoning
unwanted-reply-threshold: 10000000
# Have the validator print validation failures to the log.
val-log-level: 1
# Config for RRsets and messages in the cache
cache-min-ttl: 1800
cache-max-ttl: 14400
prefetch: yes
prefetch-key: yes
# Set the right permissions and enable the service
vpn:~$ chown -R unbound:unbound /var/lib/unbound
vpn:~$ systemctl enable unbound
# Stop systemd-resolved and let unbound take over
vpn:~$ systemctl disable systemd-resolved && systemctl stop systemd-resolved
vpn:~$ systemctl start unbound
Check the logs to make sure unbound is properly started
vpn:~$ journalctl -fu unbound
-- Logs begin at Fri 2019-09-13 10:45:21 UTC. --
Sep 13 vpn systemd[1]: Starting Unbound DNS server...
Sep 13 vpn package-helper[15855]: /var/lib/unbound/root.key has content
Sep 13 vpn package-helper[15855]: success: the anchor is ok
Sep 13 vpn systemd[1]: Started Unbound DNS server.
Sep 13 unbound[15868]: [15868:0] info: start of service (unbound 1.6.7).
Finally, test that DNS resolving works as expected on the client side
$ curl -IL --silent https://disneyplus.com
HTTP/1.1 200 OK
Content-Length: 51850
Content-Type: text/html; charset=utf-8
Server: nginx/1.12.1
Strict-Transport-Security: max-age=15552000; includeSubDomains
Connection: keep-alive
Head over to https://disneyplus.com/nl/ and enjoy.
If you browser is set to a different location, I suggest updating it.
These articles have been particularly helpful:
• https://git.zx2c4.com/WireGuard/about/src/tools/man/wg-quick.8
• https://www.wireguard.com/netns/#routing-all-your-traffic
• https://www.ckn.io/blog/2017/11/14/wireguard-vpn-typical-setup/
• https://cloud.google.com/vpn/docs/concepts/mtu-considerations
• https://gist.github.com/Overbryd/ab15ee86c58260cb6d0be634a4c58057
• https://nlnetlabs.nl/documentation/unbound/unbound.conf/
• https://github.com/cloudflare/boringtun