Setup IPSec VPN Server with Raspberry Pi

On my previous post, I shared how to configure a direct connection between my private home network and Google Cloud VPN. In my setup I was using my Raspberry Pi as my local VPN Gateway using OpenSwan. In this tutorial I’m going to show how I configure that.

Why did I choose Raspberry Pi?

First is I don’t own/have access to a dedicated VPN device/appliance. Having a Cisco ASA device would have been a good choice just to have that “Enterprise grade” experience but since this is just a POC, I think the Raspberry Pi is very well suited for this. Second, the low power consumption of this pocket-sized computer really makes it a better choice. Instead of running a power-hungry x86 server or DLXXX hardware, I could leave this one up and running all night without worrying about my electricity bill going up. But since we are using OpenSwan, you can definitely run this on any commodity hardware.

On with the installation

I have my pi up and running Raspbian Jessie Lite since I was using it as my Kodi media server. All I need to do now is to install openswan.

root@gateway:~# apt-get install openswan

When prompted ‘Use an X.509 certificate for this host?’, answer ‘No’. If you want to add it, use ‘dpkg-reconfigure openswan’ to come back.

Once installed, let’s configure our ipsec.secrets

root@gateway:~# vi /etc/ipsec.secrets

Add the following to the end of the line. Change raspberrypi_IP with the IP Address of your pi. Change the  pre-shared-key-password with something else. This will be used by both peers for authentication (RFC2409). Generate a long PSK with atleast 30 characters to mitigate brute force attack.

<raspberrypi_IP> %any: PSK "<pre-shared-key-password>"

We now need to define our VPN connection. Edit ipsec.conf

root@gateway:~# vi /etc/ipsec.conf

Add the following connection definition at the bottom part of the config file.

## connection definition in vpc-google ##
conn vpc-google     #vpc-google is the name of the connection
 auto=add
 authby=secret     #since we are using PSK, this is set to secret 
 type=tunnel #OpenSwan support l2tpd as well. For site-to-site use tunnel
 leftsubnet=192.168.0.0/24 # This is our local subnet.
 rightsubnet=10.128.0.0/20 # Remote site subnet.
 leftid=xx.xx.xxx.xx # My public IP
 left=192.168.0.100 # Raspberry PI ip address
 leftsourceip=192.168.0.100 # Raspberry PI ip address
 right=%any
 aggrmode=no

 

Under the default connection, I actually set the following

keyexchange=ike
nat_traversal=yes

I forgot to mention that this Raspberry PI is behind my router. I had to do port forwarding. IPSec uses udp port 500/4500. You need to do port forwarding if your gateway will be behind a router.

Restart openswan service.

root@gateway:~# service ipsec restart

All we need to do now is to configure a VPN Connection in GCP.

Once configured,  we can do the following to check if it’s working as expected.

Check ipsec status

root@gateway:~# service ipsec status
● ipsec.service - LSB: Start Openswan IPsec at boot time
 Loaded: loaded (/etc/init.d/ipsec)
 Active: active (running) since Thu 2017-07-20 14:23:39 UTC; 18s ago
 Process: 6866 ExecStop=/etc/init.d/ipsec stop (code=exited, status=0/SUCCESS)
 Process: 6964 ExecStart=/etc/init.d/ipsec start (code=exited, status=0/SUCCESS)
 CGroup: /system.slice/ipsec.service
 ├─7090 /bin/sh /usr/lib/ipsec/_plutorun --debug --uniqueids yes --force_busy no --nocrsend no --strictcrlpolicy no --nat_traversal yes -...
 ├─7091 logger -s -p daemon.error -t ipsec__plutorun
 ├─7094 /bin/sh /usr/lib/ipsec/_plutorun --debug --uniqueids yes --force_busy no --nocrsend no --strictcrlpolicy no --nat_traversal yes -...
 ├─7095 /bin/sh /usr/lib/ipsec/_plutoload --wait no --post
 ├─7096 /usr/lib/ipsec/pluto --nofork --secretsfile /etc/ipsec.secrets --ipsecdir /etc/ipsec.d --use-auto --uniqueids --nat_traversal --v...
 ├─7100 pluto helper # 0 
 └─7188 _pluto_adns

