There are few good choices like OpenVPN, SoftEther, Pritunl etc. If you prefer something much more robust, I'd suggest to take look at strongSwan (IPSec + ESP). If you prefer something temporary just use ssh tunnel ssh -D 9050 user@server , install proxychains and proxychains firefox, vola you're traffic is encrypted. The only issue is, it's hard use socks proxy on un-rooted mobile phone and sessions are temporary.

I choose OpenVPN since it's widely used and it's easy to configure. However OpenVPN has it's drawbacks, it's easier to detect if a person is using OpenVPN by checking the packet MTU size.

Creating FreeBSD server on Azure

Create a new freebsd 10.3 instance on Azure, generate SSH key using the following command

openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout vpn_server.key -out vpn_server.pem

Don't forget to change the default username (azureuser) and block all ports except 22 till we configure the VPN server. You may wonder why FreeBSD, well here's the resource usage on a fully functional vpn server

Enable IP packet forwarding

Enable IP forwarding on the server by setting net.inet.ip.forwarding=1 in sysctl.conf file.

sudo sysctl net.inet.ip.forwarding=1
sudo nano /etc/sysctl.conf

Enable packet filter (pf)

pf is the freebsd version of iptables, OpenVPN 10.8.0.0/24 must be routed properly, open /etc/pf.conf as root and add the following configuration

ext_if=hn0 # if your interface is eth0 then use that instead
openvpn_subnet="{10.8.0.0/24}"

# similar to iptables -t nat -a POSTROUTING -s 10.8.0.0/24 -o hn0 -j MASQUERADE
nat on $ext_if from $openvpn_subnet to any -> ($ext_if)

Start pf by default using sudo sysrc pf=yes after system reboot.

Installing DNSCrypt and Unbound

Install dnscrypt-proxy on the FreeBSD sudo pkg install dnscrypt-proxy. Update the /etc/rc.conf file so the dnscrypt autostarts after reboots, add the following config to /etc/rc.conf. cs-useast is the dnscrypt provider is closest to the server location, you can choose one that's closest to you from this list

# dnscrypt-proxy
dnscrypt_proxy_enable="YES"
dnscrypt_proxy_flags="-a 127.0.0.1:54"
dnscrypt_proxy_resolver="cs-useast"
dnscrypt_proxy_logfile="/dev/null"

Install unbound to act as a local dns cache sudo pkg install unbound. Edit the unbound config file /var/unbound/unbound.conf using sudo and add the following configuration

server:
  username: unbound
  directory: /var/unbound
  chroot: /var/unbound
  do-not-query-localhost: no
  interface: 0.0.0.0
  access-control: 0.0.0.0/0 refuse   # disable access to rest of the world
  access-control: 127.0.0.0/8 allow  # enable access to local network
  access-control: 10.8.0.0/24 allow  # enable access to OpenVPN subnet
  hide-identity: yes
  hide-version: yes
  auto-trust-anchor-file: "/var/unbound/root.key"

#include: /var/unbound/forward.conf
#include: /var/unbound/lan-zones.conf
#include: /var/unbound/control.conf

forward-zone:
   name: "." # use for ALL queries
   forward-addr: 127.0.0.1@40 # dnscrypt-proxy

Update the default DNS resolvers on /etc/resolv.conf to use unbound, 127.0.0.1 on port 53 as the default dns resolver

# Generated by resolvconf
# search XXXXXX.c4.internal.cloudapp.net
# nameserver XXX.XXX.XXX.XXX

# unbound + dnscrypt-proxy
nameserver 127.0.0.1

Start the unbound server after reboot sudo sysrc local_unbound_enable=YES, by default the unbound used the config file located at /var/unbound/unbound.conf. If you plan to use different location, update the /etc/rc.conf file to pass the config file we saved in STEP2 to unbound after reboot

# Unbound
unbound_flags="-c /var/unbound/unbound.conf"

Reboot the server sudo reboot and test the DNS resolution with the following command

