My laptop said connected. The AWS Client VPN client showed its green state, the endpoint logged a session, and I still could not open a MongoDB port on an EC2 instance in a private subnet. Nothing was obviously broken: the certificate was valid, the profile came from the console, and the tunnel carried traffic. The mistake was mine, because I had been reading "connected" as "authorized".

Login answers exactly one question: may this identity establish a session? Everything after that is decided by separate rules that never see your certificate. A session that authenticates and a packet that arrives are two different successes, and the gap between them is where most "the VPN is up but the host is unreachable" reports live.

The checks below are the order I use now.

What a connected session actually proves

During the handshake the endpoint validates the client certificate against its trust store, or redirects the browser to your identity provider when SAML is configured. If that passes, the client receives an address from the endpoint's client CIDR and the console shows an active connection. At no point in that exchange does anything ask whether you may reach 10.0.4.17 on port 27017.

Permission for a destination is a separate object on the endpoint. Every flow is matched against the authorization rules, each of which pairs a destination CIDR with the group the rule is granted to. When no rule covers the destination, the endpoint accepts the session and drops the traffic. There is no banner, no reconnect loop, and nothing in the client UI that says denied.

Compare the authorization rule to the resource, not the VPC

List the rules and hold them next to the address the instance actually has:

aws ec2 describe-client-vpn-authorization-rules \
  --client-vpn-endpoint-id cvpn-endpoint-0a1b2c3d4e5f6a7b8

The output shows a destination CIDR and a group id per rule. Two failures hide in it. The rule may authorize 10.0.0.0/16 while the instance sits in a peered VPC on 10.1.0.0/16; the peering route exists, but nobody wrote an authorization rule for the second range. Or the rule may point at a directory group that the user joins in the directory but not in the group the endpoint evaluates.

Write the narrowest rule that covers the destination, and add a second one only when a second CIDR is genuinely required. A broad 0.0.0.0/0 rule from a quick start does work, which is why it survives into production and hides the model.

Two route tables decide whether the packet moves

An endpoint route maps a destination CIDR to one of the subnets the endpoint is associated with. That is half a path. The subnet's own route table still has to know how to forward the packet, which matters the moment the destination is behind a peering connection, a transit gateway, or an on-premises range reachable over a site-to-site tunnel.

aws ec2 describe-client-vpn-routes \
  --client-vpn-endpoint-id cvpn-endpoint-0a1b2c3d4e5f6a7b8

Look for a route whose destination matches the resource CIDR and whose target is an associated subnet. If the route is missing, add it. If it is present and the host is still silent, the gap is on the VPC side, so compare against the route table of the association subnet.

One client-side setting breaks routing without breaking the tunnel: a profile containing route-nopull. The directive tells an OpenVPN-based client to ignore routes the server pushes, so the interface comes up with only the endpoint's own address and the destination is simply unrouted. Check with ip route show on Linux or netstat -rn on macOS whether a route through the tun interface exists. If it does not, drop route-nopull from the exported profile and reconnect so the pushed routes are applied.

Security groups do not see your client address

This is the part I got wrong for an afternoon. For an IPv4 endpoint associated with a subnet, the service translates the client source address to the address of the endpoint's network interface in that subnet. The packet that arrives at the instance does not carry the address your client displayed, so a security group rule allowing the client pool, say 172.16.0.0/22, matches nothing at all.

Allow the port from the endpoint's security group, which is attached to the association's interface, rather than from the client CIDR. Referencing the group is more durable than hard-coding an address, because the interface address is not yours to control. Network ACLs are stateless, so where custom entries exist, both the inbound and the outbound direction need to permit the flow.

Connect to the instance's private address as well. A public IP takes the packet out of the VPC and back in through a different path with different rules, so a public test says nothing about the private path.

An isolation order that stops at the first failure

Work through these in order and stop where one fails. Each step rules out a layer rather than all of them.

  1. Confirm which identity the session used and which group the endpoint evaluates. A certificate from one trust store is not the same principal as a SAML assertion from the directory.
  2. Confirm an authorization rule covers the resource CIDR for that group.
  3. Confirm an endpoint route exists and targets an associated subnet, then confirm the subnet route table can reach onward.
  4. Confirm the security group allows the port from the endpoint interface rather than from the client pool.
  5. Test from inside the VPC, using an SSM session on another instance in the same subnet, to separate an application problem from a client-path problem.

When the steps are not decisive, this table is what I fall back on:

SymptomLayer to inspect
Client shows connected, host times out, no byte growth on the interfaceAuthorization rule, then security group
Ping fails but a TCP connect to the port succeedsICMP is blocked; not an access failure
Tunnel reconnects every few minutesTwo machines sharing one client certificate
Works on one laptop, fails on anotherLocal profile has route-nopull or stale pushed routes

Two account details produce symptoms that look like permission failures. Every user needs a distinct client certificate; sharing one across two machines makes the sessions displace each other, and the result is a reconnect loop rather than a denial. When a new endpoint is created, the console shows a DNS name that some client versions accept only with the random prefix displayed on the endpoint page, so copy the full name instead of reconstructing it.

If step five leaves you stuck, pull the endpoint's connection log and read the authorization entries before changing anything. The log names the destination that was checked and the rule that matched or failed to match, which collapses the four-layer question into one answer.

Artiglyn mynjee