Jul 20 14:23:44 gateway pluto[7096]: "vpc-google"[1] 35.188.205.71 #1: new NAT mapping for #1, was 35.188.205.71:500, now 35.188.205.71:4500
Jul 20 14:23:44 gateway pluto[7096]: "vpc-google"[1] 35.188.205.71 #1: STATE_MAIN_R3: sent MR3, ISAKMP SA established {auth=OAKLEY_PRESHAR...modp1024}
Jul 20 14:23:44 gateway pluto[7096]: "vpc-google"[1] 35.188.205.71 #1: the peer proposed: 192.168.0.0/24:0/0 -> 10.128.0.0/20:0/0
Jul 20 14:23:44 gateway pluto[7096]: "vpc-google"[1] 35.188.205.71 #2: responding to Quick Mode proposal {msgid:3e4ab184}
Jul 20 14:23:44 gateway pluto[7096]: "vpc-google"[1] 35.188.205.71 #2: us: 192.168.0.0/24===192.168.0.100<192.168.0.100>[xx.xxx.xx.xx]
Jul 20 14:23:44 gateway pluto[7096]: "vpc-google"[1] 35.188.205.71 #2: them: 35.188.205.71===10.128.0.0/20
Jul 20 14:23:44 gateway pluto[7096]: "vpc-google"[1] 35.188.205.71 #2: transition from state STATE_QUICK_R0 to state STATE_QUICK_R1
Jul 20 14:23:44 gateway pluto[7096]: "vpc-google"[1] 35.188.205.71 #2: STATE_QUICK_R1: sent QR1, inbound IPsec SA installed, expecting QI2
Jul 20 14:23:45 gateway pluto[7096]: "vpc-google"[1] 35.188.205.71 #2: transition from state STATE_QUICK_R1 to state STATE_QUICK_R2
Jul 20 14:23:45 gateway pluto[7096]: "vpc-google"[1] 35.188.205.71 #2: STATE_QUICK_R2: IPsec SA established tunnel mode {ESP/NAT=>0x1baf69...DPD=none}
Hint: Some lines were ellipsized, use -l to show in full.
root@gateway:~#

35.188.205.71 is my GCP VPN Gateway IP. We need to see that IPsec SA established tunnel mode to confirm everything is working fine.

ipsec auto –status

