johnwfinigan.github.io

IPsec VPN from Google Cloud to OpenBSD with OpenBGPD

Like most public clouds, Google Cloud offers a managed IPsec site-to-site VPN gateway to allow you to build hybrid networks.

This a quick guide to configuring OpenBSD as the on-premises IPsec tunnel provider and router, including use of the built-in OpenBSD iked for IPSec and built-in OpenBGPD to exchange routes with GCP. At the time of testing, I was using OpenBSD 7.7.

I am mainly trying to show how to do this with OpenBSD, and will not cover the GCP side beyond saying that you can use the Web UI wizard for creating the VPN tunnel, and if you want to use BGP for routing, you must deploy a HA VPN and not a Classic VPN.

This is more of a starting point than a production ready setup. I am not an expert in these topics, and I’m sure that things like pf rules and cipher choices should be tuned further. For simplicity, I took the Web UI option of creating a single tunnel, but production setups should probably use at least dual tunnels.

Once you create your tunnel (including its associated VPN Gateway and Cloud Router) in GCP, you will have some pieces of critical information available for the rest of the setup:

Item Value
Cloud VPN Gateway Public IP 198.51.100.5
Cloud Router BGP IP 169.254.149.89
Cloud Router BGP ASN 65516
BGP peer IP (on-prem) 169.254.149.90
BGP Peer ASN (on-prem) 65515

In addition you’ll have some information about your networks you’re routing between cloud and on-prem:

Item Value
Your on-prem public IP 203.0.113.7
Your on-prem local subnet 172.16.10.0/24
Your GCP subnet #1 10.0.8.0/24
Your GCP subnet #2 10.0.9.0/24

Note that GCP’s BGP session here is listening on an ipv4 link-local address. This is not a problem, but we will have to include it in the tunnel and also have an interface on OpenBSD for bgpd to bind to. Thus, our first OpenBSD config item, to create a gif virtual interface.

$ cat /etc/hostname.gif0
inet 169.254.149.90 255.255.255.252 169.254.149.89

You can run sh /etc/netstart to create or update this virtual interface, once /etc/hostname.gif0 exists.

Next, create /etc/iked.conf to set up the IPSec tunnel:


ikev2 "gcpvpn" active esp \
    from 169.254.149.90/32 to 169.254.149.89/32  \
    from 172.16.10.0/24 to 10.0.8.0/23 \
    local 203.0.113.7 peer 198.51.100.5 \
    ikesa \
      enc aes-256 auth hmac-sha2-256 \
      group modp2048 \
    srcid 203.0.113.7 \
    dstid 198.51.100.5 \
    psk "FAKE_FIXME"

Here you can see I have created security associations (tunnels) between both the BGP link local addresses, and the local and remote networks. I have summarized the two /24 cloud networks down to one /23.

You will also need some rules in pf.conf to pass ipsec and BGP traffic. You’ll have to adjust this based on your local pf config, but this is the core of it:


# Remember, this is just a snippet to add to 
# your existing pf.conf 
# It's not a complete pf.conf
# We assume int_if is already trusted
# with something like pass in quick on $int_if

int_net = "172.16.10.0/24"
gcp_net = "10.0.8.0/23"

# Begin ipsec and bgp rules
vpn_if = "enc0"
gcp_vpn_peer = "198.51.100.5"
pass in log quick on $vpn_if from $gcp_net to $int_net
bgp_peer_gcp = "169.254.149.89"
bgp_peer_local = "169.254.149.90"
# Allow incoming VPN traffic (IKE and ESP).
pass in quick on $ext_if proto esp from $gcp_vpn_peer to any
pass in quick on $ext_if proto udp from $gcp_vpn_peer to any port { 500, 4500 }
# Allow BGP traffic over the IPsec tunnel.
pass in quick on $ext_if proto tcp from $bgp_peer_gcp to $bgp_peer_local port 179
pass out quick on $ext_if proto tcp from $bgp_peer_local to $bgp_peer_gcp port 179
# End ipsec and bgp rules

It bears repeating that these rules are being added to a setup where the OpenBSD router’s internal LAN interfaces already have a pf rule that allows traffic inbound from them. If not, you will need a new rule which allows traffic from int_net to gcp_net, just like its inverse which is above.

Once these rules are in place, you can run rcctl enable iked and rcctl start iked and you should then have active security associations and your VPN gateway in GCP should “go green"


