IPSec Almighty

Good afternoon friends. It's no secret that many of us have at least once, but have had to deal with the need to configure a VPN. Being an active reader of Habr, I noticed that despite the abundance of articles about IPSec, for many it still seems to be something complicated and overloaded. In this article I will try to dispel these myths using my own fully working configuration as an example. In four examples, we will completely go through the configuration of the most popular Linux (Strongswan) solution, from a simple tunnel with side authentication with PSK keys to a host-to-host connection with authentication of both sides based on certificates from Let's Encrypt. Interesting? Welcome to cat!

Background


Initially, the VPN was planned only for the organization of the channel between the mini-router of the parents and the home "bedside" server, which concurrently acts as a router.

After a short period of time, Keenetic was added to this company from two devices.
But once starting, it turned out to be difficult to stop, and soon phones and a laptop appeared on the diagram, who wanted to hide from the all-seeing advertising eye of MT_Free and other unencrypted WiFi networks.

Then the beloved ILV finally got stronger the Banhammer, whom he incredibly fell in love with publicly swinging in all directions, and in order to neutralize his concern for mere mortals, he had to support the foreign IT sector to acquire VPS abroad.
In addition, a certain citizen who looks like Shapoklyak, running around everywhere with her reticule by the Package, and probably believing that ā€œWho helps people, is wasting time. You cannot become famous for good deeds, ā€I wanted to secretly peek at someone else's traffic and take it in pencil. We will also have to defend ourselves against such unsolicited love and VPN in this case exactly what the doctor ordered.

To summarize a short summary. It was necessary to find a solution that ideally could close several tasks at once:

  • Interconnect between Linux routers
  • Build a tunnel between Linux and Keenetic Household
  • Give access to home resources and the Internet to wearable devices (telephones, laptops) from untrusted networks
  • Create a securely encrypted tunnel to the remote VPS

Do not forget about the wonderful KISS principle - Keep It Simple, Stupid. The fewer components will be involved and the easier it is to configure each of them - the more reliable.

Overview of existing solutions


Briefly go over what is now:

PPTP

Grandfather Lenin of all protocols. Died, "Decayed on mold and linden honey."

L2TP

Does anyone but one provider use this?

Wireguard

project is developing. Actively sawn. It is easy to create a tunnel between two peers having a static IP. In other cases, crutches, bicycles with square wheels and a blue electrical tape are always ready to help, but this is not our way.

OpenVPN

Pros:

  • Support for multiple platforms - Windows, Linux, OpenWRT and its derivatives, Android
  • Strong encryption and certificate support.
  • Flexibility of customization.

And cons:

  • Work entirely in user-space.
  • Limited support from home routers - krenenko-kosenko on Mikrotik (without detracting from the other advantages of the glands) and normal in OpenWRT.
  • Difficulties with setting up mobile clients: you need to download, or create your own installer, copy configs somewhere.
  • If there are several tunnels, dances await with editing systemd units on the server.

OpenConnect (open-source implementation of the Cisco Anyconnect protocol)
A very interesting solution about which, unfortunately, is quite a bit of information.

Pros:

  • Relatively broad support for various platforms - Windows, Android, Mac based on the Cisco Anyconnect native application from the store - is an ideal option to provide access to the internal network of wearable devices.
  • Strong encryption, certificate support, 2FA connectivity
  • The protocol itself is fully TLS-based (unlike OpenVPN, which is easily detected on port 443). In addition to TLS, DTLS is also supported - during the established session, the client can switch to transmitting data via UDP and vice versa.
  • Excellent coexistence on one port of both a VPN and a full-fledged web server using sniproxy.
  • Easy setup of both server and clients.

Here, too, were not without cons:

  • Work entirely in user-space.
  • TCP on top of TCP is a bad idea.
  • There is no support from customer-grade equipment.
  • The complexity of installing tunnels between two Linux: theoretically possible, practically - it is better to spend time on something more useful.
  • If there are several tunnels, dances with several configs and editing systemd units are waiting.

It would seem like a dead end, but after taking a closer look and spending a bit of time studying, I realized that IPSec based on IKEv2 is able to replace everything else.

IKEv2 IPSEC