drill -S freebsd.org

   # this should print somethink like
   ;; Number of trusted keys: 1
    ;; Chasing: freebsd.org. A

    DNSSEC Trust tree:
    freebsd.org. (A)
    |---freebsd.org. (DNSKEY keytag: 48075 alg: 8 flags: 256)
        |---freebsd.org. (DNSKEY keytag: 25814 alg: 8 flags: 257)
        |---freebsd.org. (DS keytag: 25814 digest type: 2)
            |---org. (DNSKEY keytag: 64353 alg: 7 flags: 256)
                |---org. (DNSKEY keytag: 9795 alg: 7 flags: 257)
                |---org. (DNSKEY keytag: 17883 alg: 7 flags: 257)
                |---org. (DS keytag: 9795 digest type: 2)
                |   |---. (DNSKEY keytag: 39291 alg: 8 flags: 256)
                |       |---. (DNSKEY keytag: 19036 alg: 8 flags: 257)
                |---org. (DS keytag: 9795 digest type: 1)
                    |---. (DNSKEY keytag: 39291 alg: 8 flags: 256)
                        |---. (DNSKEY keytag: 19036 alg: 8 flags: 257)
    ;; Chase successful

   drill google.com

   # Output
    ;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 51628
    ;; flags: qr rd ra ; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
    ;; QUESTION SECTION:
    ;; google.com.  IN  A

    ;; ANSWER SECTION:
    google.com. 300 IN  A   216.58.218.238

    ;; AUTHORITY SECTION:

    ;; ADDITIONAL SECTION:

    ;; Query time: 25 msec
    ;; SERVER: 127.0.0.1
    ;; WHEN: Sun Nov 13 03:16:49 2016
    ;; MSG SIZE  rcvd: 44

Installing OpenVPN

Install openvpn and easy-rsa by running sudo pkg install openvpn easy-rsa. Create a new directory sudo mkdir /usr/local/etc/openvpn and copy sample configuration file and easy-rsa files.

cp /usr/local/share/examples/openvpn/sample-config-files/server.conf \
       /usr/local/etc/openvpn/openvpn.conf

cp -r /usr/local/share/easy-rsa /usr/local/etc/openvpn/easy-rsa

Edit the vars file under /usr/local/etc/openvpn/easy-rsa and change the following,

cd /usr/local/etc/openvpn/easy-rsa

sudo vi vars

set_var EASYRSA_REQ_COUNTRY     "US"  # "COUNTRY"
set_var EASYRSA_REQ_PROVINCE    "NY"  # "PROVINCE"
set_var EASYRSA_REQ_CITY        "New York"  # "CITY"
set_var EASYRSA_REQ_ORG         "ORG"  # "ORGANIZATION"
set_var EASYRSA_REQ_EMAIL       "admin@localhost"  # "EMAIL"
set_var EASYRSA_REQ_OU          "VPN Server"  # "ORGANIZATIONAL UNIT"
set_var EASYRSA_REQ_CN          "vpn" # COMMON_NAME
set_var EASYRSA_KEY_SIZE        4096 # set key size to 4096
set_var EASYRSA_CA_EXPIRE       3650 # Set self-signed CA to expire after 10 years
set_var EASYRSA_CERT_EXPIRE     1825 # Set cert to expire after 5 years, don't worry even if you lost
                                     # the certs or if they are compromised, you can revoke them.

Now start generating the keys, now run sudo ./easyrsa.real help to print the list of commands. Lets start with building PKI

Build Public Key Infrastructure (PKI)

Initialize PKI by running init-pki

sudo ./easyrsa.real init-pki

-- Output --

Note: using Easy-RSA configuration from: ./vars

init-pki complete; you may now create a CA or requests.
Your newly created PKI dir is: /usr/local/etc/openvpn/easy-rsa/pki

Once PKI is initialized, run build-ca to build CA, It will ask for a password and a common name, use a strong random password because it's used to encrypt the key and if the password is compromised, it'll compromise entire PKI.

Tip: cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1 to generate 32 char long random string

