Running a VPN Server with OpenVPN and Stunnel

May 4, 2016

The Raspberry Pi that I setup as a web server has also been doing double duty as a VPN server. I use OpenVPN, and wrap it with Stunnel since I sometimes need to use the VPN in countries that filter their Internet access. Stunnel makes the VPN look like HTTPS traffic, so this gets around deep packet inspection.

Install the Software and Generate Certificates/Keys

Installing the software takes just one command:

sudo apt-get install openvpn stunnel4

Some prep work:

sudo -s make-cadir /etc/openvpn/easy-rsa cd /etc/openvpn/easy-rsa/ source ./vars ./clean-all

Generating the certificates and keys is a lengthy process, but it's easy. Each time you generate a certificate you will be asked for a Country Name, State or Province Name, Locality Name, Organization Name, Organizational Unit Name, Common Name, Name and Email Address. Only the Common Name is required. It identifies who the certificate belongs to. If you have a domain name, you can use that. Otherwise you can just use the hostname or anything really.

In the blocks below, be sure to replace the underlined text with something appropriate.

Make a Certificate Authority (CA.) For this personal VPN we use the server as the CA. The CA is used to sign all other certificates, so the server and clients can trust each others certificates.

./build-ca ... Country Name (2 letter code) [US]:leave black or write something State or Province Name (full name) [CA]:leave black or write something Locality Name (eg, city) [SanFrancisco]:leave black or write something Organization Name (eg, company) [Fort-Funston]:leave black or write something Organizational Unit Name (eg, section) [MyOrganizationalUnit]:leave black or write something Common Name (eg, your name or your server's hostname) [Fort-Funston CA]: your server hostname Name [EasyRSA]:leave black or write something Email Address [me@myhost.mydomain]:leave black or write something ...

Make the VPN server's certificate, and have the CA sign it.

./build-key-server your server hostname ... Country Name (2 letter code) [US]:leave black or write something State or Province Name (full name) [CA]:leave black or write something Locality Name (eg, city) [SanFrancisco]:leave black or write something Organization Name (eg, company) [Fort-Funston]:leave black or write something Organizational Unit Name (eg, section) [MyOrganizationalUnit]:leave black or write something Common Name (eg, your name or your server's hostname) [your server hostname]:just press enter since the default value is correct Name [EasyRSA]:leave black or write something Email Address [me@myhost.mydomain]:leave black or write something A challenge password []:leave black or write something An optional company name []:leave black or write something Sign the certificate? [y/n]:y 1 out of 1 certificate requests certified, commit? [y/n]y ...

Make certificates for any clients that will connect to the VPN server, and have the CA sign them. In the example below, I made two certificates, but you only need one if only one device will connect to VPN server.

./build-key your client1 hostname ... Country Name (2 letter code) [US]:leave black or write something State or Province Name (full name) [CA]:leave black or write something Locality Name (eg, city) [SanFrancisco]:leave black or write something Organization Name (eg, company) [Fort-Funston]:leave black or write something Organizational Unit Name (eg, section) [MyOrganizationalUnit]:leave black or write something Common Name (eg, your name or your server's hostname) [your client1 hostname]:just press enter since the default value is correct Name [EasyRSA]:leave black or write something Email Address [me@myhost.mydomain]:leave black or write something A challenge password []:leave black or write something An optional company name []:leave black or write something Sign the certificate? [y/n]:y 1 out of 1 certificate requests certified, commit? [y/n]y ... ./build-key your client2 hostname Country Name (2 letter code) [US]:leave black or write something State or Province Name (full name) [CA]:leave black or write something Locality Name (eg, city) [SanFrancisco]:leave black or write something Organization Name (eg, company) [Fort-Funston]:leave black or write something Organizational Unit Name (eg, section) [MyOrganizationalUnit]:leave black or write something Common Name (eg, your name or your server's hostname) [your client2 hostname]:just press enter since the default value is correct Name [EasyRSA]:leave black or write something Email Address [me@myhost.mydomain]:leave black or write something A challenge password []:leave black or write something An optional company name []:leave black or write something Sign the certificate? [y/n]:y 1 out of 1 certificate requests certified, commit? [y/n]y ...

Build the Diffie-Hellman parameters so PFS ciphers can be used.

./build-dh

Configure the OpenVPN Server and Enable IP Forwarding

Edit the server config file and restart the server so it goes into effect:

nano /etc/openvpn/server.conf port 1194 proto tcp dev tun tun-mtu 1500 tun-mtu-extra 32 mssfix 1450 ca /etc/openvpn/easy-rsa/keys/ca.crt cert /etc/openvpn/easy-rsa/keys/server.crt key /etc/openvpn/easy-rsa/keys/server.key dh /etc/openvpn/easy-rsa/keys/dh2048.pem server 10.8.0.0 255.255.255.0 push "redirect-gateway def1 bypass-dhcp" push "dhcp-option DNS 8.8.8.8" push "dhcp-option DNS 8.8.4.4" keepalive 5 30 comp-lzo persist-key persist-tun status /var/log/openvpn.log verb 3 service openvpn restart

Enable IP forwarding:

nano /etc/sysctl.conf uncomment this line: net.ipv4.ip_forward=1 sysctl -p iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE apt-get install iptables-persistent choose "yes" twice, to keep current IPv4 and IPv6 rules

Configure the Stunnel Server

Generate a self-signed certificate, in both PEM and PKCS12 formats. Stunnel uses the PEM file but SSLDroid requires the PKCS12 format.

cd /etc/stunnel/ sudo -s openssl genrsa -out key.pem 2048 openssl req -new -x509 -key key.pem -out cert.pem -days 3650 ... Country Name (2 letter code) [AU]:[]:leave black or write something State or Province Name (full name) [Some-State]:[]:leave black or write something Locality Name (eg, city) []:[]:leave black or write something Organization Name (eg, company) [Internet Widgits Pty Ltd]:[]:leave black or write something Organizational Unit Name (eg, section) []:[]:leave black or write something Common Name (e.g. server FQDN or YOUR name) []:your server hostname Email Address []:[]:leave black or write something ... cat key.pem cert.pem >> stunnel.pem openssl pkcs12 -export -out stunnel.p12 -inkey key.pem -in cert.pem

Configure the stunnel server and and restart to make sure everything works as expected:

nano stunnel.conf chroot = /var/lib/stunnel4 pid = /stunnel4.pid setuid = stunnel4 setgid = stunnel4 socket = l:TCP_NODELAY=1 socket = r:TCP_NODELAY=1 cert = /etc/stunnel/stunnel.pem [openvpn] accept = 443 connect = localhost:1194 cert = /etc/stunnel/stunnel.pem nano /etc/default/stunnel4 change the enabled line to 1, like this: ENABLED=1 shutdown -r now

Setup a PC Client

In order for a Stunnel client to communicate with the Stunnel server, it needs a copy of the certificate. That certificate was stored in /etc/stunnel/stunnel.pem on the server. You could use scp to copy the PEM file from the server to the client, or just cat the file and copy-and-paste the text into a stunnel.pem file on the client.

Install and run the Stunnel client, then configure it, then have Stunnel reload the configuration:

Start > All Apps > Stunnel allusers > stunnel GUI Start In the taskbar tray, right-click the stunnel icon > Edit Configuration Replace the contents of the file with: [openvpn] client = yes accept = 1337 connect = yourdomain.com:443 cert = C:\Path\to\your\stunnel.pem Save the file, then right-click the stunnel icon > Reload Configuration Check for errors: right-click the stunnel icon > Show Log Window

Create an OpenVPN configuration file on the client. Make a textfile with a .ovpn file extension:

client dev tun proto tcp remote localhost 1337 route yourdomain.com 255.255.255.255 net_gateway resolv-retry infinite nobind tun-mtu 1500 tun-mtu-extra 32 mssfix 1450 persist-key persist-tun auth-nocache remote-cert-tls server comp-lzo verb 3 # copy over the server's /etc/openvpn/easy-rsa/keys/ca.crt <ca> -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- </ca> # copy over the server's /etc/openvpn/easy-rsa/keys/your client1 hostname.crt <cert> -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- </cert> # copy over the server's /etc/openvpn/easy-rsa/keys/your client1 hostname.key <key> -----BEGIN PRIVATE KEY----- ... -----END PRIVATE KEY----- </key>

With stunnel running in the taskbar tray, just right-click the .ovpn file and choose Start OpenVPN on This Config File. After a few seconds you should see "Initialization Sequence Completed" and you're done. To stop using the VPN just close that window. You can leave stunnel running, or close it as well.

Setup an Android Client

There are two apps you will need to install: OpenVPN Connect and SSLDroid. Both are available in the Google Play Store.

Just like with the PC client, you will need a .ovpn file for OpenVPN on your Android device. Write one like we did for the PC client, except use the second certificate and key that were generated.

Copy the .ovpn file, and the .p12 file to your Android device.

While Stunnel has an Android version, it's not an app. It's just a command line program, so it's not as easy to use. Instead, I use the SSLDroid app. It does the same job -- it masks OpenVPN traffic in an SSL connection, to work around deep packet inspection. When you open the app just tap "MORE" then tap "Add Tunnel". Give it a name (anything), set Local Port to 1337, set Remote Host to your server's domain name or IP, set Remote Port to 443, and select the .p12 file you copied over earlier. Tap Apply.

YouTube Video