Pros:

  • With the advent of IKEv2, the protocol itself has become easier to configure, compared to the previous version, but at the cost of losing backward compatibility.
  • Thanks to standardization, work is provided anywhere and on anything - the list can be maintained indefinitely. Linux, Mikrotik (in the latest versions of RouterOS), OpenWRT, Android, iPhone. Windows also has native support starting with Windows 7.
  • High speed: traffic processing completely in kernel-space. The user-space part is needed only for setting connection parameters and monitoring the channelā€™s health.
  • The ability to use several authentication methods: using both PSK and certificates, and in any combination.
  • Several operating modes: tunnel and transport. How they differ can be read including on HabrĆ©.
  • Undemanding settings for intermediate nodes: if in the first version of IKE there were problems caused by NAT, then IKEv2 has built-in mechanisms for overcoming NAT and native fragmentation of IKE messages, which allows you to establish a connection on channels with an MTU curve. Looking ahead, Iā€™ll say that in practice Iā€™ve never encountered a WiFi network, wherever a client can establish a connection.

Cons, however, also have:

  • You need to spend some time studying and understanding how it works.
  • A feature that can confuse a newbie: IPSec, unlike conventional VPN solutions, does not create network interfaces. Only traffic processing policies are set, everything else is resolved by means of firewall.

Before proceeding with the setup, we assume that the reader is already a little familiar with the basic concepts and terms. To help a beginner, you can recommend an article from Wikipedia and Habr himself, on which there are already quite interesting and useful articles on this topic.

Getting started setting up


Having decided on the decision, we proceed to the configuration. The network diagram in my case has the following form (removed under the spoiler)

Network diagram

ipsecgw.example.com is the home server that is the center of the network. External IP 1.1.1.1. An internal network 10.0.0.0/23 and another address 10.255.255.1/30 for setting a private BGP session with VPS;
mama is a Linux router based on a small, silent nettop installed by parents. The ISP issues a dynamic IP address. Internal network 10.0.3.0/24;
keenetic - Keenetic router with IPSec installed. The ISP issues a dynamic IP address. Internal network 10.0.4.0/24;
road-warriors - portable devices connecting from untrusted networks. Addresses are issued to clients dynamically when connected from the internal pool (10.1.1.0/24);
rkn.example.com- VPS outside the jurisdiction of a respected ILV. External IP - 5.5.5.5, internal address 10.255.255.2/30 for setting a private BGP session.

First step. From simple to complex: tunnels using pre-shared keys (PSK)


On both Linux-boxes we install the necessary packages:

sudo yum install strongswan

On both hosts, open the ports 500 / udp, 4500 / udp and allow the passage of the ESP protocol.
Edit the file /etc/strongswan/ipsec.secrects (on the host side ipsecgw.example.com) and add the following line:

mama@router.home.local: PSK "Very strong PSK"

On the second side, similarly:

root@root.mama.local: PSK "Very strong PSK"

In this case, the ID is a fictitious email address. More information can be found on the official wiki .

Secrets saved, moving on.

On the ipsecgw.example.com host, edit the /etc/strongswan/ipsec.conf file:

config setup //   charon
    charondebug = "dmn 0, mgr 0, ike 0, chd 0, job 0, cfg 0, knl 0, net 0, asn 0, enc 0, lib 0, esp 0, tls 0, tnc 0, imc 0, imv 0, pts 0" //  
conn %default //    
    reauth = yes
    rekey = yes
    keyingtries = %forever
    keyexchange = ikev2 //      - IKEv2
    dpdaction = hold
    dpddelay = 5s // 5   DPD (Dead Peer Detection)   
    mobike = yes // Mobile IKE -     IP    
conn mama //  
    left = %defaultroute //Left -  .  %defaultroute       IKE- ,    default route
    right = %any //     IP-
    authby = psk //   -   
    leftid = mama@router.home.local // ID,   ipsec.secrets
    rightid = root@router.mama.local //ID  
    leftsubnet = 10.0.0.0/23,10.1.1.0/24 
    rightsubnet = 10.0.3.0/24
    type = tunnel 
    ike = aes256-aes192-aes128-sha256-sha384-modp2048-modp3072-modp4096-modp8192,aes128gcm16-sha384-x25519!
    esp = aes256-aes192-aes128-sha256-sha384-modp2048-modp3072-modp4096-modp8192,aes128gcm16-sha256-sha384-x25519! 
    auto = add //  charon        

Similarly, we edit on the remote peer /etc/strongswan/ipsec.conf:

config setup
    charondebug = "dmn 0, mgr 0, ike 0, chd 0, job 0, cfg 0, knl 0, net 0, asn 0, enc 0, lib 0, esp 0, tls 0, tnc 0, imc 0, imv 0, pts 0"
