Connecting CloudStack VPC to an on premises or remote pfSense firewall via a site to site IPsec VPN allows secure communication between cloud virtual machines and local infrastructure.
Under the hood, CloudStack Virtual Routers use strongSwan (specifically the charon IKE daemon) to establish and maintain IPsec site to site VPN tunnels.
In this post, we will walk through setting up a site to site IPsec VPN tunnel between a CloudStack VPC and a pfSense firewall using IKEv2 and mutual pre-shared keys, as well as troubleshooting IPsec tunnels on the CloudStack Virtual Router.
Prerequisites
- A pfSense firewall with a dedicated public IP address.
- CloudStack Virtual Private Cloud (VPC) with at least one tier configured.
CloudStack Configuration
- Create the Site to Site VPN Gateway Navigate to your VPC and select the VPN Gateway tab, then click Create site-to-site VPN gateway. This automatically provisions a VPN gateway using the source NAT IP of the VPC.
Alternatively, go to Network > Site-to-site VPN, click Add VPN gateway, and select your VPC.
- Create the Customer Gateway Go to Network > VPN Customer Gateway and click Add VPN Customer Gateway. Configure the parameters to match your pfSense firewall:
- Name: Enter a descriptive name for the connection
- Gateway: The public IP address of your pfSense firewall
- CIDR List: The local networks behind pfSense (comma-separated for multiple subnets, e.g.,
192.168.1.0/24,192.168.10.0/24) - IPsec Pre-Shared Key: Generate and set a secure pre-shared key
- IKE Encryption:
aes128 - IKE Hash:
sha256 - IKE Version:
ikev2 - IKE DH:
Group 14 (modp2048) - ESP Encryption:
aes128 - ESP Hash:
sha256 - Perfect Forward Secrecy:
Group 14 (modp2048) - IKE Lifetime:
86400 - ESP Lifetime:
3600 - Dead Peer Detection:
Enabled
- Create the VPN Connection Under VPC > VPN Connection, click Create site-to-site VPN connection. Select the VPN customer gateway created above and confirm.
pfSense Configuration
- Phase 1 Configuration On your pfSense firewall, navigate to VPN > IPsec > Tunnels and click Add P1:
- Description: Enter a descriptive name
- Key Exchange Version:
IKEv2 - Interface:
WAN - Remote Gateway: The VPN Gateway IP / Source NAT IP of your CloudStack VPC
- Authentication Method:
Mutual PSK - My Identifier:
IP address - Peer Identifier:
IP address - Pre-Shared Key: Enter the pre-shared key generated in CloudStack
- Encryption Algorithm:
AES128-bit, Hash:SHA256, DH Group:14 (2048 bit) - Lifetime:
86400seconds
Click Save and Apply Changes.
- Phase 2 Configuration Click Show Phase 2 Entries under the P1 entry, then click Add P2:
- Mode:
Tunnel IPv4 - Local Network: Select
LAN Subnet(or local on-prem network CIDR) - Remote Network: Select
Networkand enter your CloudStack VPC tier subnet CIDR - Encryption Algorithm:
AES128-bit - Hash Algorithm:
SHA256 - PFS Key Group:
14 - Lifetime:
3600seconds
If you need to access multiple subnets or tiers within your CloudStack VPC, add an additional Phase 2 entry for each remote subnet.
Click Save and Apply Changes.
Connecting and Verifying Status
- Navigate to Status > IPsec on pfSense.
- Click Connect P1 and Connect P2.
- Verify that the tunnel status changes to connected.
If the tunnel fails to negotiate initially, reset the VPN connection from the CloudStack UI and click Connect P1 / P2 again on pfSense.
Troubleshooting IPsec Tunnels on CloudStack
If a tunnel fails to negotiate or drops, you can log directly into the CloudStack Virtual Router (VR) to inspect strongSwan logs and state.
Accessing the Virtual Router via SSH
From a hypervisor host:
ssh -i /root/.ssh/id_rsa.cloud [email protected] -p 3922
To SSH into the VR from your local machine using the hypervisor host as a jump host:
# Copy the private key from the hypervisor
scp [email protected]:/root/.ssh/id_rsa.cloud ~/.ssh/id_rsa.cloud
ssh -J [email protected] \
-i ~/.ssh/id_rsa.cloud \
-p 3922 [email protected]
Monitoring strongSwan Logs
To watch real-time log output from the strongSwan service:
journalctl -fu strongswan-starter.service
Or view all charon/IPsec log events:
journalctl -f | grep -E "charon|ipsec|ipsec_starter"
Avoid running
ipsec restartdirectly while debugging active production routers, as it clears in-memory logs. If you modify configuration files, reload them cleanly instead.
Inspecting Tunnel Status
To check the status of a specific VPN tunnel configuration:
ipsec statusall vpn-203.0.113.10
Key indicators in ipsec statusall output:
ESTABLISHED: Phase 1 (IKE SA) is active and authenticated.INSTALLED: Phase 2 (ESP SA) is active and handling encrypted traffic. MissingINSTALLEDindicates a Phase 2 proposal mismatch.- Traffic Counters: Displays
bytes_i/bytes_oto confirm bidirectionally routed packets.
Example status output:
Status of IKE charon daemon (strongSwan 5.9.8, Linux 6.1.0-37-amd64, x86_64):
uptime: 29 minutes, since Dec 29 19:47:25 2025
worker threads: 11 of 16 idle, 5/0/0/0 working, job queue: 0/0/0/0
Listening IP addresses:
198.51.100.10
192.168.1.1
Connections:
vpn-203.0.113.10: 198.51.100.10...203.0.113.10 IKEv2, dpddelay=30s
vpn-203.0.113.10: local: [198.51.100.10] uses pre-shared key authentication
vpn-203.0.113.10: remote: [203.0.113.10] uses pre-shared key authentication
vpn-203.0.113.10: child: 10.1.0.0/16 === 192.168.1.0/24 TUNNEL, dpdaction=start
Routed Connections:
vpn-203.0.113.10{60}: ROUTED, TUNNEL, reqid 7
vpn-203.0.113.10{60}: 10.1.0.0/16 === 192.168.1.0/24
Security Associations (1 up, 0 connecting):
vpn-203.0.113.10[3]: ESTABLISHED 29 minutes ago, 198.51.100.10[198.51.100.10]...203.0.113.10[203.0.113.10]
vpn-203.0.113.10[3]: IKEv2 SPIs: df374d9868c42534_i fb456e13039527d3_r*, pre-shared key reauthentication in 23 hours
vpn-203.0.113.10[3]: IKE proposal: AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048
vpn-203.0.113.10{8}: INSTALLED, TUNNEL, reqid 6, ESP SPIs: c5d5b4e4_i c25bbf95_o
vpn-203.0.113.10{8}: AES_CBC_128/HMAC_SHA2_256_128, 420256 bytes_i (4285 pkts, 0s ago), 725372 bytes_o (3776 pkts, 0s ago), rekeying in 17 minutes
vpn-203.0.113.10{8}: 10.1.0.0/16 === 192.168.1.0/24
Safely Reloading IPsec Configurations
Configuration files reside in /etc/ipsec.d/. Instead of restarting the whole daemon, update configuration and reset only the specific tunnel connection:
# Apply manual configuration changes in /etc/ipsec.d/
ipsec update
# Restart individual connection
ipsec down vpn-203.0.113.10
ipsec up vpn-203.0.113.10
# Verify active status
ipsec statusall vpn-203.0.113.10
Packet Captures (tcpdump)
To verify traffic flow at different network layers on the Virtual Router:
Unencrypted local traffic prior to IPsec encryption:
tcpdump -ni any net 10.1.0.0/16 and net 192.168.1.0/24
IKE traffic (UDP port 4500):
tcpdump -ni eth1 host 203.0.113.10 and udp port 4500
Encrypted ESP traffic (IP Protocol 50):
tcpdump -ni eth1 host 203.0.113.10 and proto 50
Subnet Reset Workaround
If CloudStack periodically resets leftsubnet in /etc/ipsec.d/ back to the broader VPC CIDR instead of a specific host subnet, you can enforce the desired CIDR using a crontab entry:
crontab -e
*/5 * * * * sed -i 's|leftsubnet=10\.1\.0\.0/16|leftsubnet=10.1.5.20/32|g' /etc/ipsec.d/ipsec.vpn-203.0.113.10.conf