sudo ./easyrsa.real build-ca

    -- Output --

    Note: using Easy-RSA configuration from: ./vars
    Generating a 4096 bit RSA private key
    ....................++
    .................++
    writing new private key to '/usr/local/etc/openvpn/easy-rsa/pki/private/ca.key.UCYBDDSvsh'

    Enter PEM pass phrase:
    Verifying - Enter PEM pass phrase:

    -----
    You are about to be asked to enter information that will be incorporated
    into your certificate request.
    What you are about to enter is what is called a Distinguished Name or a DN.
    There are quite a few fields but you can leave some blank
    For some fields there will be a default value,
    If you enter '.', the field will be left blank.
    -----
    Common Name (eg: your user, host, or server name) [Easy-RSA CA]:

    CA creation complete and you may now import and sign cert requests.
    Your new CA certificate file for publishing is at:
    /usr/local/etc/openvpn/easy-rsa/pki/ca.crt

Now generate the vpn server certificate with sudo ./easyrsa.real build-server-full openvpn-server nopass with certificate name as openvpn-server and without the password. It'll ask the password for ca.key, enter the password used in the previous step.

# ./easyrsa.real build-server-full openvpn-server nopass

Note: using Easy-RSA configuration from: ./vars
Generating a 4096 bit RSA private key
.....................................................................................++
.............................................................................++
writing new private key to '/usr/local/etc/openvpn/easy-rsa/pki/private/openvpn-server.key.65YXAVRkws'
-----
Using configuration from /usr/local/etc/openvpn/easy-rsa/openssl-1.0.cnf
Enter pass phrase for /usr/local/etc/openvpn/easy-rsa/pki/private/ca.key:
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'openvpn-server'
Certificate is to be certified until Nov 11 21:30:50 2021 GMT (1825 days)

Write out database with 1 new entries
Data Base Updated

Check the certificate with sudo ./easyrsa.real show-cert openvpn-server, it should print something like this

Note: using Easy-RSA configuration from: ./vars

Showing cert details for 'openvpn-server'.
This file is stored at:
/usr/local/etc/openvpn/easy-rsa/pki/issued/openvpn-server.crt

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 1 (0x1)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer:
            commonName                = vpn
        Validity
            Not Before: Nov 12 21:30:50 2016 GMT
            Not After : Nov 11 21:30:50 2021 GMT
        Subject:
            commonName                = openvpn-server
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            X509v3 Subject Key Identifier:
                4B:E5:F1:1A:01:1F:9F:AB:C8:72:16:9B:70:C9:C9:9D:6B:6B:50:E8
            X509v3 Authority Key Identifier:
                keyid:A6:13:41:E5:E8:E1:A2:B7:03:91:D8:83:8D:DA:F7:9C:28:34:FF:A4
                DirName:/CN=vpn
                serial:AD:B0:43:C8:3B:25:03:B3

            X509v3 Extended Key Usage:
                TLS Web Server Authentication
            X509v3 Key Usage:
                Digital Signature, Key Encipherment

Once the server certificate is generated, build a client certificate sudo ./easyrsa.real build-client-full <UNIQUE_NAME_GOES_HERE> nopass, nopass leaves the client key unencrypted, it's upto you if you want to leave it that way. If you need client certificates for another device, repeat the above step but make sure you use a different name.

sudo ./easyrsa.real build-client-full desktop nopass

Note: using Easy-RSA configuration from: ./vars
Generating a 4096 bit RSA private key
.....................................................................................................
...................................................................++
writing new private key to '/usr/local/etc/openvpn/easy-rsa/pki/private/desktop.key.m4nxiIGhou'
-----
Using configuration from /usr/local/etc/openvpn/easy-rsa/openssl-1.0.cnf
Enter pass phrase for /usr/local/etc/openvpn/easy-rsa/pki/private/ca.key:
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'desktop'
Certificate is to be certified until Nov 11 21:42:07 2021 GMT (1825 days)

Write out database with 1 new entries
Data Base Updated

finally generate Diffie Hellman parameters dh.pem using sudo ./easyrsa.real gen-dh. This openssl uses generator 2 by default but if you want more secure prime, use generator 5 ( it takes a while to generate a 4096 bit long prime, please be patient)

sudo ./easyrsa.real gen-dh

Create a directory keys in /usr/local/etc/openvpn, copy the generated keys to /usr/local/etc/openvpn/keys.

cd /usr/local/etc/openvpn/easy-rsa
cp pki/dh.pem pki/ca.crt pki/issued/openvpn-server.crt \
    pki/private/openvpn-server.key /usr/local/etc/openvpn/keys

Copy the individual keys to the respective client.

