Aller au contenu principal

VPN IPsec IKEv2 host-à-site : strongSwan ↔ Cisco IOS

Retour d'expérience complet (config + dépannage réel) sur un VPN d'accès distant (road warrior) authentifié par certificats X.509, entre un client Linux (strongSwan) et un routeur Cisco ISR faisant office de concentrateur VPN. Pour la partie client générique (nmcli / strongSwan), voir VPN avec nmcli.

:::note Sur les exemples Les valeurs sensibles (IP publique, nom de domaine, identités de certificat) sont remplacées par des marqueurs entre chevrons, par exemple <PUBLIC_IP>. Remplace-les par tes propres valeurs. :::

Objectif

Permettre à un laptop Debian (client nomade / road warrior) de se connecter en VPN à un réseau domestique et d'y accéder comme s'il était physiquement présent sur le LAN 192.168.1.0/24, via un routeur Cisco ISR de la gamme 800 servant de concentrateur VPN.

Authentification par certificats X.509 (pas de PSK), protocole IKEv2.

:::info État du projet Le tunnel fonctionne parfaitement en test local (client et Cisco sur le même LAN) : négociation IKEv2, authentification par certificats, établissement CHILD_SA, tout est OK.

L'accès depuis l'extérieur (4G/5G) est bloqué par la box opérateur : les paquets IKE arrivent bien au Cisco et les réponses en repartent physiquement (compteurs output confirmés), mais le NAT retour de la box ne les réachemine pas jusqu'au client. Cause probable : ALG/helper IPsec non désactivable de la box, ou gestion NAT défaillante du trafic UDP 500/4500. Voir la section « Blocage résiduel » plus bas. :::


Architecture

Internet

IP publique <PUBLIC_IP>

┌───────┴────────┐
│ Box opérateur │ LAN 192.168.1.0/24, DHCP .1–.199
│ mode routeur │ Port forwarding UDP 500/4500 → .250
└───────┬────────┘

Switch (non managé, L2 pur)

┌───────┴────────────────────┐
│ Cisco ISR (FastEth4) │ WAN : 192.168.1.250/24
│ Concentrateur VPN IKEv2 │ Mode "one-armed" + proxy-ARP
│ Pool client : .240–.249 │ (une seule patte réseau active)
└─────────────────────────────┘

Client nomade (Debian + NetworkManager/strongSwan)
→ reçoit une IP virtuelle dans 192.168.1.0/24 une fois le tunnel monté

Choix d'architecture : le mode « one-armed » + proxy-ARP

L'exigence clé était que le client VPN soit dans le même sous-réseau 192.168.1.0/24 que le LAN cible, sans créer de sous-réseau séparé.

  • Le Cisco n'a qu'une seule interface active (FastEthernet4), branchée sur le switch qui remonte à la box opérateur.
  • Le client reçoit une IP du pool 192.168.1.240-249 (dans la plage LAN, hors bail DHCP box).
  • ip proxy-arp + ip unnumbered FastEthernet4 sur la Virtual-Template font que le client distant apparaît comme un hôte normal du LAN — le Cisco répond aux requêtes ARP à sa place, donc aucune route supplémentaire n'est nécessaire sur la box ou les autres machines.
  • Aucun NAT n'est requis pour cet usage (client et cible dans le même /24).

:::note Contrainte réseau importante Le LAN interne du Cisco et le LAN de la box sont le même 192.168.1.0/24. C'est possible ici parce que le Cisco est en mode one-armed (il ne route pas entre deux réseaux distincts). En topologie routée classique, il aurait fallu deux sous-réseaux différents. :::


Prérequis matériel / logiciel

ÉlémentDétail
RouteurCisco ISR 800, IOS 15.4(2)T1 (C800-UNIVERSALK9-M)
ClientDebian 13 (Trixie), network-manager-strongswan, strongSwan 6.0.1
PKIGénérée avec OpenSSL (voir note sur pki plus bas)
Accès CiscoConsole série puis SSH

Étape 1 — Accès et préparation du Cisco

Constat de départ : les ports FastEthernet0-3 sont du switch L2