root@gateway:~# ipsec auto --status
000 using kernel interface: netkey
000 interface lo/lo ::1
000 interface lo/lo 127.0.0.1
000 interface lo/lo 127.0.0.1
000 interface eth0/eth0 192.168.0.100
000 interface eth0/eth0 192.168.0.100
000 interface wlan0/wlan0 192.168.1.1
000 interface wlan0/wlan0 192.168.1.1
000 %myid = (none)
000 debug none
000 
000 virtual_private (%priv):
000 - allowed 6 subnets: 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, 25.0.0.0/8, fd00::/8, fe80::/10
000 - disallowed 0 subnets: 
000 WARNING: Disallowed subnets in virtual_private= is empty. If you have 
000 private address space in internal use, it should be excluded!
000 
000 algorithm ESP encrypt: id=2, name=ESP_DES, ivlen=8, keysizemin=64, keysizemax=64
000 algorithm ESP encrypt: id=3, name=ESP_3DES, ivlen=8, keysizemin=192, keysizemax=192
000 algorithm ESP encrypt: id=6, name=ESP_CAST, ivlen=8, keysizemin=40, keysizemax=128
000 algorithm ESP encrypt: id=11, name=ESP_NULL, ivlen=0, keysizemin=0, keysizemax=0
000 algorithm ESP encrypt: id=12, name=ESP_AES, ivlen=8, keysizemin=128, keysizemax=256
000 algorithm ESP encrypt: id=13, name=ESP_AES_CTR, ivlen=8, keysizemin=160, keysizemax=288
000 algorithm ESP encrypt: id=14, name=ESP_AES_CCM_A, ivlen=8, keysizemin=128, keysizemax=256
000 algorithm ESP encrypt: id=15, name=ESP_AES_CCM_B, ivlen=8, keysizemin=128, keysizemax=256
000 algorithm ESP encrypt: id=16, name=ESP_AES_CCM_C, ivlen=8, keysizemin=128, keysizemax=256
000 algorithm ESP encrypt: id=18, name=ESP_AES_GCM_A, ivlen=8, keysizemin=128, keysizemax=256
000 algorithm ESP encrypt: id=19, name=ESP_AES_GCM_B, ivlen=8, keysizemin=128, keysizemax=256
000 algorithm ESP encrypt: id=20, name=ESP_AES_GCM_C, ivlen=8, keysizemin=128, keysizemax=256
000 algorithm ESP auth attr: id=1, name=AUTH_ALGORITHM_HMAC_MD5, keysizemin=128, keysizemax=128
000 algorithm ESP auth attr: id=2, name=AUTH_ALGORITHM_HMAC_SHA1, keysizemin=160, keysizemax=160
000 algorithm ESP auth attr: id=5, name=AUTH_ALGORITHM_HMAC_SHA2_256, keysizemin=256, keysizemax=256
000 algorithm ESP auth attr: id=6, name=AUTH_ALGORITHM_HMAC_SHA2_384, keysizemin=384, keysizemax=384
000 algorithm ESP auth attr: id=7, name=AUTH_ALGORITHM_HMAC_SHA2_512, keysizemin=512, keysizemax=512
000 algorithm ESP auth attr: id=9, name=AUTH_ALGORITHM_AES_CBC, keysizemin=128, keysizemax=128
000 algorithm ESP auth attr: id=251, name=AUTH_ALGORITHM_NULL_KAME, keysizemin=0, keysizemax=0
000 
000 algorithm IKE encrypt: id=0, name=(null), blocksize=16, keydeflen=131
000 algorithm IKE encrypt: id=5, name=OAKLEY_3DES_CBC, blocksize=8, keydeflen=192
000 algorithm IKE encrypt: id=7, name=OAKLEY_AES_CBC, blocksize=16, keydeflen=128
000 algorithm IKE hash: id=1, name=OAKLEY_MD5, hashsize=16
000 algorithm IKE hash: id=2, name=OAKLEY_SHA1, hashsize=20
000 algorithm IKE hash: id=4, name=OAKLEY_SHA2_256, hashsize=32
000 algorithm IKE hash: id=6, name=OAKLEY_SHA2_512, hashsize=64
000 algorithm IKE dh group: id=2, name=OAKLEY_GROUP_MODP1024, bits=1024
000 algorithm IKE dh group: id=5, name=OAKLEY_GROUP_MODP1536, bits=1536
000 algorithm IKE dh group: id=14, name=OAKLEY_GROUP_MODP2048, bits=2048
000 algorithm IKE dh group: id=15, name=OAKLEY_GROUP_MODP3072, bits=3072
000 algorithm IKE dh group: id=16, name=OAKLEY_GROUP_MODP4096, bits=4096
000 algorithm IKE dh group: id=17, name=OAKLEY_GROUP_MODP6144, bits=6144
000 algorithm IKE dh group: id=18, name=OAKLEY_GROUP_MODP8192, bits=8192
000 algorithm IKE dh group: id=22, name=OAKLEY_GROUP_DH22, bits=1024
000 algorithm IKE dh group: id=23, name=OAKLEY_GROUP_DH23, bits=2048
000 algorithm IKE dh group: id=24, name=OAKLEY_GROUP_DH24, bits=2048
000 
000 stats db_ops: {curr_cnt, total_cnt, maxsz} :context={0,0,0} trans={0,0,0} attrs={0,0,0} 
000 
000 "vpc-google": 192.168.0.0/24===192.168.0.100<192.168.0.100>[xx.xx.xx.xx]...%any===10.128.0.0/20; unrouted; eroute owner: #0
000 "vpc-google": myip=192.168.0.100; hisip=unset;
000 "vpc-google": ike_life: 3600s; ipsec_life: 1200s; rekey_margin: 180s; rekey_fuzz: 100%; keyingtries: 3 
000 "vpc-google": policy: PSK+ENCRYPT+TUNNEL+PFS+IKEv2ALLOW+SAREFTRACK+lKOD+rKOD; prio: 24,20; interface: eth0; 
000 "vpc-google": newest ISAKMP SA: #0; newest IPsec SA: #0; 
000 "vpc-google": IKE algorithms wanted: AES_CBC(7)_256-SHA1(2)_000-MODP1024(2); flags=-strict
000 "vpc-google": IKE algorithms found: AES_CBC(7)_256-SHA1(2)_160-MODP1024(2)
000 "vpc-google": ESP algorithms wanted: AES(12)_256-SHA1(2)_000; flags=-strict
000 "vpc-google": ESP algorithms loaded: AES(12)_256-SHA1(2)_160
000 "vpc-google"[1]: 192.168.0.0/24===192.168.0.100<192.168.0.100>[xx.xx.xxx.xx]...35.188.205.71===10.128.0.0/20; erouted; eroute owner: #2
000 "vpc-google"[1]: myip=192.168.0.100; hisip=unset;
000 "vpc-google"[1]: ike_life: 3600s; ipsec_life: 1200s; rekey_margin: 180s; rekey_fuzz: 100%; keyingtries: 3 
000 "vpc-google"[1]: policy: PSK+ENCRYPT+TUNNEL+PFS+IKEv2ALLOW+SAREFTRACK+lKOD+rKOD; prio: 24,20; interface: eth0; 
000 "vpc-google"[1]: newest ISAKMP SA: #1; newest IPsec SA: #2; 
000 "vpc-google"[1]: IKE algorithms wanted: AES_CBC(7)_256-SHA1(2)_000-MODP1024(2); flags=-strict
000 "vpc-google"[1]: IKE algorithms found: AES_CBC(7)_256-SHA1(2)_160-MODP1024(2)
000 "vpc-google"[1]: IKE algorithm newest: AES_CBC_128-SHA1-MODP1024
000 "vpc-google"[1]: ESP algorithms wanted: AES(12)_256-SHA1(2)_000; flags=-strict
000 "vpc-google"[1]: ESP algorithms loaded: AES(12)_256-SHA1(2)_160
000 "vpc-google"[1]: ESP algorithm newest: AES_128-HMAC_SHA1; pfsgroup=<Phase1>
000 
000 #2: "vpc-google"[1] 35.188.205.71:4500 STATE_QUICK_R2 (IPsec SA established); EVENT_SA_REPLACE in 671s; newest IPSEC; eroute owner; isakmp#1; idle; import:not set
000 #2: "vpc-google"[1] 35.188.205.71 esp.1baf698c@35.188.205.71 esp.c810f1e5@192.168.0.100 tun.0@35.188.205.71 tun.0@192.168.0.100 ref=0 refhim=4294901761
000 #1: "vpc-google"[1] 35.188.205.71:4500 STATE_MAIN_R3 (sent MR3, ISAKMP SA established); EVENT_SA_REPLACE in 3070s; newest ISAKMP; lastdpd=20s(seq in:0 out:0); idle; import:not set
000 
root@gateway:~#

