Tools: Self-Hosted IPsec VPN: PKI Setup, StrongSwan Config, and Client Provisioning End-to-End (2026)

Tools: Self-Hosted IPsec VPN: PKI Setup, StrongSwan Config, and Client Provisioning End-to-End (2026)

The PKI: generating your certificate hierarchy

StrongSwan server configuration

Platform-specific client configuration

Certificate revocation A self-hosted VPN server gives you full control over authentication, logging, and certificate issuance. Here is what the complete technical stack looks like — from PKI setup through to client connection. A minimal PKI for IPsec VPN needs three certificate types: Keep root-ca.key offline — it is only needed to sign new certificates. iOS / macOS (native IKEv2): Package root CA + client cert + client key into a .mobileconfig profile. iOS imports it via Settings → Profile Downloaded. Windows (native IKEv2): Android (StrongSwan app): Import .p12 bundle (client cert + key + root CA), configure IKEv2 Certificate auth. Linux (NetworkManager): When a device is lost or compromised, revoke its client certificate: Distribute the updated CRL to the server. StrongSwan checks CRL on connection if configured: CacheGuard automates this entire workflow — root CA generation, certificate signing, and client profile creation — through its web interface, and handles DynDNS updates when the server's public IP is dynamic. → https://www.cacheguard.com/self-hosted-vpn-server/ Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Code Block

Copy