# ikectl show sa                                                                                                                                                                                          
iked_sas: 0xc8e0977720 rspi 0x89919a0ae17b9893 ispi 0xb502da797c203a8e 203.0.113.7:500->198.51.100.5:500<IPV4/198.51.100.5>[] ESTABLISHED i nexti 0x0 pol 0xc8fafbb000
  sa_childsas: 0xc8e098f480 ESP 0x3f52449e out 203.0.113.7:500 -> 198.51.100.5:500 (L) B=0x0 P=0xc8e096fa80 @0xc8e0977720
  sa_childsas: 0xc8e096fa80 ESP 0x9bbfc7dd in 198.51.100.5:500 -> 203.0.113.7:500 (LA) B=0x0 P=0xc8e098f480 @0xc8e0977720
  sa_flows: 0xc8e0957400 ESP out 172.16.10.0/24 -> 10.0.8.0/23 [0]@-1 (L) @0xc8e0977720
  sa_flows: 0xc8e0985800 ESP in 10.0.8.0/23 -> 172.16.10.0/24 [0]@-1 (L) @0xc8e0977720
  sa_flows: 0xc8e0965c00 ESP out 169.254.149.90/32 -> 169.254.149.89/32 [0]@-1 (L) @0xc8e0977720
  sa_flows: 0xc8e0965800 ESP in 169.254.149.89/32 -> 169.254.149.90/32 [0]@-1 (L) @0xc8e0977720
iked_activesas: 0xc8e098f480 ESP 0x3f52449e out 203.0.113.7:500 -> 198.51.100.5:500 (L) B=0x0 P=0xc8e096fa80 @0xc8e0977720
iked_activesas: 0xc8e096fa80 ESP 0x9bbfc7dd in 198.51.100.5:500 -> 203.0.113.7:500 (LA) B=0x0 P=0xc8e098f480 @0xc8e0977720
iked_flows: 0xc8e0985800 ESP in 10.0.8.0/23 -> 172.16.10.0/24 [0]@-1 (L) @0xc8e0977720
iked_flows: 0xc8e0965800 ESP in 169.254.149.89/32 -> 169.254.149.90/32 [0]@-1 (L) @0xc8e0977720
iked_flows: 0xc8e0957400 ESP out 172.16.10.0/24 -> 10.0.8.0/23 [0]@-1 (L) @0xc8e0977720
iked_flows: 0xc8e0965c00 ESP out 169.254.149.90/32 -> 169.254.149.89/32 [0]@-1 (L) @0xc8e0977720
iked_dstid_sas: 0xc8e0977720 rspi 0x89919a0ae17b9893 ispi 0xb502da797c203a8e 203.0.113.7:500->198.51.100.5:500<IPV4/198.51.100.5>[] ESTABLISHED i nexti 0x0 pol 0xc8fafbb000

Note that sa_flows exist for all our desired connectivity. Now that the tunnel is up, you can configure bgpd.conf:


AS 65515
router-id 169.254.149.90

network 172.16.10.0/24

neighbor 169.254.149.89 {
    remote-as 65516
    descr "GCP VPN Gateway"
}

allow to 169.254.149.89 prefix { 172.16.10.0/24 }
allow from 169.254.149.89 prefix { 10.0.8.0/24, 10.0.9.0/24 }

Note that we are filtering routes inbound and outbound, but can’t summarize the 10. prefix because that is not what GCP is sending.

You can now run rcctl enable bgpd followed by rcctl start bgpd and after a quick wait, should see something like this:


r# bgpctl show rib                                                                                                                                                                                         
flags: * = Valid, > = Selected, I = via IBGP, A = Announced,
       S = Stale, E = Error, F = Filtered, L = Leaked
origin validation state: N = not-found, V = valid, ! = invalid
aspa validation state: ? = unknown, V = valid, ! = invalid
origin: i = IGP, e = EGP, ? = Incomplete

flags  vs destination          gateway          lpref   med aspath origin
*>    N-? 10.0.8.0/24          169.254.149.89    100   100 65516 ?
*>    N-? 10.0.9.0/24          169.254.149.89    100   100 65516 ?
AI*>  N-? 172.16.10.0/24       0.0.0.0           100     0 i

Importantly, our inbound routes on the 10. prefixes are showing *> which means “Valid and Selected”. If you run netstat -rn you should see them in your OpenBSD router’s system routes.

At this point you should be able to ping hosts bidirectionally. On the OpenBSD router, you will probably need to use something like ping -S 172.16.10.1 10.0.9.2 to ensure the ping originates from an interface that can route to the VPN.

For troubleshooting you can employ tcpdump -i enc0 to see what is going over the tunnel, and similarly for gif0