I have recently reorganized my home network's wild side naming and addressing, and the challenge now is to update the VPNs' configurations and to get them working again.
For security by obscurity, in this document example.net
is my
domain name, certified by a reputable
trust vendor (Startcom), whereas
example.org
is the internal name certified by my own self-signed X.509
root certificate. Although this root cert is installed for TLS on my personal
machines, it's a challenge to get Android to use it consistently, which is
why I have the split naming scheme. Also, if I want to pass a URL to someone
else, they will not have my root cert.
There is one wild side interface which gets a dynamic IPv4 address from my ISP (Verizon FIOS). When it changes it is registered with my outsourced DNS vendor (dyn.com) under the name example.net (no 1-component hostname). It is also registered with my IPv6 tunnel broker (Hurricane Electric). The wild side IPv6 address is fixed. The internal fixed IPv4 address of the router is also registered in Dyn's DNS under the name jacinth.example.net, and the same address is known to the internal DNS server as jacinth.example.org.
I have three VPNs: StrongSwan (IPSec), OpenVPN on port 1194/udp, and OpenVPN on 443/tcp. The latter is the last choice, but it is unfortunately very common for hotel Wi-Fi nets to block all ports except 53, 80 and 443 (TCP only). HTTPS service on example.net is provided on a nonstandard port; in fact I have a small collection of these:
OOBA means Out of Band Authentication
; it opens a firewall hole for
the client, and without it no traffic gets in except for the VPNs, OOBA itself,
and incoming mail. If the remote network is blocking ports, the OOBA ports
definitely will be included.
As with all VPN software, StrongSwan's error messages are clear if you already know what the problem is; in other words, they are arcane if your configuration is messed up.
On the server (example.net), /etc/ipsec.conf reads like this. Remember that within a section each line must begin with whitespace including comments and otherwise blank lines.
config setup # Message verbosity for normal operation: charondebug = "dmn 0,mgr 1,ike 0,chd 1,job 1,cfg 0,knl 1,net 1,tls 1,lib 0,enc 0,tnc 0" # Verbosity for debugging problems: #OFF charondebug = "dmn 2,mgr 2,ike 2,chd 2,job 1,cfg 0,knl 1,net 1,tls 1,lib 1,enc 0,tnc 0" conn %default auto = add # We're a responder, start when peer connects dpddelay = 0 # Rely on rekeying for dead peer detection dpdaction = clear left = %any leftauth = pubkey # See below about what this cert has to certify. The root certificate # that signed it, and intermediate certs, must be in # /etc/ipsec.d/cacerts/ (symbolic links OK). leftcert = /etc/ssl/hostcerts/host.crt leftsendcert = always leftdns = 192.168.200.193,2001:470:1f05:844::3 right = %any rightid = %any rightauth = pubkey # Any client whose cert is signed by this CA is admitted. # Use this command line to extract the Distinguished Name in your root # certificate in the format StrongSwan wants to see: # openssl x509 -in root.crt -noout -nameopt sname,sep_comma_plus_space -subject # The certificate, and any intermediate certs, must be in # /etc/ipsec.d/cacerts/ (symbolic links OK). rightca = "C=GN, L=Minas Tirith, CN=Example.Net Root Cert 2024" rightsendcert = ifasked # Assign the client's IP from these pool(s) rightsourceip = 192.168.200.160/29,2001:470:1f05:844::c8f0/125 conn roadwarrior # The peer just wants access to example.org leftsubnet = 192.168.200.192/26,2001:470:1f05:844::/64 conn defaultroute # The peer needs to send its default route down the tunnel leftsubnet = 0.0.0.0/1,128.0.0.0/1,::/1,8000::/1
/etc/ipsec.secrets (readable only by root) needs a line for the host
key corresponding to the leftcert. See man 5 ipsec.secrets
for the
format. If the key has a passphrase you can put
it after the key's filename. This means, if the black hats get onto your
machine, in addition to stealing the host key they need to also steal
/etc/ipsec.conf. This is a rather small increment in security and is a
major hassle for other services that use this host key. The identifier
should be (I'm pretty sure) the Common Name certified in leftcert (versus
a SAN that the client relies on).
jacinth.example.org : /etc/ssl/private/host.key
The leftcert has to satisfy two conflicting requirements if you want to use IPSec on Android. You can designate one trusted CA cert, which Android StrongSwan is going to push to the server to induce it to trust the client cert that it also pushes. (The server doesn't believe in this cert; it uses the cert to identify its own copy which it does trust.) But the same CA cert will be used to establish trust in the server's host cert (leftcert) -- which must certify the hostname that the client used to connect to the server. For the latter a SAN (Subject Alternate Name) is accepted.
I tried without success to use the host cert for example.net that was certified by Startcom: if the Android client had the Startcom cert it refused to send the client cert (certified by example.org, not Startcom); if it had the example.org cert it rejected the server's host cert signed by Startcom; and if it was told to select a CA cert automatically, it sent cert requests for about 200 CA's, and I'm not sure what it selected, but trust was not established.
I finally realized that just because Startcom has certified example.net, doesn't mean that I can't certify it too. I created a new host cert for jacinth.example.org (Common Name) with a SAN for example.net (and some aliases that I need). Now the Android client believes in its own client cert and in the server's host cert.
You first need to load your client certificate and key into Android's
certificate storage. Your best bet is to obtain from your Certificate
Authority a PKCS#12 file (extension .p12 or .pfx) containing your key, cert,
intermediate CA cert(s) (if any) and root cert. It will most likely end up
in /sdcard/Download or whatever alias Android is using this year. Start the
Settings app, find Security, scroll almost to the bottom and find Credential
Storage, and under that, Install from SD Card. In the file listbox click on
Download, and you should find your downloaded PKCS#12 file. Click on it,
give the password, give a friendly name
for the content (which Android
will not show when it's most important), and you're done.
To edit the VPN profile, start StrongSwan and long-press on the line
item for your connection; the headline changes and you hit Edit
. Or for
a new connection, just hit Add VPN Profile
. On the profile page:
Usercollection.
To turn on IPSec, short-click on the line item for your connection. It should connect promptly. In case of problems a message box will pop up with a choice to view the log file.
The Network Manager icon is in your toolbar. On Wi-Fi the icon is the
traditional signal strength bars; for a wired connection it is a picture of two
computer monitors. Left click to get the connection menu. Near the bottom is
a line for VPN Connections; slide to the right and a submenu will open, whose
second from last item is Configure VPN. Click on it to get a list of VPN
definitions. Click on one (IPSec) and hit Edit, or hit Add
. Give the
root password (twice) if in paranoia mode. Fill out non-VPN tabs according to
your normal policy, specifically on the General tab, I mark All users may
connect to this network
. On the VPN tab for IPSec:
To turn on IPSec, click on the Network Manager icon, slide to the right end of VPN Connections, and click on the menu item for IPSec. It just takes a few seconds to initiate the connection, if it's going to work. A padlock is added to the Network Manager icon. If it fails, look in /var/log/debug for clues (assuming one configured /var/log/debug).
The configuration for the two OpenVPN ports is almost identical and I will describe both together. On Android the app is OpenVPN Settings by Friedrich Schäuffelhut, and the binary program that it downloads and installs is OpenVPN-2.1.1. Current in OpenSuSE 13.1 dated 2014-12-01 is OpenVPN-2.3.2.
The server configuration file goes like this. Host-specific parameters are grouped at the end.
# Verbosity: verb 1 mute 10 # Preserve root-only files and options. persist-key persist-local-ip persist-remote-ip persist-tun # Lock key and buffers in memory, keeping them out of the swap file. mlock # Use a dynamic tun device. (Could also be tap, for ether bridging.) dev tun # Should we use DF for path MTU discovery? Empirically verify the MTU? mtu-disc maybe mtu-test # Dead peer detection by pings keepalive 15 31 ping-timer-rem # Don't complain if started when the network isn't up yet. ifconfig-nowarn # Resist denial of service attacks. connect-freq 1 1 # Allow reconnects with a different IP address (DHCP renew does that sometimes) float # Allow multiple connections from the same user, e.g. from different hosts. duplicate-cn # https://wiki.debian.org/OpenVPN recommends to push a DNS server for Android. push "dhcp-option DNS 192.168.200.193" # Crypto Parameters (must match the peer, can't push them) # HMAC algorithm (anti-tampering checksum) auth SHA256 # Cryptographic cipher on main data channel (not used in tls-server/client mode) cipher AES-256-CBC # Use LZO compression (with adaptive shutoff) comp-lzo # Polarity of this host (tls-client or tls-server) tls-server # Diffie-Hellman parameter file, only on server. # You should generate your own; runtime: 13 sec on Intel i7-3632QM @ 2.2GHz # openssl genpkey -genparam -algorithm DH -out dh2048.pem -pkeyopt dh_paramgen_prime_len:2048 dh /etc/openvpn/dh2048.pem # Server-specific options: # Protocol and port proto udp port 1194 # proto tcp # port 443 # Multi-client server, uses dynamic addresses from 192.168.200.128/28, # 16 addresses, 4 per client and the server takes 1 set. A different # address range is used for the port 443/tcp server. mode server server 192.168.200.128 255.255.255.240 max-clients 3 # To get on, the client must present a certificate signed by a CA in # this file. PEM format. Multiple certs may be concatenated. Include # intermediate certs. ca /etc/ssl/ca/example.org.crt # If a different root certificate signed the server's host cert, list it # (and intermediate certs) here or append to the cert file. # extra-certs /etc/ssl/ca/example.net.crt # The server's host certificate and private key (unencrypted). Recommended # to appeend the intermediate cert(s) and trust anchor that signed it. cert /etc/ssl/hostcerts/host.cia key /etc/ssl/private/host.key
On Android the authentic OpenVPN binary is used so the configuration file is nearly identical. It differs in these aspects:
# Accept configuration overrides from the server pull # Slightly different ping/keepalive parameters: ping 60 ping-exit 180 ping-timer-rem # Server's hostname or IPv(4 or 6). Use a name you can resolve and connect to. remote example.net # Require this Common Name in the certificate which the server will send over. # Too modern: verify-x509-name jacinth.example.net name # This option is deprecated: tls-remote jacinth.example.net # Polarity of this host (tls-client or tls-server) tls-client # Unlike on real Linux, the certificates and key go in the OpenVPN directory # /sdcard/openvpn , and are specified by relative paths. # This is the CA cert(s) that signed the server's host cert. PEM format, # and include the intermediate cert(s) if any. ca example.net.pth # This is the CA cert(s) that signed our client cert. # extra-certs example.org.pth # The client's user certificate and private key (unencrypted). # You are allowed to concatenate the root and intermediate certs and # to omit extra-certs. cert example.org.cia key example.org.key
You also need to set some preferences. Long-press on the line item for the connection and from the menu pick Preferences.
An issue with OpenVPN is, the tunnel cannot go through itself; there has to be a route from the client to the gateway's wild side for the tunnel packets to follow. But payload packets to the gateway's wild side will follow the same route, not through the tunnel. If inimical forces are blocking my payload packets, they will continue to do so with OpenVPN. Or if you have sensitive information not protected by TLS (I don't), OpenVPN will not be protecting it either. The cure for that is to connect to payload services on the internal address (jacinth.example.net), which will go through the tunnel.
To turn on OpenVPN, launch the OpenVPN Settings app. The first menu item is for turning on the whole OpenVPN mechanism. Then short-click on the line item for your connection. It should connect promptly with progress notes below the connection title. In case of problems turn it off, then long-click on it and pick the choice to view the log file.
The Network Manager icon is in your toolbar. On Wi-Fi the icon is the
traditional signal strength bars; for a wired connection it is a picture of two
computer monitors. Left click to get the connection menu. Near the bottom is
a line for VPN Connections; slide to the right and a submenu will open, whose
second from last item is Configure VPN. Click it to get a list of VPN
definitions. Click on one (OpenVPN) and hit Edit, or hit Add
. Give the
root password (twice) if in paranoia mode. Fill out non-VPN tabs according to
your normal policy, specifically on the General tab, I mark All users may
connect to this network
. On the VPN tab for OpenVPN:
/CN=is prepended, but I believe I tried it and it didn't work; I never found out why. I leave the other items turned off.
To turn on OpenVPN, click on the Network Manager icon, slide to the right end of VPN Connections, and click on the menu item for OpenVPN (normal or tls/443). It just takes a few seconds to initiate the connection, if it's going to work. A padlock is added to the Network Manager icon. If it fails, look in /var/log/debug for clues (assuming one configured /var/log/debug).
These hosts were tested as clients:
KitKat. It is directly using cellular data on the wild side.
KitKat. It communicates on the wild side via Selen's Wi-Fi Hotspot (hostapd).
My firewall rules prevent many of these tests from working unless the payload packets go through the VPN tunnel. These tests may or may not work without the VPN:
featurethat if you connect to http(s)://example.net/ and it fails, including timeout, Firefox will retry on http(s)://www.example.net/ . In my case this is a CNAME to jacinth.example.net, which has the internal address so traffic will go through the tunnel and connect successfully. Nonetheless, when this behavior is noticed it should count as a failure.
On Android, the Hurricane Electric Network Tools app was used for the DNS
and ping tests, whereas on desktop Linux dig
and/or host
was
used. Firefox was used to test URLs on both OS's.
On Android when you use cellular data DHCP will give you the IPv4 addresses
of your ISP's DNS server(s). These will not give service to outside hosts,
specifically to packets coming from your VPN gateway. Therefore you need to
change the DNS server. On Android-4.2 Super Jelly Bean
and earlier,
you would do setprop net.dns1 8.8.8.8
(Google's free DNS service is
shown). However, starting in 4.3 or 4.4 KitKat
DNS queries are
directed to netd
, a local caching nameserver (which some forum posters
say is there to prevent ad blockers from working). There is a new API to
control who netd forwards to. And Android-4.4.3 and earlier has a bug in this
API, preventing DNS alteration apps from controlling netd. Fortunately,
CyanogenMod-11-M8 and later (2014-06-xx) is based on Android-4.4.4 which has
the bugfix.
IPSec on Android does not obey the DNS server announced over the VPN. To
get the right DNS server with IPSec I'm using the DNS Forwarder
app by
Evan He (free, ad supported), which has presets for many popular recursive DNS
services; you can also configure your own custom server. It requires root
access. For these tests I made changes in this order:
StrongSwan (IPSec) | OpenVPN (1194/udp) | OpenVPN (443/tcp) | |||||||
---|---|---|---|---|---|---|---|---|---|
Test | Selen | Mica | Xena | Selen | Mica | Xena | Selen | Mica | Xena |
How long to connect | 2s | 15s | 2s | 4s | 5s | 6s | 8s | 10s | 14s |
DNS for example.net [1] | ok | ok | ok[5] | ok | ok | ok[5] | ok | ok | ok[5] |
DNS for example.org [1] | ok | ok | ok[5] | ok | ok | ok[5] | ok | ok | ok[5] |
Ping to internal IPv4 adr | ok | ok | ok | ok | ok | ok | ok | ok | ok |
Ping to internal IPv6 adr | FAIL | FAIL | FAIL | FAIL | FAIL | FAIL | FAIL | FAIL | FAIL |
Ping to wild side IPv4 adr | ok | ok | ok | [3] | [3] | [3] | [3] | [3] | [3] |
Ping to example.net (wild side) | ok | ok | ok | [3] | [3] | [3] | [3] | [3] | [3] |
Ping to jacinth.example.net (internal) | ok | ok | ok | ok | ok | ok | ok | ok | ok |
Traceroute to arachne.math.ucla.edu [2] | ok | ok | ok | ok | ok | ok | ok | ok | ok |
http://jacinth.example.net/ | ok[4] | ok | ok | ok | ok | ok | ok | ok | ok |
https://jacinth.example.net:$PORT/ | ok[4] | ok | ok | ok | ok | ok | ok | ok | ok |
http://example.net/ | ok[4] | ok | ok | [3] | [3] | [3] | [3] | [3] | [3] |
https://example.net:$PORT/ | ok[4] | ok | ok | [3] | [3] | [3] | [3] | [3] | [3] |
Conclusion on testing: all three VPNs are fully functional except for these deficiencies:
Issues to be worked on in the future: