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.