Sur ce modèle, FastEthernet0 à 3 sont des ports switch (commutateur interne, VLAN 1). On ne peut pas leur assigner d'IP directement (% IP addresses may not be configured on L2 links). L'IP LAN se met sur l'interface Vlan1. FastEthernet4 est l'interface routée (WAN).

Activer SSH (copier-coller fiable, indispensable pour les certificats)

hostname VPN
ip domain-name <DOMAINE_LOCAL>
crypto key generate rsa modulus 2048
username admin privilege 15 secret <MotDePasseSolide>
ip ssh version 2
ip ssh time-out 60
ip ssh authentication-retries 3
line vty 0 4
transport input ssh
login local
exec-timeout 30 0

:::tip Connexion SSH depuis un client moderne L'IOS ancien ne propose que des algos SSH obsolètes (KEX SHA1, clés ssh-rsa). OpenSSH récent les refuse par défaut. Forcer côté client :

ssh -oKexAlgorithms=+diffie-hellman-group14-sha1 \
-oHostKeyAlgorithms=+ssh-rsa \
-oPubkeyAcceptedAlgorithms=+ssh-rsa admin@192.168.1.250

Ou dans ~/.ssh/config :

Host cisco-vpn
HostName 192.168.1.250
User admin
KexAlgorithms +diffie-hellman-group14-sha1
HostKeyAlgorithms +ssh-rsa
PubkeyAcceptedAlgorithms +ssh-rsa

:::


Étape 2 — PKI (autorité de certification et certificats)

Note importante : pki (strongSwan) vs openssl

Sur Debian 13, la commande pki de strongswan-pki échoue à charger ses plugins (aes, sha2, gmpfailed to load). Plutôt que de débuguer les chemins de plugins, on utilise OpenSSL directement — le résultat est strictement équivalent (certificats X.509 standards).

Génération complète (sur le laptop Debian)

mkdir -p ~/pki/{cacerts,certs,private,reqs}
cd ~/pki

# --- CA (avec CA:TRUE explicite, sinon IOS rejette la chaîne) ---
openssl genrsa -out private/caKey.pem 4096
openssl req -x509 -new -key private/caKey.pem -sha256 -days 3650 \
-out cacerts/caCert.pem \
-subj "/C=FR/O=HOMELAB/CN=HOMELAB CA" \
-addext "basicConstraints=critical,CA:TRUE" \
-addext "keyUsage=critical,keyCertSign,cRLSign"

# --- Certificat client (laptop) ---
openssl genrsa -out private/laptopKey.pem 3072
openssl req -new -key private/laptopKey.pem -out reqs/laptopReq.pem \
-subj "/C=FR/O=HOMELAB/CN=<CLIENT_IDENTITY>"
openssl x509 -req -in reqs/laptopReq.pem \
-CA cacerts/caCert.pem -CAkey private/caKey.pem -CAcreateserial \
-out certs/laptopCert.pem -days 1825 -sha256 \
-extfile <(printf "subjectAltName=DNS:<CLIENT_IDENTITY>\nextendedKeyUsage=clientAuth")

# --- Certificat serveur (Cisco) : IP en SAN OBLIGATOIRE (voir note) ---
openssl genrsa -out private/ciscoKey.pem 2048
openssl req -new -key private/ciscoKey.pem -out reqs/ciscoReq.pem \
-subj "/C=FR/O=HOMELAB/CN=<VPN_GATEWAY_IDENTITY>"
openssl x509 -req -in reqs/ciscoReq.pem \
-CA cacerts/caCert.pem -CAkey private/caKey.pem -CAcreateserial \
-out certs/ciscoCert.pem -days 1825 -sha256 \
-extfile <(printf "subjectAltName=DNS:<VPN_GATEWAY_IDENTITY>,IP:192.168.1.250,IP:<PUBLIC_IP>\nextendedKeyUsage=serverAuth")