conn %default
    reauth = yes
    rekey = yes
    keyingtries = %forever
    keyexchange = ikev2
    dpdaction = restart
    dpddelay = 5s
    mobike = yes
conn mama
    left = %defaultroute
    right = ipsecgw.example.com
    authby = psk
    leftid = root@router.mama.local
    rightid = mama@router.home.local
    leftsubnet = 10.0.3.0/24
    rightsubnet = 10.0.0.0/23,10.1.1.0/24
    type = tunnel
    ike = aes128gcm16-sha384-x25519!
    esp = aes128gcm16-sha384-x25519!
    auto = route

If you compare the configs, you can see that they are almost mirrored, only the definitions of peers are cross-exchanged.

The auto = route directive causes charon to set a trap for traffic falling into the left / rightsubnet (traffic selectors) directives. Coordination of the tunnel parameters and key exchange will begin immediately after the appearance of traffic that falls under the given conditions.

On the ipsecgw.example.com server in the firewall settings, we prohibit masquerading for a 10.0.3.0/24 network. Allow packet forwarding between 10.0.0.0/23 and 10.0.3.0/24 and vice versa. On the remote host, we perform similar settings by disabling masquerading for the 10.0.0.0/23 network and setting up forwarding.

We restart strongswan on both servers and try to ping the central node:

sudo systemctl restart strongswan
ping 10.0.0.1


Make sure everything works:
sudo strongswan status
Security Associations (1 up, 0 connecting):
        mama[53]: ESTABLISHED 84 minutes ago, 1.1.1.1[mama@router.home.local]...2.2.2.2[root@router.mama.local]
        mama{141}:  INSTALLED, TUNNEL, reqid 27, ESP in UDP SPIs: c4eb45fe_i ca5ec6ca_o
        mama{141}:   10.0.0.0/23 10.1.1.0/24 === 10.0.3.0/24


It will also be useful to make sure that the make_before_break parameter is set to yes in the /etc/strongswan/strongswan.d/charon.conf file at all peers. In this case, the charon daemon serving the IKEv2 protocol will not delete the current security association during the key change procedure, but will create a new one first.

Step Two The appearance of Keenetic


A pleasant surprise was the built-in IPSec VPN in the official Keenetic firmware. To activate it, just go to the KeeneticOS Component Settings and add the IPSec VPN package .

We prepare the settings on the central node, for this:

Edit /etc/strongswan/ipsec.secrects and add the PSK for the new peer:

keenetic@router.home.local: PSK "Keenetic+PSK"

Edit /etc/strongswan/ipsec.conf and add another connection to the end:

conn keenetic
    left = %defaultroute
    right = %any
    authby = psk
    leftid = keenetic@router.home.local
    rightid = root@router.keenetic.local
    leftsubnet = 10.0.0.0/23
    rightsubnet = 10.0.4.0/24
    type = tunnel
    ike = aes256-aes192-aes128-sha256-sha384-modp2048-modp3072-modp4096-modp8192,aes128gcm16-sha384-x25519!
    esp = aes256-aes192-aes128-sha256-sha384-modp2048-modp3072-modp4096-modp8192,aes128gcm16-sha256-sha384-x25519!
    auto = add

On the Keenetic side, configuration is performed in the WebUI along the path: Internet -> Connections ->
Other Connections
. It's pretty simple
(3 pictures)






If you plan to drive significant volumes of traffic through the tunnel, then you can try to enable hardware acceleration, which is supported by many models. Enabled by the crypto engine hardware command in the CLI. To disable and process encryption and hashing processes using general-purpose CPU instructions - crypto engine software

After saving the settings, we restore strongswan and let Keenetic think for half a minute. Then in the terminal we see a successful connection:

Everything is working:
sudo strongswan status
Security Associations (2 up, 0 connecting):
    keenetic[57]: ESTABLISHED 39 minutes ago, 1.1.1.1[keenetic@router.home.local]...3.3.3.3[root@router.keenetic.local]
    keenetic{146}:  INSTALLED, TUNNEL, reqid 29, ESP SPIs: ca8f556e_i ca11848a_o
    keenetic{146}:   10.0.0.0/23 === 10.0.4.0/24
        mama[53]: ESTABLISHED 2 hours ago, 1.1.1.1[mama@router.home.local]...2.2.2.2[root@router.mama.local]
        mama{145}:  INSTALLED, TUNNEL, reqid 27, ESP in UDP SPIs: c5dc78db_i c7baafd2_o
        mama{145}:   10.0.0.0/23 10.1.1.0/24 === 10.0.3.0/24


