Reverse VPN: turn any private device into public cloud server
Published 2015-6-4NOTICE there might still be some bugs and sparsely commented snippets in this document, but if you follow it, it should work.
Video Demo https://youtu.be/O7D7UkA3IKQ by ravallblog
Typically when you think of a VPN, you think of a private office network behind a firewall that you want to securely access from home or another office.
But what about when your home server hosting your blog and your business site is stuck behind an apartment firewall?
Or what if you want to be able to take a device with you, attach it to any network, and begin using it's web services immediately?
Effectively, we're turning the VPN inside out. A reverse VPN, if you will.
Use a public VPS as the VPN server
Digital Ocean, ChunkHost, AWS, or a server (or Raspberry Pi) in your office, home, or a friend's home - anything that you can get root access to and give a public network access (even if it's with a dynamic dns service).
Install some tools
NOTE: You must be using bash (the default shell / terminal) -- not zsh or fish or anything fancy -- legit bash.
Log into your VPS (ssh user@example.com
) and then continue:
Install the Tools
bash
sudo apt-get install --yes openvpn
Switch to the root user
Be the root you know in your heart. You're going to have to do pretty much everything as root from this point on, so it makes sense just to switch (if you aren't root already).
sudo su -
Get the easy-rsa 2.x tools from github
The new line of tools is 3.x, but I'm not familiar with those yet and these work, so these are what we will use. :-D
# Install git
sudo apt-get install --yes git
# Copy all the files from the internet to your computer
git clone https://github.com/OpenVPN/easy-rsa.git ~/easy-rsa
pushd ~/easy-rsa/
git checkout 'release/2.x'
popd
# Copy all the files into the right place
rsync -av ~/easy-rsa/easy-rsa/2.0/ /etc/openvpn/easy-rsa/
Next we update the config
# Push into the easy-rsa directory
pushd /etc/openvpn/easy-rsa/
# Then open the ./vars file, which is a config file
# You'll need to take a look around and season to taste
# It's nothing fancy, just names and preferences
vim ./vars
Note a few things that you probably should change:
/etc/openvpn/easy-rsa/vars
:
# change all of the things down at the bottom that look like these
# (these are given as an example for me, yours should be different)
export KEY_COUNTRY="US"
export KEY_PROVINCE="Utah"
export KEY_CITY="Provo"
export KEY_ORG="AJ ONeal Tech LLC"
export KEY_EMAIL="awesome@coolaj86.com"
export KEY_CN="*.coolaj86.com"
export KEY_NAME="AJ ONeal"
export KEY_OU="Department of Docs & Blogging"
Now we'll get to work
Then load all of the vars into your environment
# load the variables
source ./vars
# delete any and all previous keys.
./clean-all
mkdir -p ./keys
ls -lah ./keys
# TODO (note to self) show the plain openssl commands (it's not that hard, y'know?)
# Build a certificate authority for your organization
./build-ca
# NOTE: answer the questions
sudo apt-get install --yes tree
tree ./keys/
# The out put will look like this:
# ./keys/
# ├── ca.crt
# ├── ca.key
# ├── index.txt
# └── serial
# Inpsect the keys, just for fun
openssl x509 -text -noout -in ./keys/ca.crt
openssl x509 -text -noout -in ./keys/ca.key
# When prompted, enter the same CN as above - coolaj86.com in my case
# No Password
./build-key-server rvpn.coolaj86.com
tree ./keys/
# ./keys/
# ├── 01.pem
# ├── ca.crt
# ├── ca.key
# ├── index.txt
# ├── index.txt.attr
# ├── index.txt.old
# ├── rvpn.coolaj86.com.crt
# ├── rvpn.coolaj86.com.csr
# ├── rvpn.coolaj86.com.key
# ├── serial
# └── serial.old
# The less you're doing with your computer, the longer this will take.
# If you're downloading a big file or something or https, it should only take a minute.
# Otherwise it could take several minutes
./build-dh
ProTipTM:
If you use https, you can download a file over and over again (such as a large image) with curl and you'll get a different set of bits each time because the random seed for the https connetion will be different. This will give you tons of entropy if you run it while generating keys.
while true; do sleep 1; curl 'https://pbs.twimg.com/media/A7hdDEnCYAA8oky.jpg:large' > /dev/null; done
openvpn --genkey --secret keys/ta.key
curl -fssL https://gist.githubusercontent.com/coolaj86/18b92ee350b38f18fca6/raw/server.conf \
-o /etc/openvpn/server.conf
vim /etc/openvpn/server.conf
Configure the Server's Network and Firewall settings
vim /etc/sysctl.conf
/etc/sysctl.conf
:
# uncomment the following
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
TODO: the router may need to be setup to forward 1194 to the pi
sysctl -p
ufw status
ufw allow 1194/udp
# IMPORTANT change the 192.168.1.4 to your server's IP
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j SNAT --to-source 192.168.1.4
touch /etc/firewall-openvpn-rules.sh
chmod 700 /etc/firewall-openvpn-rules.sh
# this will be open, you'll add the stuff below
vim /etc/firewall-openvpn-rules.sh
/etc/firewall-openvpn-rules.sh
:
#!/bin/bash
# IMPORTANT change the 192.168.1.4 to your server's IP
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j SNAT --to-source 192.168.1.4
Now we'll make sure that this firewall rule is added on each boot
vim /etc/network/interfaces
# You'll see a line that looks like this
iface eth0 inet dhcp
# underneath it you'll need to add this line
# (it shouldn't matter whether you use a tab or spaces)
pre-up /etc/firewall-openvpn-rules.sh
And finally we're ready to restart the openvpn service
/etc/init.d/openvpn stop
/etc/init.d/openvpn start
Use a private device as a Web Server
I must note that this should be the process:
- create your client key on the client,
- create a client.CSR.PEM (certificate signing request)
- send the client.CSR.PEM to the server
- the server creates a client.CRT.PEM from the client.CSR.PEM
However, if the server is compromised then the server private key and certificate are compromised which means that every certificate the server has signed are now invalid.
On the SERVER
We're going to create the client's keys and OVPN file.
# For example
./build-key client-xyz.example.com
Generating Client Keys
Each device will need its own keys.
Use ./build-key
if the device is meant to be always-on and not require user interaction,
like a raspberry pi home server.
# Example
# this will NOT require a passphrase
./build-key homeserver.coolaj86.com
# just showing the new files (omitting *.old backups)
tree ./keys/
# ./keys/
# ├── 02.pem
# ├── homeserver.coolaj86.com.crt
# ├── homeserver.coolaj86.com.csr
# └── homeserver.coolaj86.com.key
Use ./build-key-pass
if the device is something you log into,
such as your laptop.
# Example
# this WILL require a passphrase
./build-key-pass macbook.coolaj86.com
# just showing the new files (omitting *.old backups)
tree ./keys/
# ./keys/
# ├── 03.pem
# ├── macbook.coolaj86.com.crt
# ├── macbook.coolaj86.com.csr
# └── macbook.coolaj86.com.key
Some devices (iOS, Android, Macbook(?)) need a des3 version of the key, might as well do that now:
# this will require your previous passphrase, and to create a passphrase (it can be the same)
openssl rsa -in ./keys/macbook.coolaj86.com.key -des3 -out ./keys/macbook.coolaj86.com.3des.key
curl http://checkip.dyndns.org
curl https://coolaj86.com/services/whatsmyip
vim /etc/openvpn/easy-rsa/keys/Default.txt
/etc/openvpn/easy-rsa/keys/Default.txt
:
client
dev tun
proto udp
remote coolaj86.com 1194
resolv-retry infinite
nobind
persist-key
persist-tun
mute-replay-warnings
ns-cert-type server
key-direction 1
cipher AES-128-CBC
comp-lzo
verb 1
mute 20
MakeOVPN.sh:
#!/bin/bash
# Default Variable Declarations
DEFAULT="Default.txt"
FILEEXT=".ovpn"
CRT=".crt"
KEY=".3des.key"
CA="ca.crt"
TA="ta.key"
NAME="${1}"
if [ -z "${NAME}" ]; then
#Ask for a Client name
echo "Please enter an existing Client Name:"
read NAME
fi
#1st Verify that client’s Public Key Exists
if [ ! -f $NAME$CRT ]; then
echo "[ERROR]: Client Public Key Certificate not found: $NAME$CRT"
exit
fi
echo "Client’s cert found: $NAME$CR"
#Then, verify that there is a private key for that client
if [ ! -f $NAME$KEY ]; then
echo "[ERROR]: Client 3des Private Key not found: $NAME$KEY"
exit
fi
echo "Client’s Private Key found: $NAME$KEY"
#Confirm the CA public key exists
if [ ! -f $CA ]; then
echo "[ERROR]: CA Public Key not found: $CA"
exit
fi
echo "CA public Key found: $CA"
#Confirm the tls-auth ta key file exists
if [ ! -f $TA ]; then
echo "[ERROR]: tls-auth Key not found: $TA"
exit
fi
echo "tls-auth Private Key found: $TA"
#Ready to make a new .opvn file - Start by populating with the default file
cat $DEFAULT > $NAME$FILEEXT
#Now, append the CA Public Cert
echo "<ca>" >> $NAME$FILEEXT
cat $CA >> $NAME$FILEEXT
echo "</ca>" >> $NAME$FILEEXT
#Next append the client Public Cert
echo "<cert>" >> $NAME$FILEEXT
cat $NAME$CRT | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' >> $NAME$FILEEXT
echo "</cert>" >> $NAME$FILEEXT
#Then, append the client Private Key
echo "<key>" >> $NAME$FILEEXT
cat $NAME$KEY >> $NAME$FILEEXT
echo "</key>" >> $NAME$FILEEXT
#Finally, append the TA Private Key
echo "<tls-auth>" >> $NAME$FILEEXT
cat $TA >> $NAME$FILEEXT
echo "</tls-auth>" >> $NAME$FILEEXT
echo "Done! $NAME$FILEEXT Successfully Created."
#Script written by Eric Jodoin
pushd /etc/openvpn/easy-rsa/keys
./MakeOVPN.sh client-xyz.example.com
# copy this and paste it on the client
cat client-xyz.example.com.ovpn
On the Client
You've just got to paste that config onto the client and start the connection.
sudo apt-get install --yes openvpn
# note it's important to use '&' rather than ';' or '&&', even though it's a daemon
OVPN_CLIENT="client-xyz"
sudo kill --signal TERM $(cat /var/run/ovpn."${OVPN_CLIENT}".pid)
sleep 0.5
sudo kill --signal KILL $(cat /var/run/ovpn."${OVPN_CLIENT}".pid)
sudo openvpn --config "${OVPN_CLIENT}".ovpn --daemon --writepid /var/run/ovpn."${OVPN_CLIENT}".pid
# you can later kill the process with
# sudo bash -c 'kill $(cat /var/run/client-xyz.pid)'
Check if you were successful.
ifconfig
ping -c 1 10.8.0.1
If you were successful you'll see a tun0
device with an IP address
and you should be able to ping the VPN server.
Connect on Startup
If you're lucky the openvpn service was already configured to start on boot and all you have to do is move your config file to the correct directory and change the config file to say that it will be loaded
sudo mv client-xyz.example.com.ovpn /etc/openvpn/client-xyz.conf
sudo vim /etc/default/openvpn
# uncomment the following
# AUTOSTART="client-xyz"
upstart
vim /etc/init/myopenvpn
# OpenVPN autostart on boot upstart job
start on runlevel [2345]
stop on runlevel [!2345]
respawn
exec /usr/sbin/openvpn --status /var/run/openvpn.client.status 10 --cd /etc/openvpn --config /etc/openvpn/client.conf --syslog openvpn
By AJ ONeal
Did I make your day?
Buy me a coffee
(you can learn about the bigger picture I'm working towards on my patreon page )