If tunnel isn’t coming up/establishing, your best pal is tcpdump. Initiate a ping or some traffic from the remote site to your local network. I prefer to start by pinging my local VPN gateway from one of my cloud instance.

root@gateway:~# tcpdump -n "port 4500" -vvvv
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
14:35:24.609126 IP (tos 0x0, ttl 64, id 448, offset 0, flags [DF], proto UDP (17), length 29)
 192.168.0.100.4500 > 35.188.205.71.4500: [bad udp cksum 0xb22a -> 0x2ba3!] isakmp-nat-keep-alive
14:35:24.609953 IP (tos 0x0, ttl 64, id 449, offset 0, flags [DF], proto UDP (17), length 29)
 192.168.0.100.4500 > 35.188.205.71.4500: [bad udp cksum 0xb22a -> 0x2ba3!] isakmp-nat-keep-alive

If everything is ok we can test connectivity from our local network to any of our remote instance.

root@gateway:~# ping 10.128.0.3
PING 10.128.0.3 (10.128.0.3) 56(84) bytes of data.
64 bytes from 10.128.0.3: icmp_seq=1 ttl=64 time=210 ms
64 bytes from 10.128.0.3: icmp_seq=2 ttl=64 time=211 ms
^C
--- 10.128.0.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 210.571/210.973/211.375/0.402 ms
root@gateway:~# ssh root@10.128.0.3
The authenticity of host '10.128.0.3 (10.128.0.3)' can't be established.
ECDSA key fingerprint is 8f:f7:62:4f:1e:85:ad:1e:50:cc:bc:21:fd:ae:bb:9e.
Are you sure you want to continue connecting (yes/no)? 