Step Three Protecting mobile devices


After reading a bunch of manuals and a bunch of articles, it was decided to stop at a bunch of a free certificate from Let's Encrypt to authenticate the server and classic authorization by login and password for clients. Thus, we get rid of the need to maintain our own PKI infrastructure, monitor the expiration of keys and perform unnecessary gestures with mobile devices by installing self-signed certificates in the trusted list.

Install the missing packages:

sudo yum install epel-release
sudo yum install certbot

We get the standalone certificate (do not forget to open 80 / tcp in the iptables settings first):

sudo certbot certonly --standalone -d ipsecgw.example.com

After certbot has completed its work, we must teach Strongswan to see our certificate:

  • in the directory /etc/strongswan/ipsec.d/cacerts create 2 symbolic links: one to the root store of trusted certificates in / etc / pki / tls / certs; and a second with the name ca.pem pointing to /etc/letsencrypt/live/ipsecgw.example.com/chain.pem
  • Two symlinks are also created in the /etc/strongswan/ipsec.d/certs directory: the first, with the name certificate.pem, refers to the file /etc/letsencrypt/live/ipsecgw.example.com/cert.pem. And the second, named fullchain.pem, linking to /etc/letsencrypt/live/ipsecgw.example.com/fullchain.pem
  • In the /etc/strongswan/ipsec.d/private directory, place the key.pem symlink pointing to the private key generated by certbot and lying along the path /etc/letsencrypt/live/ipsecgw.example.com/privkey.pem

Add authentication via RSA to ipsec.secrets and a bunch of logins / passwords for new users:

ipsecgw.example.com     : RSA key.pem
username phone          : EAP "Q1rkz*qt"
username notebook       : EAP "Zr!s1LBz"

We restart Strongswan and when calling sudo strongswan listcerts we should see the certificate information:

List of X.509 End Entity Certificates

  subject:  "CN=ipsecgw.example.com"
  issuer:   "C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X3"
  validity:  not before May 23 19:36:52 2020, ok
             not after  Aug 21 19:36:52 2020, ok (expires in 87 days)
  serial:    04:c7:70:9c:a8:ce:57:cc:bf:6f:cb:fb:d3:a9:cf:06:b0:a8
  altNames:  ipsecgw.example.com
  flags:     serverAuth clientAuth
  OCSP URIs: http://ocsp.int-x3.letsencrypt.org
  certificatePolicies:
             2.23.140.1.2.1
             1.3.6.1.4.1.44947.1.1.1
             CPS: http://cps.letsencrypt.org

Then we describe the new connection in ipsec.conf :

conn remote-access
    dpddelay = 30s //   DPD ,     
    left = %defaultroute
    leftid = "CN=ipsecgw.example.com"
    leftcert = fullchain.pem //         
    leftsendcert = always
    leftsubnet = 0.0.0.0/0 //      
    right = %any
    rightid = %any
    rightauth = eap-mschapv2 // ,  EAP-MSCHAP2
    rightsendcert = never
    eap_identity = %identity 
    rightsourceip = 10.1.1.0/24 //Strongswan       
    rightdns = 10.0.0.1,10.0.0.3 //   DNS
    type = tunnel
    ike = aes256-aes192-aes128-sha256-sha384-modp2048-modp3072-modp4096-modp8192,aes128gcm16-sha384-x25519!
    esp = aes256-aes192-aes128-sha256-sha384-modp2048-modp3072-modp4096-modp8192,aes128gcm16-sha256-sha384-x25519!
    auto = add //      
    dpdaction = restart // ,      DPD

Do not forget to edit the file / etc / sysconfig / certbot indicating that we will also update the certificate as standalone, adding CERTBOT_ARGS = "- standalone" to it.

Also, do not forget to turn on the certbot-renew.timer timer and set the hook to restart Strongswan in case of issuing a new certificate. To do this, either place a simple bash script in / etc / letsencrypt / renewal-hooks / deploy /, or edit the / etc / sysconfig / certbot file again.

We restart Strongswan, turn on masquerading for the 10.1.1.0/24 network in iptables and go on to configure mobile devices.

Android


Install the Strongswan application from Google Play .

We launch and create a new

profile


We save the profile, connect and, after a second, we donā€™t have to worry about someone being able to spy on us.