# --- Bundle PKCS12 pour import Cisco (option -legacy OBLIGATOIRE) ---
openssl pkcs12 -export -legacy \
-in certs/ciscoCert.pem -inkey private/ciscoKey.pem \
-certfile cacerts/caCert.pem \
-out ciscoBundle.p12 -name "cisco-vpn" \
-certpbe PBE-SHA1-3DES -keypbe PBE-SHA1-3DES -macalg sha1

:::warning Trois pièges PKI rencontrés (et résolus)

  1. CA:TRUE manquant → IOS rejette la validation de chaîne. Toujours ajouter -addext "basicConstraints=critical,CA:TRUE" sur la CA.
  2. PKCS12 chiffré en AES-256/SHA256 (défaut OpenSSL 3.x) → IOS 15.4 ne sait pas le lire (E_DIGEST_ALG_NOT_SUPPORTED). Il faut -legacy + PBE-SHA1-3DES + -macalg sha1.
  3. IP absente du SAN du certif Cisco → le client strongSwan rejette avec certificate does not confirm identity '192.168.1.250'. Mettre toutes les IP de connexion (locale ET publique) en IP: dans le SAN. :::

Étape 3 — Import des certificats sur le Cisco

Transfert du bundle (TFTP — SCP peut échouer selon l'image IOS)

# Sur le laptop
sudo apt install tftpd-hpa
sudo cp ~/pki/ciscoBundle.p12 /srv/tftp/
! Sur le Cisco
copy tftp: flash:
! IP du laptop : 192.168.1.66, fichier : ciscoBundle.p12

Import PKCS12 (crée le trustpoint, la CA, le certif et la clé en une fois)

crypto pki import HOMELAB-CA pkcs12 flash:ciscoBundle.p12 password <MotDePasseP12>

:::danger revocation-check none — le piège n°1 de tout le projet L'import PKCS12 recrée le trustpoint avec la vérification de révocation activée par défaut. Comme le certif n'a pas de CDP/CRL, IOS tente une vérification CRL, échoue, et rejette toute l'authentification (AUTHENTICATION_FAILED, alors que la chaîne est pourtant valide — visible via debug crypto pki validation). Il faut réappliquer :

crypto pki trustpoint HOMELAB-CA
revocation-check none

:::

Vérification

show crypto pki certificates

Doit lister le certif d'identité et le certif CA. (NB : la date de validité de la CA peut s'afficher « Aug 5 1900 » — bug cosmétique d'affichage ASN.1 sur vieil IOS, sans impact fonctionnel avec revocation-check none.)


Étape 4 — Configuration IKEv2 / IPsec (Cisco)

! --- Proposal & policy IKEv2 ---
! group 19 EN PREMIER : strongSwan propose ECP_256 (grp 19) spontanément.
! Sans lui, IOS renvoie INVALID_KE_PAYLOAD et force un 2e paquet (fragile en NAT).
crypto ikev2 proposal PROP1
encryption aes-cbc-256
integrity sha256
group 19 14

crypto ikev2 policy POL1
proposal PROP1

! --- Pool d'IP virtuelles pour le client (dans le LAN, hors DHCP box) ---
ip local pool VPN-POOL 192.168.1.240 192.168.1.249

! --- Politique d'autorisation ---
crypto ikev2 authorization policy AUTH-POLICY
pool VPN-POOL
route set interface

! --- AAA (requis pour l'autorisation par certificat) ---
aaa new-model
aaa authentication login default local
aaa authorization exec default local
aaa authorization network default local

! --- Matching par certificat (plus robuste que match identity fqdn) ---
crypto pki certificate map CERT-MAP 10
issuer-name co homelab ca

! --- Profil IKEv2 ---
crypto ikev2 profile PROFILE-ROADWARRIOR
match certificate CERT-MAP
identity local fqdn <VPN_GATEWAY_IDENTITY>
authentication local rsa-sig
authentication remote rsa-sig
pki trustpoint HOMELAB-CA
aaa authorization group cert list default AUTH-POLICY
virtual-template 1

! --- Transform-set & profil IPsec ---
crypto ipsec transform-set TSET esp-aes 256 esp-sha256-hmac
mode tunnel

crypto ipsec profile IPSEC-PROF
set transform-set TSET
set ikev2-profile PROFILE-ROADWARRIOR

! --- Interface virtuelle du tunnel (one-armed) ---
interface Virtual-Template1 type tunnel
ip unnumbered FastEthernet4
tunnel mode ipsec ipv4
tunnel protection ipsec profile IPSEC-PROF

! --- Interface WAN : proxy-arp pour le mode one-armed ---
interface FastEthernet4
ip address 192.168.1.250 255.255.255.0
ip proxy-arp
no shutdown

! --- (optionnel) fragmentation IKEv2 pour gros paquets AUTH ---
crypto ikev2 fragmentation

end
write memory

:::tip Pourquoi match certificate CERT-MAP plutôt que match identity remote fqdn strongSwan envoie son identité sous forme de DN complet (cn=<CLIENT_IDENTITY>,o=...), pas de FQDN. Un match identity remote fqdn domain <DOMAINE_LOCAL> échoue donc. Matcher sur l'émetteur du certificat (issuer-name co homelab ca) accepte tout appareil signé par la CA, quel que soit le format d'identité — plus simple et extensible. :::


Étape 5 — Configuration du client (Debian / NetworkManager)

Placement des certificats

mkdir -p ~/.cert/vpn-homelab
cp ~/pki/cacerts/caCert.pem ~/pki/certs/laptopCert.pem ~/pki/private/laptopKey.pem ~/.cert/vpn-homelab/
chmod 600 ~/.cert/vpn-homelab/laptopKey.pem

Création du profil (nmcli — crée un profil isolé, sans toucher aux VPN existants)

nmcli connection add type vpn vpn-type strongswan con-name "VPN-Homelab-Cisco" \
vpn.data "address=<PUBLIC_IP>, \
certificate=/home/<user>/.cert/vpn-homelab/caCert.pem, \
usercert=/home/<user>/.cert/vpn-homelab/laptopCert.pem, \
userkey=/home/<user>/.cert/vpn-homelab/laptopKey.pem, \
local-identifier=<CLIENT_IDENTITY>, \
remote-identifier=<VPN_GATEWAY_IDENTITY>, \
method=key, \
ike=aes256-sha256-modp2048, \
esp=aes256-sha256"

:::note Modifier l'adresse plus tard vpn.data se réécrit en entier (pas de modif d'une seule sous-clé). Pour changer l'IP de passerelle, renvoyer tout le bloc avec nmcli connection modify ... vpn.data "...". Utiliser 192.168.1.250 pour tester en LAN, l'IP publique pour tester en externe. :::

Montée / descente du tunnel

nmcli connection up VPN-Homelab-Cisco
nmcli connection down VPN-Homelab-Cisco

Voir aussi VPN avec nmcli pour la gestion générique côté client (dépannage, vérification des certificats).


Étape 6 — Port forwarding sur la box

Section NAT & PAT de l'admin box :

NomProtocolePort externeÉquipementPort interne
VPN-IKEUDP500192.168.1.250 (par MAC)500
VPN-NATTUDP4500192.168.1.250 (par MAC)4500
astuce

Cibler l'équipement par adresse MAC plutôt que par IP évite toute dérive si le bail change. Ne jamais mettre le Cisco en DMZ (exposerait tous ses ports, dont SSH admin).


Vérification & diagnostic

Côté Cisco

show crypto ikev2 sa ! sessions IKEv2 actives
show crypto ipsec sa ! CHILD_SA / trafic chiffré
show crypto session ! vue synthétique
show crypto pki certificates ! état des certificats
show crypto ikev2 profile ! vérifier match / trustpoint / virtual-template
show interface FastEthernet4 | include packets output ! compteur sortie (diag NAT)

Debug (à couper avec undebug all après)

terminal monitor ! afficher les logs dans la session SSH
debug crypto ikev2
debug crypto pki transactions
debug crypto pki validation
! Capture ciblée des paquets IKE :
access-list 150 permit udp any host 192.168.1.250 eq 500
access-list 150 permit udp any host 192.168.1.250 eq 4500
debug ip packet 150 detail

Côté client

sudo journalctl -u NetworkManager -f ! logs strongSwan en direct
ip a ! vérifier l'interface nm-xfrm-* et l'IP virtuelle

Séquence d'une négociation réussie (repères dans les logs)

IKE_SA_INIT → selected proposal AES_CBC_256/SHA256/MODP_2048 (ou ECP_256)
IKE_AUTH → sending/received end entity cert
→ Validation of certificate chain PASSED
→ CHILD_SA established
%LINEPROTO-5-UPDOWN: Virtual-Access1 changed state to up (côté Cisco)

Blocage résiduel : NAT box

Symptôme

Depuis l'extérieur (4G/5G), la négociation démarre puis se bloque :

  • IKE_SA_INIT part du client, le Cisco le reçoit et répond
  • côté Cisco : Failed to receive the AUTH msg before the timer expired
  • côté client : peer not responding / giving up after 3 retransmits

Ce qui a été prouvé

VérificationRésultat
Paquets IKE arrivent au Cisco depuis l'extérieur✅ Oui (debug ip packet : s=<IP externe>, d=192.168.1.250)
Cisco génère et émet physiquement les réponses✅ Oui (packets output augmente de +3 par tentative)
Port forwarding box 500/4500 correct✅ Oui (règles UDP actives, par MAC)
Pas de CGNAT✅ IP publique = IP vue de l'extérieur
Le même tunnel monte parfaitement en LAN✅ Oui (test local complet réussi)

Conclusion : les paquets retour sortent du Cisco mais n'atteignent jamais le client. Le blocage est dans la box (NAT retour UDP 500/4500 défaillant, ou ALG/helper IPsec interne non désactivable qui interfère avec le port forwarding manuel).

Pistes de contournement (non testées, pour reprise ultérieure)

  1. Box en mode bridge + PPPoE sur le Cisco : le Cisco gère le WAN directement, plus de double-NAT ni d'ALG box. Le plus propre, mais reconfigure tout l'accès internet.
  2. VPS relais : monter le tunnel vers un serveur externe sans limitation NAT, qui relaie vers le homelab. Plus complexe (nécessite un VPS + tunnel permanent).
  3. IPv6 : si la box fournit un préfixe IPv6 public, un accès IKEv2 en IPv6 natif contournerait entièrement le NAT IPv4. À explorer.
  4. Chercher un firmware/réglage box exposant la désactivation de l'ALG IPsec (peu probable sur l'interface grand public).