Root CA (self-signed, 10yr validity) ├── Server certificate (signed by Root CA, for VPN server) └── Client certificates (signed by Root CA, one per device/user) Root CA (self-signed, 10yr validity) ├── Server certificate (signed by Root CA, for VPN server) └── Client certificates (signed by Root CA, one per device/user) Root CA (self-signed, 10yr validity) ├── Server certificate (signed by Root CA, for VPN server) └── Client certificates (signed by Root CA, one per device/user) # Generate root CA key and self-signed certificate openssl genrsa -out root-ca.key 4096 openssl req -x509 -new -key root-ca.key -sha256 -days 3650 \ -out root-ca.crt -subj "/CN=VPN Root CA/O=MyOrg" # Generate server key and CSR openssl genrsa -out server.key 2048 openssl req -new -key server.key \ -out server.csr -subj "/CN=vpn.example.com/O=MyOrg" # Sign server certificate with root CA openssl x509 -req -in server.csr -CA root-ca.crt -CAkey root-ca.key \ -CAcreateserial -out server.crt -days 825 -sha256 \ -extfile <(echo "subjectAltName=DNS:vpn.example.com") # Generate client certificate (repeat per device) openssl genrsa -out client-device1.key 2048 openssl req -new -key client-device1.key \ -out client-device1.csr -subj "/CN=device1/O=MyOrg" openssl x509 -req -in client-device1.csr -CA root-ca.crt -CAkey root-ca.key \ -CAcreateserial -out client-device1.crt -days 825 -sha256 # Generate root CA key and self-signed certificate openssl genrsa -out root-ca.key 4096 openssl req -x509 -new -key root-ca.key -sha256 -days 3650 \ -out root-ca.crt -subj "/CN=VPN Root CA/O=MyOrg" # Generate server key and CSR openssl genrsa -out server.key 2048 openssl req -new -key server.key \ -out server.csr -subj "/CN=vpn.example.com/O=MyOrg" # Sign server certificate with root CA openssl x509 -req -in server.csr -CA root-ca.crt -CAkey root-ca.key \ -CAcreateserial -out server.crt -days 825 -sha256 \ -extfile <(echo "subjectAltName=DNS:vpn.example.com") # Generate client certificate (repeat per device) openssl genrsa -out client-device1.key 2048 openssl req -new -key client-device1.key \ -out client-device1.csr -subj "/CN=device1/O=MyOrg" openssl x509 -req -in client-device1.csr -CA root-ca.crt -CAkey root-ca.key \ -CAcreateserial -out client-device1.crt -days 825 -sha256 # Generate root CA key and self-signed certificate openssl genrsa -out root-ca.key 4096 openssl req -x509 -new -key root-ca.key -sha256 -days 3650 \ -out root-ca.crt -subj "/CN=VPN Root CA/O=MyOrg" # Generate server key and CSR openssl genrsa -out server.key 2048 openssl req -new -key server.key \ -out server.csr -subj "/CN=vpn.example.com/O=MyOrg" # Sign server certificate with root CA openssl x509 -req -in server.csr -CA root-ca.crt -CAkey root-ca.key \ -CAcreateserial -out server.crt -days 825 -sha256 \ -extfile <(echo "subjectAltName=DNS:vpn.example.com") # Generate client certificate (repeat per device) openssl genrsa -out client-device1.key 2048 openssl req -new -key client-device1.key \ -out client-device1.csr -subj "/CN=device1/O=MyOrg" openssl x509 -req -in client-device1.csr -CA root-ca.crt -CAkey root-ca.key \ -CAcreateserial -out client-device1.crt -days 825 -sha256 # /etc/ipsec.conf config setup charondebug="ike 1, knl 1, cfg 0" conn ikev2-vpn auto=add compress=no type=tunnel keyexchange=ikev2 fragmentation=yes forceencaps=yes # Always use UDP/4500 (better NAT compatibility) # Server side left=%any [email protected] leftcert=server.crt leftsendcert=always leftsubnet=0.0.0.0/0 # Route all client traffic through VPN # Client side right=%any rightid=%any rightauth=pubkey rightsourceip=10.8.0.0/24 # Virtual IP pool rightdns=10.8.0.1 ike=aes256-sha256-ecp256,aes256-sha1-modp2048! esp=aes256-sha256,aes256-sha1! dpdaction=clear dpddelay=300s rekey=no # /etc/ipsec.conf config setup charondebug="ike 1, knl 1, cfg 0" conn ikev2-vpn auto=add compress=no type=tunnel keyexchange=ikev2 fragmentation=yes forceencaps=yes # Always use UDP/4500 (better NAT compatibility) # Server side left=%any [email protected] leftcert=server.crt leftsendcert=always leftsubnet=0.0.0.0/0 # Route all client traffic through VPN # Client side right=%any rightid=%any rightauth=pubkey rightsourceip=10.8.0.0/24 # Virtual IP pool rightdns=10.8.0.1 ike=aes256-sha256-ecp256,aes256-sha1-modp2048! esp=aes256-sha256,aes256-sha1! dpdaction=clear dpddelay=300s rekey=no # /etc/ipsec.conf config setup charondebug="ike 1, knl 1, cfg 0" conn ikev2-vpn auto=add compress=no type=tunnel keyexchange=ikev2 fragmentation=yes forceencaps=yes # Always use UDP/4500 (better NAT compatibility) # Server side left=%any [email protected] leftcert=server.crt leftsendcert=always leftsubnet=0.0.0.0/0 # Route all client traffic through VPN # Client side right=%any rightid=%any rightauth=pubkey rightsourceip=10.8.0.0/24 # Virtual IP pool rightdns=10.8.0.1 ike=aes256-sha256-ecp256,aes256-sha1-modp2048! esp=aes256-sha256,aes256-sha1! dpdaction=clear dpddelay=300s rekey=no # /etc/ipsec.secrets — certificate-based auth needs no shared secrets : RSA server.key # /etc/ipsec.secrets — certificate-based auth needs no shared secrets : RSA server.key # /etc/ipsec.secrets — certificate-based auth needs no shared secrets : RSA server.key # Import root CA to machine trust store Import-Certificate -FilePath root-ca.crt -CertStoreLocation Cert:\LocalMachine\Root # Import client cert to personal store Import-PfxCertificate -FilePath client-device1.pfx -CertStoreLocation Cert:\CurrentUser\My # Add VPN connection Add-VpnConnection -Name "OrgVPN" -ServerAddress "vpn.example.com" ` -TunnelType IKEv2 -AuthenticationMethod MachineCertificate ` -EncryptionLevel Required -RememberCredential $false # Import root CA to machine trust store Import-Certificate -FilePath root-ca.crt -CertStoreLocation Cert:\LocalMachine\Root # Import client cert to personal store Import-PfxCertificate -FilePath client-device1.pfx -CertStoreLocation Cert:\CurrentUser\My # Add VPN connection Add-VpnConnection -Name "OrgVPN" -ServerAddress "vpn.example.com" ` -TunnelType IKEv2 -AuthenticationMethod MachineCertificate ` -EncryptionLevel Required -RememberCredential $false # Import root CA to machine trust store Import-Certificate -FilePath root-ca.crt -CertStoreLocation Cert:\LocalMachine\Root # Import client cert to personal store Import-PfxCertificate -FilePath client-device1.pfx -CertStoreLocation Cert:\CurrentUser\My # Add VPN connection Add-VpnConnection -Name "OrgVPN" -ServerAddress "vpn.example.com" ` -TunnelType IKEv2 -AuthenticationMethod MachineCertificate ` -EncryptionLevel Required -RememberCredential $false nmcli connection add type vpn vpn-type libreswan \ con-name "OrgVPN" vpn.data \ "right=vpn.example.com,[email protected],\ leftcert=client-device1.crt,leftkey=client-device1.key,\ rightca=root-ca.crt,ikev2=insist" nmcli connection add type vpn vpn-type libreswan \ con-name "OrgVPN" vpn.data \ "right=vpn.example.com,[email protected],\ leftcert=client-device1.crt,leftkey=client-device1.key,\ rightca=root-ca.crt,ikev2=insist" nmcli connection add type vpn vpn-type libreswan \ con-name "OrgVPN" vpn.data \ "right=vpn.example.com,[email protected],\ leftcert=client-device1.crt,leftkey=client-device1.key,\ rightca=root-ca.crt,ikev2=insist" openssl ca -revoke client-device1.crt -config openssl.cnf openssl ca -gencrl -out crl.pem -config openssl.cnf openssl ca -revoke client-device1.crt -config openssl.cnf openssl ca -gencrl -out crl.pem -config openssl.cnf openssl ca -revoke client-device1.crt -config openssl.cnf openssl ca -gencrl -out crl.pem -config openssl.cnf # strongswan.conf charon { crl_strict = yes } # strongswan.conf charon { crl_strict = yes } # strongswan.conf charon { crl_strict = yes }