We check:
sudo strongswan statusall
Security Associations (3 up, 0 connecting):
remote-access[109]: ESTABLISHED 2 seconds ago, 1.1.1.1[CN=ipsecgw.example.com]...4.4.4.4[phone]
remote-access{269}:  INSTALLED, TUNNEL, reqid 55, ESP in UDP SPIs: c706edd1_i e5c12f1d_o
remote-access{269}:   0.0.0.0/0 ::/0 === 10.1.1.1/32
        mama[101]: ESTABLISHED 34 minutes ago, 1.1.1.1[mama@router.home.local]...2.2.2.2[root@router.mama.local]
        mama{265}:  INSTALLED, TUNNEL, reqid 53, ESP in UDP SPIs: c8c83342_i c51309db_o
        mama{265}:   10.0.0.0/23 10.1.1.0/24 === 10.0.3.0/24
    keenetic[99]: ESTABLISHED 36 minutes ago, 1.1.1.1[keenetic@router.home.local]...3.3.3.3[root@router.keenetic.local]
    keenetic{263}:  INSTALLED, TUNNEL, reqid 52, ESP SPIs: c3308f33_i c929d6f1_o
    keenetic{263}:   10.0.0.0/23 === 10.0.4.0/24


Windows


Windows of current versions was pleasantly surprised. All configuration of the new VPN takes place by calling two PowerShell cmdlets:

Add-VpnConnection -Name "IKEv2" -ServerAddress ipsecgw.example.com -TunnelType "IKEv2"
Set-VpnConnectionIPsecConfiguration -ConnectionName "IKEv2" -AuthenticationTransformConstants SHA256128 -CipherTransformConstants AES128 -EncryptionMethod AES128 -IntegrityCheckMethod SHA256 -PfsGroup PFS2048 -DHGroup Group14 -PassThru -Force

And one more thing, if Strongswan is configured to issue IPv6 addresses to clients (yes, he can do it too):

Add-VpnConnectionRoute -ConnectionName "IKEv2" -DestinationPrefix "2000::/3"

Part Four, Final. We cut a window to Europe


Having seen the provider stubs ā€œThe site was blocked by the decision of the left heel of the fifth deputy prosecutor of the village Trudovye Mozoli of Bogozabyt countyā€, a small, inconspicuous VPS appeared (with the well-sounding domain name rkn.example.com) a thousand kilometers from monkeys who like to wave with a banhammer and block networks of size / 16 at a time. And spinning on this little VPS was a wonderful creation of colleagues from NIC.CZ called BIRD. The bird of the first version constantly died in a panic from the activity of monkeys with batons, who banned almost 4% of the Internet at the peak of their labor activity, leaving for deep thoughtfulness during reconfig, therefore it was updated to version 2.0.7. If readers are interested, Iā€™ll publish an article on the transition from BIRD to BIRD2, in which the config format has changed dramatically,but the new version has become much faster and there are no problems with reconfig with a large number of routes. And since we use the dynamic routing protocol, then there must be a network interface through which you need to route traffic. By default, IPSec does not create interfaces, but due to its flexibility, we can use the classic GRE tunnels, which we will protect in the future. As a bonus, the ipsecgw.example.com and rkn.example.com hosts will authenticate each other using Lets Encrypt self-renewing certificates. No PSK, only certificates, only hardcore, there is not much security.By default, IPSec does not create interfaces, but due to its flexibility, we can use the classic GRE tunnels, which we will protect in the future. As a bonus, the ipsecgw.example.com and rkn.example.com hosts will authenticate each other using Lets Encrypt self-renewing certificates. No PSK, only certificates, only hardcore, there is not much security.By default, IPSec does not create interfaces, but due to its flexibility, we can use the classic GRE tunnels, which we will protect in the future. As a bonus, the ipsecgw.example.com and rkn.example.com hosts will authenticate each other using Lets Encrypt self-renewing certificates. No PSK, only certificates, only hardcore, there is not much security.

We believe that the VPS is prepared, Strongswan and Certbot are already installed.

On the ipsecgw.example.com host (its IP is 1.1.1.1), we describe the new gif0 interface:
sudo vi /etc/sysconfig/network-scripts/ifcfg-gif0
DEVICE="gif0"
MY_OUTER_IPADDR="1.1.1.1"
PEER_OUTER_IPADDR="5.5.5.5"
MY_INNER_IPADDR="10.255.255.1/30"
PEER_INNER_IPADDR="10.255.255.2/30"
TYPE="GRE"
TTL="64"
MTU="1442"
ONBOOT="yes"

Mirrored on the host vps.example.com (its IP is 5.5.5.5):

