Build your own VPN service
Install spire to your path
$ curl -O https://raw.githubusercontent.com/epiccastle/spire/master/scripts/install
$ bash install
Create A Cloud Server
You can use your favourite cloud provider to create a linux Virtual Private Server. In this tutorial we take you through the use of Digital Ocean to create this server
[ create $5/month instance ]
- Create an account and login
- Upload your ssh public key (normally
~/.ssh/id_rsa.pub
) to digital ocean account - Click on "Create" and then on "Droplets"
- Select the following: image: Ubuntu 18.04.3 (LTS) x64 plan: standard - $5/mo datacenter region: choose a region where you want your VPN traffic to appear from additional option: Monitoring (IPv6) authentication: SSH keys, then select your key hostname: choose a name like countryname.mydomain.com where country name is the location endpoint you have chosen, and mydomain.com is your domain name. If you do not have a domain name you can put anything here.
- Click "Create Droplet"
- Wait for the machine to be created
- Write down the machines IP number. The machine I built has an IP of 159.203.119.225. Anywhere you see 159.203.119.225 from now on, write your real IP number instead
- Upload your ssh public key (normally
Write A Blueprint To Provision The Cloud Server
Create the following blueprint. Create a file wireguard.clj
with the following contents:
(ssh "root@159.203.119.225"
(get-fact [:system]))
Replace 159.203.119.225
with the IP number of your new cloud machine.
Run the blueprint with spire
to connect and then report the type of system it is. When it asks "Are you sure you want to continue connecting?" answer by typing y
and hitting enter.
$ spire wireguard.clj
The authenticity of host '159.203.119.225' can't be established.
RSA key fingerprint is 43:d6:ed:1e:86:26:f2:5a:8a:ed:06:35:99:a3:6f:8b.
Are you sure you want to continue connecting? y
{:codename :bionic,
:description "Ubuntu 18.04.3 LTS",
:distro :ubuntu,
:os :linux,
:platform :x86_64,
:release "18.04",
:shell :bash}
Install wireguard on the server
Now we know we can connect, lets provision the machine.
The installation instructions for wireguard [https://www.wireguard.com/install/] tell us we need to install a wireguard
package from a ppa. Let's do that now. Change the wireguard.clj
to read:
(ssh "root@159.203.119.225"
(apt-repo :present "ppa:wireguard/wireguard")
(apt :update)
(apt :install "wireguard"))
Let's run this to install wireguard...
$ spire wireguard.clj
wireguard.clj:2 (apt-repo :present "ppa:wireguard/wireguard") root@159.203.119.225
wireguard.clj:3 (apt :update) root@159.203.119.225
wireguard.clj:4 (apt :install "wireguard") root@159.203.119.225
{:err "",
:exit 0,
...
}
Generate server keypair
Lets generate a key pair for the server and return it. We will run this on the server for now. wireguard.clj
becomes:
(require '[clojure.string :as string])
(ssh "root@159.203.119.225"
(apt-repo :present "ppa:wireguard/wireguard")
(apt :update)
(apt :install "wireguard")
(shell {:cmd "umask 077 && wg genkey | tee privatekey | wg pubkey > publickey"
:creates ["privatekey" "publickey"]})
{:private-key (string/trim (:out (get-file "privatekey")))
:public-key (string/trim (:out (get-file "publickey")))})
$ spire wireguard.clj
wireguard.clj:4 (apt-repo :present "ppa:wireguard/wireguard") root@159.203.119.225
wireguard.clj:5 (apt :update) root@159.203.119.225
wireguard.clj:6 (apt :install "wireguard") root@159.203.119.225
wireguard.clj:7 (shell {:creates ["privatekey" "publickey"], :cmd "umask 077 && wg genkey | tee privatekey | wg pu
wireguard.clj:9 (get-file "privatekey") root@159.203.119.225
wireguard.clj:10 (get-file "publickey") root@159.203.119.225
{:private-key "8M5di1Sahxqe0hlnQMAKTN4YRx9mUMPC9upfGr9BcE8=",
:public-key "rKjdEHcNNClS5cyhPpAx0/eKaswhxmJHrFMwx+5ZAn4="}
We will need to use these keys in setting up our local client.
Generate client keypair
Note: This local installation requires you to be running ubuntu linux on the local client
Lets connect to localhost and generate some client keys. We can break out some of our wireguard installer into some functions now to avoid repeating ourselves. Change wireguard.clj
to:
(require '[clojure.string :as string])
(defn install []
(apt-repo :present "ppa:wireguard/wireguard")
(apt :update)
(apt :install "wireguard"))
(defn generate-keypair []
(shell {:cmd "umask 077 && wg genkey | tee privatekey | wg pubkey > publickey"
:creates ["privatekey" "publickey"]})
{:private-key (string/trim (:out (get-file "privatekey")))
:public-key (string/trim (:out (get-file "publickey")))})
(let [server-keys (ssh "root@159.203.119.225"
(install)
(generate-keypair))
client-keys (ssh "root@localhost"
(install) ;; comment out this line if not running a Debian derivative locally
(generate-keypair))]
{:server server-keys
:client client-keys})
Now running this gives:
$ spire wireguard.clj
wireguard.clj:4 (apt-repo :present "ppa:wireguard/wireguard") root@159.203.119.225 root@localhost
wireguard.clj:5 (apt :update) root@159.203.119.225 root@localhost
wireguard.clj:6 (apt :install "wireguard") root@159.203.119.225 root@localhost
wireguard.clj:9 (shell {:creates ["privatekey" "publickey"], :cmd "umask 077 && wg genkey | tee privatekey | wg pu
wireguard.clj:11 (get-file "privatekey") root@159.203.119.225 root@localhost
wireguard.clj:12 (get-file "publickey") root@159.203.119.225 root@localhost
{:client {:private-key "YJaxgsPuQsWijT0lbcMCjDzBuC7OkDk7RK5DTUunpl0=",
:public-key "NcOb0sNKGf4uXwH4W90geHVd7/eGyW8zYESfx9KZSR8="},
:server {:private-key "8M5di1Sahxqe0hlnQMAKTN4YRx9mUMPC9upfGr9BcE8=",
:public-key "rKjdEHcNNClS5cyhPpAx0/eKaswhxmJHrFMwx+5ZAn4="}}
Complete the setup of the client and server
Now we have all the information we need to setup both the client and server configurations.
Note This assumes you are running Debian Linux or a derivative as a client and will try to apt-get install wireguard locally. If you are not running a Debian derivative locally, comment out line 18 and install wireguard locally by hand.
Change wireguard.clj
to read:
(require '[clojure.string :as string])
(defn install []
(apt-repo :present "ppa:wireguard/wireguard")
(apt :update)
(apt :install "wireguard"))
(defn generate-keypair []
(shell {:cmd "umask 077 && wg genkey | tee privatekey | wg pubkey > publickey"
:creates ["privatekey" "publickey"]})
{:private (string/trim (:out (get-file "privatekey")))
:public (string/trim (:out (get-file "publickey")))})
(let [server-keys (ssh "root@159.203.119.225"
(install)
(generate-keypair))
client-keys (ssh "root@localhost"
(install) ;; comment out this line if not running a Debian derivative locally
(generate-keypair))]
(ssh "root@159.203.119.225"
(upload {:content (selmer "wireguard-server.conf"
{:wan-ip "159.203.119.225"
:private (:private server-keys)
:peers [{:name "my desktop"
:public (:public client-keys)
:allowed-ips "10.20.30.40/32"
:keepalive "120"}]})
:dest "/etc/wireguard/wg0.conf"
:mode 0600})
(sysctl :present {:name "net.ipv4.ip_forward" :value "1"})
(service :restarted {:name "wg-quick@wg0"}))
(ssh "root@localhost"
(upload {:content (selmer "wireguard-client.conf"
{:wan-ip "10.20.30.40/24"
:private (:private client-keys)
:peer {:public (:public server-keys)
:endpoint "159.203.119.225"}})
:dest "/etc/wireguard/vpn-tunnel.conf"
:mode 0600})))
You will need to write the server config templates.
In the same directory, put the following in wireguard-server.conf
:
[Interface]
Address = {{ wan-ip }}
PrivateKey = {{ private }}
ListenPort = 51820
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
{% for peer in peers %}
# {{ peer.name }}
[Peer]
PublicKey = {{ peer.public }}
{% if peer.endpoint %}
Endpoint = {{ peer.endpoint }}
{% endif %}
{% if peer.allowed-ips %}
AllowedIPs = {{ peer.allowed-ips }}
{% endif %}
{% if peer.keepalive %}
PersistentKeepalive = {{ peer.keepalive }}
{% endif %}
{% endfor %}
Also put the following client config in wireguard-client.conf
:
[Interface]
Address = {{ wan-ip }}
ListenPort = 51820
PrivateKey = {{ private }}
[Peer]
PublicKey = {{ peer.public }}
AllowedIPs = 0.0.0.0/0
Endpoint = {{ peer.endpoint }}:51820
PersistentKeepalive = 30
Now build the blueprint to finish the setup:
$ spire wireguard.clj
wireguard.clj:4 (apt-repo :present "ppa:wireguard/wireguard") root@159.203.119.225 root@localhost
wireguard.clj:5 (apt :update) root@159.203.119.225 root@localhost
wireguard.clj:6 (apt :install "wireguard") root@159.203.119.225 root@localhost
wireguard.clj:9 (shell {:creates ["privatekey" "publickey"], :cmd "umask 077 && wg genkey | tee privatekey | wg pu
wireguard.clj:11 (get-file "privatekey") root@159.203.119.225 root@localhost
wireguard.clj:12 (get-file "publickey") root@159.203.119.225 root@localhost
wireguard.clj:21 (upload {:content (selmer "wireguard-server.conf" {:private (:private server-keys), :peers [{:all
wireguard.clj:30 (sysctl :present {:name "net.ipv4.ip_forward", :value "1"}) root@159.203.119.225
wireguard.clj:31 (service :restarted {:name "wg-quick@wg0"}) root@159.203.119.225
wireguard.clj:33 (upload {:content (selmer "wireguard-client.conf" {:private (:private client-keys), :peer {:publi
{:attr-result {:result :ok}, :copy-result {:result :changed}, :result :changed}
Your setup is now complete. Try and start up the tunnel with
$ sudo service wg-quick@vpn-tunnel start
Now check that your vpn tunnel is working by opening a browser and going to whatismypublicip.com
You should see your web browser is being seen by the internet with an IP of 159.203.119.225 in the remote country you started the server in!
Congratulations! You have built your own personal VPN service!