From the above, we can see that we are able to connect to one of my instance in GCP.

 

 

Connecting On-Premise network to a Virtual Private Cloud Network

Last time I’ve worked with configuring two sites using OpenSwan was more than 10 years ago. Had a good success deploying that solution with good throughput using commodity hardware and leveraging Open Source solutions. This time I wanted to test if I could do the same using Raspberry Pi.

In this post I want to show how I was able to configure Google Compute Engine VPN and connect my home network.

Under Networking – click VPN. Click Create VPN connection. This will open up a page which will guide you on creating a VPN connection.

Under the Create a VPN connection form, we must first create a static IP Address that can be used by our VPN Gateway. Under the IP Address dropdown list box, select Create IP Address.

 

Put in a name to distinguish this IP Address and click RESERVE.

Complete the form by giving it a Name and selecting a Region where we could deploy this VPN Gateway. Here I am using us-central-1.

Put in your VPN Gateway IP Address in the Remote peer IP address. This is the IP Address of your home network VPN Gateway. I am currently using a Raspberry Pi installed with OpenSwan. I am using port forwarding (udp 500/4500) since this gateway is behind my router. (Installation and configuration of OpenSwan/IPSec on Raspberry Pi deserves a separate post)

Select the IKE version. Mine is using IKEv1.

In the Remote network IP ranges, enter your home network range. Select the Local subnetworks (Google Cloud side) which you want to associate this tunnel to.

Click Create. Deploying this could take a minute or two to complete.

Once done, you should be able to see that the Remote peer IP address is up with a green check icon.

In one of my compute instance, I can verify that the tunnel is up by pinging my home network.

Or by running TCP dump on my local VPN gateway

We now have a secure way of connecting our on-premise network to our Virtual Private Cloud network.

One thing to note, if you are deleting the VPN Connection, you must also release the IP Address you allocated to the VPN Gateway so as not to incur additional cost since that is a Static IP Address.

 

 

 

Google Cloud Platform Functions

I want to share my experience testing Google Cloud Platform’s Serverless offering called Cloud Functions. I’ve been playing around GCP for a more than a week now (moving my site, resizing it! etc) and I had this itch to test Cloud Functions after coming out from an AWS SNG User Group meeting which talked about AWS Lambda.

Let’s get started.

Login to your Cloud Console (if you haven’t subscribed yet, go sign up for an account and make use of that $300 Free Tier offering for a year!).