sudo vi /etc/sysconfig/network-scripts/ifcfg-gif0
DEVICE="gif0"
MY_OUTER_IPADDR="5.5.5.5"
PEER_OUTER_IPADDR="1.1.1.1"
MY_INNER_IPADDR="10.255.255.2/30"
PEER_INNER_IPADDR="10.255.255.1/30"
TYPE="GRE"
TTL="64"
MTU="1442"
ONBOOT="yes"

We raise the interfaces, but since iptables does not have a rule that allows the GRE protocol, traffic will not go (which is what we need, since there is no protection inside GRE against fans of any kind of legislative ā€œpackagesā€).

Cooking VPS


First, we get another certificate for the domain name rkn.example.com. Create symlinks in /etc/strongswan/ipsec.d as described in the previous section.

Edit ipsec.secrets by adding a single line to it:

rkn.example.com     : RSA key.pem

Edit ipsec.conf:

config setup
    charondebug = "dmn 0, mgr 0, ike 0, chd 0, job 0, cfg 0, knl 0, net 0, asn 0, enc 0, lib 0, esp 0, tls 0, tnc 0, imc 0, imv 0, pts 0"
    strictcrlpolicy = yes
conn %default
    reauth = yes
    rekey = yes
    keyingtries = %forever
    keyexchange = ikev2
    dpdaction = restart
    dpddelay = 5s
    mobike = yes
conn rkn
    left = %defaultroute
    right = ipsecgw.example.com
    authby = pubkey
    leftcert = fullchain.pem
    leftsendcert = always
    leftauth = pubkey
    rightauth = pubkey
    leftid = "CN=rkn.example.com"
    rightid = "CN=ipsecgw.example.com"
    rightrsasigkey = /etc/strongswan/ipsec.d/certs/ipsecgw.example.com.pem
    leftsubnet = %dynamic
    rightsubnet = %dynamic
    type = transport
    ike = aes256gcm16-sha384-x25519!
    esp = aes256gcm16-sha384-x25519!
    auto = route

On the host side, ipsecgw.example.com is also added to ipsec.conf in the setup section the strictcrlpolicy = yes parameter, which includes strict CRL checking. And describe another connection:

conn rkn
    left = %defaultroute
    right = rkn.example.com
    leftcert = fullchain.pem
    leftsendcert = always
    leftauth = pubkey
    rightauth = pubkey
    rightrsasigkey = /etc/strongswan/ipsec.d/certs/rkn.exapmle.com.pem
    leftid = "CN=ipsecgw.example.com"
    rightid = "CN=rkn.example.com"
    leftsubnet = %dynamic
    rightsubnet = %dynamic
    type = transport
    ike = aes256gcm16-sha384-x25519!
    esp = aes256gcm16-sha384-x25519!
    auto = route
    dpdaction = restart

Configs are almost mirrored. The attentive reader could already pay attention to a couple of points:

  • left / rightsubnet =% dynamic - instructs Strongswan to apply policies to all types of traffic between peers
  • rightrsasigkey. IKE SA IKE AUTH ERROR , Strongswan RSA- . openssl. (ipsecgw RKN) sudo /usr/bin/openssl rsa -in /etc/letsencrypt/live/ipsecgw.example.com/privkey.pem -pubout > ~/ipsecgw.example.com.pem sudo /usr/bin/openssl rsa -in /etc/letsencrypt/live/rkn.example.com/privkey.pem -pubout > ~/rkn.example.com.pem, scp ,

Do not forget to configure the firewall and auto-renew certificates. After restarting Strongswan on both servers, start ping the far side of the GRE tunnel and see a successful connection. On VPS (rkn):

sudo strongswan status
Routed Connections:
         rkn{1}:  ROUTED, TRANSPORT, reqid 1
         rkn{1}:   5.5.5.5/32 === 1.1.1.1/32
Security Associations (1 up, 0 connecting):
         rkn[33]: ESTABLISHED 79 minutes ago, 5.5.5.5[CN=rkn.example.com]...1.1.1.1[CN=ipsecgw.example.com]
         rkn{83}:  INSTALLED, TRANSPORT, reqid 1, ESP SPIs: cb4bc3bb_i c4c35a5a_o
         rkn{83}:   5.5.5.5/32 === 1.1.1.1/32


And on the host side ipsecgw
under the spoiler
Routed Connections:
         rkn{1}:  ROUTED, TRANSPORT, reqid 1
         rkn{1}:   1.1.1.1/32 === 5.5.5.5/32
