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, which gives you $300 of free credit to spend over 12 months: 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 [email protected]$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
WireGuardis an extremely fast and modern VPN solution with a focus on security.
BoringTunis 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 = 126.96.36.199 # server ip address PrivateKey = 4IXGgjoTa/HJJVoFS1ofmGZTTt5c3Q= # server public key ListenPort = 51820 # server port [Peer] PublicKey = MOhoJcQaWggUeZ6nbYorIBTTV64ZvVY= # client public key AllowedIPs = 188.8.131.52/32 # client ip address
Great, we can now bring up the tunnel using
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 184.108.40.206 dev wg0 [#] ip link set mtu 1380 up dev wg0 [#] ip -4 route add 220.127.116.11/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: 18.104.22.168/32
I will use macOS’ 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 = 22.214.171.124/32 # Client IP address [Peer] PublicKey = 5bjWGUZ5hjX... # Server public key AllowedIPs = 126.96.36.199/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 188.8.131.52 PING 184.108.40.206 (220.127.116.11): 56 data bytes 64 bytes from 18.104.22.168: icmp_seq=0 ttl=64 time=14.222 ms 64 bytes from 22.214.171.124: icmp_seq=1 ttl=64 time=16.634 ms 64 bytes from 126.96.36.199: 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. 188.8.131.52 ;; connection timed out; no servers could be reached ...
Let’s instruct WireGuard to NAT all traffic on the main interface.
Add the following
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 = 184.108.40.206 # 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 = 220.127.116.11/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 = 18.104.22.168/32 DNS = 22.214.171.124 # 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: 126.96.36.199/24 allow private-address: 188.8.131.52/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: Starting Unbound DNS server... Sep 13 vpn package-helper: /var/lib/unbound/root.key has content Sep 13 vpn package-helper: success: the anchor is ok Sep 13 vpn systemd: Started Unbound DNS server. Sep 13 unbound: [15868:0] info: start of service (unbound 1.6.7).
Finally, check 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 your browser is set to a different location than
Netherlands, I suggest updating it.