Client Cert: /usr/local/etc/openvpn/easy-rsa/pki/issued/<name>.crt
Client Key: /usr/local/etc/openvpn/easy-rsa/pki/private/<name>.key
CA Cert:    /usr/local/etc/openvpn/easy-rsa/pki/ca.crt
# archive the above files for easy distribution.
# use scp or rsync to download the archive.
tar czvf desktop.tar.gz pki/ca.crt pki/issued/desktop.crt pki/private/desktop.key

Generate OpenVPN server and client config files

Now edit the OpenVPN server configuration file openvpn.conf, first change dir to cd /usr/local/etc/openvpn and edit the openvpn.conf file we copied in the initial steps or simply copy-paste the following configuration

# Server configuration
;listen IPADDRESS_OF_NIC

# Port
;port 1194 # best to use a random port
port YYYYY

# TCP or UDP
proto udp

# Routed IP tunnel
dev tun

# Cert and keys
ca /usr/local/etc/openvpn/keys/ca.crt
cert /usr/local/etc/openvpn/keys/openvpn-server.crt
key /usr/local/etc/openvpn/keys/openvpn-server.key
dh /usr/local/etc/openvpn/keys/dh.pem

# HMAC
# both server and client must have copy of ta.key
# ta.key 0 --> server
# ta.key 1 --> client
tls-auth /usr/local/etc/openvpn/keys/ta.key 0

# Cryptographic cipher
# select AES 256 bit
cipher AES-256-CBC

# tls support
remote-cert-tls client

server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
push "redirect-gateway def1 bypass-dhcp"

# Force clients to use unbound + dnscrypt-proxy server
push "dhcp-option DNS 10.8.0.1"

# Allow clients to see each other
;client-to-client

# Resume client config for multiple users
;duplicate-cn

# Ping client every 10 secs
keepalive 10 120

# Disable compression
# you can enable it if you like
;comp-lzo

# Privileges, chroot
# chroot /var/openvpn

user nobody
group nobody

# The persist options will try to avoid
# accessing certain resources on restart
# that may no longer be accessible because
# of the privilege downgrade.
persist-key
persist-tun

# client status information
status openvpn-status.log

# log
log openvpn.log
# presistant log accross reboots
;log-append  openvpn.log
verb 4
mute 20

Now let's create the client openvpn configuration file and copy paste the following config. Make sure you replace all the certs i.e copy ca.crt and past it inbetween <ca> </ca> tags, change server ip address (XXX.XXX.XXX.XXX) and port (YYYYY)

#Run in client mode
client

# tun/tap
dev tun

# TCP or UDP
proto udp

# remote SERVER_IP_ADDRESS SERVER_OPENVPN_PORT
# you can add multiple OpenVPN servers to load balance
# between them. Enable remote-random to tell client to
# choose a random server

remote XXX.XXX.XXX.XXX YYYYY

;remote-random

# keep retrying if connection is lost
# enable this if one of your clients is
# a mobile or laptop
resolv-retry infinite

nobind

# if non-windows systems enable
;user nobody
;group nobody

persist-key
persist-tun

# tls auth
remote-cert-tls server

# Enable if server has compression enabled
;comp-lzo


# logging
verb 4
mute 20

# Use same cipher as server
cipher AES-256-CBC


# ca ca.crt
# cert client.crt
# key client.key
# tls-auth ta.key 1

# You can also embed the files within the config, easier for distribution.
#ca [inline]
#cert [inline]
#key [inline]
#tls-auth [inline] 1

key-direction 1

# ca.crt
#<ca>
-----BEGIN CERTIFICATE-----

-----END CERTIFICATE-----
#</ca>

# client.crt
#<cert>

#</cert>

# client.key
#<key>

#</key>

# ta.key
#< tls-auth >

#</tls-auth>

Now we just need to enable openvpn to run as a daemon and reboot the system

sudo sysrc openvpn_enable="YES"
sudo sysrc openvpn_if="tun"
sudo reboot

Download the client configuration files and import them into your network manager. That's it, your network traffic is now encrypted.

Tip: OpenVPN on android recognized .ovpn files, simply replace the client.conf --> client.ovpn

Well then, I'm gonna go and play 0 A.D.