Security Associations (4 up, 0 connecting):
remote-access[10]: ESTABLISHED 5 seconds ago, 1.1.1.1[CN=ipsecgw.example.com]...4.4.4.4[phone]
remote-access{12}:  INSTALLED, TUNNEL, reqid 7, ESP in UDP SPIs: c7a31be1_i a231904e_o
remote-access{12}:   0.0.0.0/0 === 10.1.1.1/32
    keenetic[8]: ESTABLISHED 22 minutes ago, 1.1.1.1[keenetic@router.home.local]...3.3.3.3[root@router.keenetic.local]
    keenetic{11}:  INSTALLED, TUNNEL, reqid 6, ESP SPIs: cfc1b329_i c01e1b6e_o
    keenetic{11}:   10.0.0.0/23 === 10.0.4.0/24
        mama[4]: ESTABLISHED 83 minutes ago, 1.1.1.1[mama@router.home.local]...2.2.2.2[root@router.mama.local]
        mama{8}:  INSTALLED, TUNNEL, reqid 3, ESP in UDP SPIs: c4a5451a_i ca67c223_o
        mama{8}:   10.0.0.0/23 10.1.1.0/24 === 10.0.3.0/24
         rkn[3]: ESTABLISHED 83 minutes ago, 1.1.1.1[CN=ipsecgw.example.com]...5.5.5.5[CN=rkn.example.com]
         rkn{7}:  INSTALLED, TRANSPORT, reqid 1, ESP SPIs: c4c35a5a_i cb4bc3bb_o
         rkn{7}:   1.1.1.1/32 === 5.5.5.5/32


The tunnel is installed, pings go, in tcpdump it is visible that only ESP goes between hosts. It would seem possible to rejoice. But you can not relax without checking everything to the end. We are trying to reissue the certificate for VPS and ...

Chef, it's all broken


We begin to understand and stumble upon one unpleasant feature of Let's Encrypt, which is beautiful in everything else - with any reissue of the certificate, the private key associated with it also changes. The private key has changed - and the public has changed. At first glance, the situation is hopeless for us: even if we can easily extract the public key during re-issuance of the certificate using the hook in certbot and pass it to the remote side via SSH, it is not clear how to make the remote Strongswan re-read it. But help came from where they did not wait - systemd can monitor changes to the file system and run the services associated with the event. This is what we will use.

We will create a keywatcher service user on each of the hosts with the most restricted rights, generate SSH keys for each of them, and exchange them between the hosts.

On the ipsecgw.example.com host, create the / opt / ipsec-pubkey directory in which we place 2 scripts.

sudo vi /opt/ipsec-pubkey/pubkey-copy.sh

#!/bin/sh
if [ ! -f /home/keywatcher/ipsecgw.example.com.pem ]; then
  /usr/bin/openssl rsa -in /etc/letsencrypt/live/ipsecgw.example.com/privkey.pem -pubout > /home/keywatcher/ipsecgw.example.com.pem;
  /usr/bin/chown keywatcher:keywatcher /home/keywatcher/ipsecgw.example.com.pem;
  /usr/bin/chmod 0600 /home/keywatcher/ipsecgw.example.com.pem;
  sudo -u keywatcher /usr/bin/scp /home/keywatcher/ipsecgw.example.com.pem rkn.example.com:/home/keywatcher/ipsecgw.example.com.pem;
  status=$?;
  if [ $status -eq 0 ]; then
    rm -f /home/keywatcher/ipsecgw.example.com.pem;
    logger "Public key ipsecgw.example.com.pem has been successfully uploaded to remote host";
  else
    logger "Public key ipsecgw.example.com.pem has not been uploaded to remote host due to error";
  fi
  else
    logger "Public key ipsecgw.example.com.pem already exist on /home/keywatcher directory, something went wrong";
fi
exit 0

sudo vi /opt/ipsec-pubkey/key-updater.sh

#!/bin/sh
/usr/bin/cp /home/keywatcher/rkn.example.com.pem /etc/strongswan/ipsec.d/certs/rkn.example.com.pem
/usr/bin/chown root:root /etc/strongswan/ipsec.d/certs/rkn.example.com.pem
/usr/bin/chmod 0600 /etc/strongswan/ipsec.d/certs/rkn.example.com.pem
logger "Public key of server rkn.example.com has been updated, restarting strongswan daemon to re-read it"
/usr/bin/systemctl restart strongswan
exit 0

