Client VPN übergibt dem Client eine Adresse, der Handshake läuft durch, und die Verbindung führt trotzdem nirgendwohin. Bevor du Zertifikate oder Clientversionen anfasst, sieh dir die eigene Routentabelle des Endpunkts an. Ein AWS Client VPN Endpunkt leitet ein Paket nur weiter, wenn eine seiner Routen das Zielpräfix abdeckt; passt nichts, wird das Paket am Endpunkt verworfen, und der Zielhost sieht niemals ein SYN.

Das ist eine Weiterleitungsentscheidung, keine Authentifizierungsentscheidung. Sie fällt, nachdem der Tunnel steht und nachdem die Autorisierungsregeln ausgewertet wurden, weshalb ein gültiges Zertifikat und eine gültige Gruppe trotzdem eine tote Verbindung ergeben können.

Der Endpunkt leitet nur weiter, was seine Routentabelle abdeckt

Jede Route ist ein Ziel-CIDR plus das Subnetz, das den Datenverkehr erhalten soll. Der Endpunkt vergleicht das Ziel des Clients mit dieser Liste, wählt den passenden Eintrag und schickt das Paket in das zugehörige Subnetz. Kein Eintrag bedeutet keinen nächsten Hop.

Beachte, was die Route nicht ist. Sie ist kein Eintrag in der Routentabelle der VPC und autorisiert nichts. Autorisierungsregeln entscheiden, ob eine Gruppe ein Ziel erreichen darf; Routen entscheiden, ob der Endpunkt ein Ziel zum Senden hat. Beides muss zusammenpassen, und beides scheitert mit unterschiedlichen Symptomen, deshalb kostet Raten, welches defekt ist, einen ganzen Nachmittag.

Der klassische Fehlgriff ist eine gepeerte VPC. Jemand fügt eine Route für 10.20.0.0/24 hinzu, während der Host in 10.20.4.0/24 liegt, sodass die Adresse außerhalb jedes Eintrags fällt. Ein Mitschnitt auf Clientseite zeigt wiederholte SYN ohne Antwort, und die Logs des Ziels zeigen überhaupt nichts.

Lies die Routenliste vor dem Zertifikat

Die Routen des Endpunkts sind ein eigenes Objekt, frage sie also direkt ab, statt die VPC-Tabelle zu lesen:

aws ec2 describe-client-vpn-routes --client-vpn-endpoint-id cvpn-endpoint-0123456789abcdef0 --query 'Routes[].{Dest:DestinationCidr,Target:TargetSubnet,Origin:Origin}'

Nimm die exakte Adresse und Präfixlänge des fehlschlagenden Clients und prüfe dann die Abdeckung Zeichen für Zeichen. Es gilt die längste Präfixübereinstimmung, eine breitere Route rettet dich also nicht, wenn eine engere woandershin zeigt.

Eine Ressource pro Ziel mit for_each

Handgeschriebene Routenblöcke driften in dem Moment, in dem jemand ein Präfix in der Konsole hinzufügt. Halte die Ziele in einer Map und erzeuge eine Ressource pro Schlüssel, damit jede Hinzufügung oder Entfernung isoliert bleibt.

variable "client_vpn_routes" {
  type = map(string)
  default = {
    "10.20.0.0/16"  = "subnet-0aa11bb22cc33dd44"
    "172.31.0.0/20" = "subnet-0aa11bb22cc33dd44"
  }
}

resource "aws_ec2_client_vpn_route" "this" {
  for_each               = var.client_vpn_routes
  client_vpn_endpoint_id = var.client_vpn_endpoint_id
  destination_cidr_block = each.key
  target_vpc_subnet_id   = each.value
  depends_on             = [aws_ec2_client_vpn_network_association.this]
}

Behalte die Abhängigkeit von der Netzwerkzuordnung. Eine Route braucht ein zugeordnetes Subnetz, ein Apply, der beides erzeugt, muss die Zuordnung also zuerst einordnen, und ohne diese Referenz darf Terraform die Route zuerst versuchen. Lies dann den Plan: die Zahl der Routenhinzufügungen sollte den hinzugefügten Präfixen entsprechen. Ersetzt der Plan auch den Endpunkt, ist die Endpunktressource falsch geschrieben, und ihr Anwenden trennt jeden verbundenen Client.

Einen Endpunkt übernehmen, den du nicht erstellt hast

Die meisten Teams erben einen in der Konsole gebauten Endpunkt, deshalb existiert kein Routencode. Baue keinen zweiten Endpunkt, nur um Routen in Terraform zu bekommen.

Importiere stattdessen, oder übergib die IDs als Variablen und lass die geerbten Objekte unverwaltet. Der Import braucht das Kennungsformat, das der Anbieter für jeden Ressourcentyp dokumentiert; es kodiert den Endpunkt, das Zielpräfix und das Zielsubnetz. Kopiere diesen String aus der Ressourcendokumentation, statt ihn aus dem Gedächtnis zusammenzusetzen, führe einen Plan aus und erwarte keine Änderungen, bevor du etwas Neues hinzufügst.

Verfolge das Paket in der Reihenfolge, in der es reist

Wenn die Route existiert und die Verbindung trotzdem scheitert, gehe den Weg der Reihe nach ab, statt auf der obersten Ebene zu raten.

  1. Route: ein Präfix deckt das Ziel ab und zeigt auf ein dem Endpunkt zugeordnetes Subnetz.
  2. Autorisierung: eine Autorisierungsregel erlaubt dieses Ziel für die Gruppe des Benutzers. Eine Route allein gewährt nie Zugriff.
  3. Rückweg: die Routentabelle des Zielsubnetzes schickt Antworten zurück zum Endpunkt, und die Sicherheitsgruppe erlaubt den Port aus dem Adresspool der Clients.
  4. Anwendung: öffne eine echte TCP-Verbindung vom Client und bestätige dann, dass die Logs des Ziels die erwartete Quelladresse aus dem Pool zeigen.

Ändere ein Präfix pro Apply, lies die Routenliste danach erneut und notiere Ziel, autorisierende Regel und Rückweg zusammen. Wer als Nächstes eine abgelehnte Verbindung debuggt, sollte mit diesem Inventar beginnen, nicht mit einer Zertifikatstheorie.

Ähnliche Artikel