Under COMPUTE section, click Cloud Functions.

Click Create Function

Give your function a name. I’m going to use storageTriggeredImageProcessor. I’ll be using the us-central-1 Region  (since it’s under the Free tier). You need to consider latency when selecting Region.  Not sure if GCP would have something like Lambda Edge in the future.

Remember for Cloud Functions, you are billed by how long your function executes and the amount of memory you allocate to it. I’ll choose 128 MB for this test.

For Timeout, i’ll stick with what’s shown which is 60s.

We are going to use Cloud Storage Bucket under the Trigger section. This will ask us to select which bucket to “monitor”.

Under the source code section, we are presented with a skeleton template of our function. package.json holds the information of what other APIs you want to use. You can copy the code and package.json on my Github repository.

Cloud Functions needs to be staged in Google Storage buckets. Select or create one where we could store our codes.

Click Create.

It would take a minute or two for GCP to stage our function.

Once done, we can now test this. Since we are only logging the output of this function, under Function Details, click View Logs

On another window, upload an image file to your Storage bucket and watch the log entries.

I will be uploading the image below

And on the logs, we can see what Google Vision detected on this image.

We’ve successfully created and tested GCP’s Cloud Functions. Remember we actually run an application without provisioning application servers (Serverless!)

Note:

If you wan’t to use Google Vision API or any other APIs, you need to enable it under the API Section. Else your function won’t execute correctly and will throw an exception.

 

 

Deploying a LAMP server in Google Cloud Platform

Since my Raspberry Pi, which hosts my WordPress site, has been quirky the past couple of weeks I decided to try out Google Cloud Platform.

Like AWS 12 months Free-Tier, GCP is giving out $300-free trial to get you started. You would need to have a credit card to sign up and you could read more information about the Free Trial in this link.

Using GCP’s Cloud Launcher, we can deploy solutions and applications in an instant (AWS MarketPlace?). In this tutorial, we are deploying a LAMP server which could maybe host a WordPress site.

Login to GCP Console and select/create a project. Under Computer Engine – VM instances, click CREATE INSTANCE.

 

Select a zone in any of the following: us-east1, us-west1, and us-central1. Using an f1-micro Machine type we could be well within the “744 hours monthly free limit”. Capacity might not be enough but this could get me start a LAMP server.

I want to use CentOS this time so under Boot disk, select the CentOS 7 image. The 10GB persisitent disk is well enough for my site so I’ll just go ahead and click select.

 

I’ll just check both option to allow HTTP and HTTPS under the Firewall section and hit Create to start deploying this instance.

 

Once GCP is done, you should see the following

Click SSH. This will open a new window which automatically gives us an SSH connection to our instance. (No more managing keys?)

sudo su to become root and keep things up to date by running yum update -y

Let’s install apache by running the following

yum install httpd

Install mariadb by executing the following

yum install mariadb-server mariadb

Install php and php-mysql

yum install php php-mysql

Let’s now configure our Mysql/MariaDB server. Change the root account, and remove the test user, database when prompted after running the following

mysql_secure_installation

make sure to have Apache and MariaDB start at boot time

chkconfig httpd on
chkconfig mariadb on

If it’s not yet started, start Apache and MariaDB server.

service mariadb start
service httpd start

Using the External IP of our instance, let’s check if our Web server is accessible

We could create a simple page to test if PHP is working

[root@wordpress paulinomreyes]# cat /var/www/html/info.php
<?php

phpinfo();

?>
[root@wordpress paulinomreyes]# 

And navigating to info.php we should see the following

We just deployed a LAMP server on Google Cloud Platform. Using this instance, we could then deploy a WordPress site.

Google Cloud Platform in my opinion is “minimalist” (compared to how huge AWS offers) but this platform gives me the basic things I need for now . In the future, I want to try out how to configure Load Balancing, and how Container Engine and Cloud Functions ( equivalent I guess to AWS Lambda) works.