On VPS (the host rkn.example.com), we similarly create a directory with the same name, in which we also create similar scripts, changing only the name of the key. Code, so as not to clutter up the article, under

spoiler
sudo vi /opt/ipsec-pubkey/pubkey-copy.sh
#!/bin/sh
if [ ! -f /home/keywatcher/rkn.example.com.pem ]; then
  /usr/bin/openssl rsa -in /etc/letsencrypt/live/rkn.example.com/privkey.pem -pubout > /home/keywatcher/rkn.example.com.pem;
  /usr/bin/chown keywatcher:keywatcher /home/keywatcher/rkn.example.com.pem;
  /usr/bin/chmod 0600 /home/keywatcher/rkn.example.com.pem;
  sudo -u keywatcher /usr/bin/scp /home/keywatcher/rkn.example.com.pem ipsecgw.example.com:/home/keywatcher/rkn.example.com.pem;
  status=$?;
  if [ $status -eq 0 ]; then
    rm -f /home/keywatcher/rkn.example.com.pem;
    logger "Public key rkn.example.com.pem has been successfully uploaded to remote host";
  else
    logger "Public key rkn.example.com.pem has not been uploaded to remote host";
  fi
  else
    logger "Public key rkn.example.com.pem already exist on /home/keywatcher directory, something went wrong";
fi
exit 0

sudo vi /opt/ipsec-pubkey/key-updater.sh


#!/bin/bash
/usr/bin/cp /home/keywatcher/ipsecgw.example.com.pem /etc/strongswan/ipsec.d/certs/ipsecgw.example.com.pem;
/usr/bin/chown root:root /etc/strongswan/ipsec.d/certs/ipsecgw.example.com.pem
/usr/bin/chmod 0600 /etc/strongswan/ipsec.d/certs/ipsecgw.example.com.pem
logger "Public key of server ipsecgw.example.com has been updated, restarting connection"
/usr/bin/systemctl restart strongswan
exit 0


The pubkey-copy.sh script is needed to extract the public part of the key and copy it to the remote host when issuing a new certificate. To do this, in the / etc / letsencrypt / renewal-hooks / deploy directory on both servers, create another microscript:


#!/bin/sh
/opt/ipsec-pubkey/pubkey-copy.sh > /dev/null 2>&1
/usr/bin/systemctl restart strongswan
exit 0

Half of the problem is resolved, certificates are re-issued, public keys are extracted and copied between the servers, and the time has come for systemd with its path units.

On the ipsecgw.example.com server in the / etc / systemd / system directory, create the keyupdater.path file

[Unit]
Wants=strongswan.service
[Path]
PathChanged=/home/keywatcher/rkn.example.com.pem
[Install]
WantedBy=multi-user.target

Similarly on the VPS host:

[Unit]
Wants=strongswan.service
[Path]
PathChanged=/home/keywatcher/ipsecgw.example.com.pem
[Install]
WantedBy=multi-user.target

And finally, on each server we create a service associated with this unit, which will be launched when the condition (PathChanged) is fulfilled - changing the file and closing it after recording. We create the files /etc/systemd/system/keyupdater.service and write:

[Unit]
Description= Starts the IPSec key updating script
Documentation= man:systemd.service
[Service]
Type=oneshot
ExecStart=/opt/ipsec-pubkey/key-updater.sh
[Install]
WantedBy=multi-user.target

Do not forget to re-read the systemd configurations with sudo systemctl daemon-reload and assign autostart to path units via sudo systemctl enable keyupdater.path && sudo systemctl start keyupdater.

As soon as the remote host writes the file containing the public key to the keywatcher user home directory and the file descriptor is closed, systemd will automatically start the corresponding service, which will copy the key to the desired location and restart Strongswan. The tunnel will be installed using the correct public key of the second side.

You can exhale and enjoy the result.

Instead of a conclusion


As we just saw the hell IPSec is not as scary as it is painted. All that has been described is a fully operational configuration that is currently in use. Even without much knowledge, you can configure a VPN and reliably protect your data.

Of course, the moments of iptables setup remained outside the scope of the article, but the article itself turned out to be already voluminous, and much has been written about iptables.

There are points in the article that can be improved, be it refusal to restart the Strongswan daemon, re-reading its configs and certificates, but I could not achieve this.

However, the daemonā€™s restarts werenā€™t scary: one or two pings between peers are lost, mobile clients restore the connection themselves.

I hope colleagues in the comments will prompt the correct solution.

All Articles