Annexe — Concepts clés

  • IKEv2 — protocole de négociation des clés et d'authentification (phase de contrôle), sur UDP 500, bascule sur UDP 4500 dès qu'un NAT est détecté (NAT-Traversal).
  • ESP — encapsulation du trafic chiffré (les données réelles), encapsulé dans UDP 4500 en présence de NAT.
  • CHILD_SA — l'association de sécurité qui transporte réellement les données une fois IKE établi.
  • Mode one-armed — le concentrateur VPN n'a qu'une interface réseau ; combiné au proxy-ARP, le client distant est intégré au LAN existant sans routage dédié.
  • proxy-ARP — le routeur répond aux requêtes ARP à la place d'un hôte qu'il sait joindre, rendant un client distant « visible » comme local.
  • rsa-sig — méthode d'authentification IKEv2 par signature RSA (certificats X.509), par opposition au PSK (clé pré-partagée).
  • DVTI (Dynamic Virtual Tunnel Interface) — l'interface Virtual-Access clonée dynamiquement depuis la Virtual-Template à chaque connexion client.

📝 Tester ses connaissances

1. Pourquoi faut-il réappliquer `revocation-check none` après un `crypto pki import ... pkcs12` ?

2. Pourquoi le bundle PKCS12 doit-il être exporté avec `-legacy -certpbe PBE-SHA1-3DES` plutôt qu'avec les valeurs par défaut d'OpenSSL 3.x ?

3. À quoi sert le mode « one-armed » combiné à `ip proxy-arp` dans cette architecture ?

4. Pourquoi utiliser `match certificate` (sur l'émetteur du certificat) plutôt que `match identity remote fqdn` dans le profil IKEv2 ?