The situation
Wexley Instruments designs industrial flow sensors. The company has decided not to renew the leases on its three offices, and over the next eight months four hundred engineers move to permanent remote working. The three Site-to-Site VPN connections that carried office traffic into AWS go away with the leases. Four hundred laptops on domestic broadband become the edge of the network.
What those laptops need to reach sits behind one transit gateway in ap-southeast-2, in a shared network account and shared to five other accounts with AWS RAM. Six VPCs hang off it: build and CI on 10.40.0.0/16, the hardware-in-the-loop test rigs on 10.41.0.0/16, simulation on 10.42.0.0/16, shared services on 10.43.0.0/16 (the directory, the artefact repository, the licence servers), telemetry on 10.44.0.0/16, and a sandbox on 10.45.0.0/16. Two on-premises ranges are staying: the calibration lab on 172.18.0.0/16 and the plant floor on 172.19.0.0/16. Both are reached through a Direct Connect gateway attached to the same transit gateway, with a Site-to-Site VPN standing behind it. The estate is a hub already, and it has been a hub for three years.
Two constraints shape everything else. Sixty of the four hundred are contractors on one simulation programme. They need a licence server and a job-submission API that both live in 10.42.7.0/24, and they should not be able to reach the test rigs, the plant floor, or anything else. And the security team is three people. There is no proxy fleet and no plan to build one, and counsel has been clear that the company should not carry four hundred people’s personal internet traffic through its own egress.
What actually matters
Start with what the default does. When you create a Client VPN endpoint, split tunnel is off. The client’s route table is overwritten with 0.0.0.0/0 and every packet the laptop emits goes up the tunnel. A video call, an operating-system update, a teenager’s game download on the same laptop at 9pm: all of it arrives inside AWS, is charged NAT gateway data processing on the way through, and leaves again as data transfer out. At four hundred households that is a real line on the bill for traffic nobody at the company chose to carry. Once that traffic is on your network it is arguably yours to log, filter and answer for, and Wexley has three people in security and no inspection capability. Full tunnel is the right answer when policy demands that every byte is inspected and there is a stack in place to inspect it. Here it creates an obligation with no matching control. Split tunnel pushes only the routes on the endpoint route table to the client and leaves everything else on the home connection.
Reach and permission are two separate mechanisms on this service, and they fail in different ways. A route on the endpoint route table says a destination network exists and how to get there. An authorization rule says who may use it. With no rule the answer is no: authorization defaults to deny, and there is no way to write a rule that subtracts access. A destination with a route and no rule gives you a tunnel that comes up, a client route table that looks correct, and traffic dropped at the endpoint. A destination with a rule and no route, under split tunnel, means the route is never pushed to the client, so the packets go out of the home broadband instead. Neither failure shows an error on the laptop. Evaluation is longest-prefix-match across all rules regardless of which group each one names, so a /24 granted to contractors is a /24 taken away from the engineers, unless the engineers get a rule for that same prefix too.
Where the tunnel terminates determines how much routing and how much identity you own. All the subnets you associate with an endpoint have to sit in the same VPC, so six VPCs plus two datacentre ranges has never meant six associations. It has meant one dedicated VPC that exists only to host the endpoint, attached to the transit gateway, with the client’s source address translated to the endpoint’s network interface on the way through. That translation is what hurts an investigation. Every flow log in every spoke shows the same handful of interface addresses, and answering “who was on 10.42.7.19 at 14:05” becomes a correlation exercise across connection logs. Attaching the endpoint natively to the transit gateway removes both the hosting VPC and the translation. It gives up two things. Security-group-based authorization is not supported on that attachment type, so all access control is network-based rules plus the destination’s own security groups written against the client CIDR. And nothing propagates from the transit gateway into the endpoint route table, so every destination is a route you add and maintain by hand.
Finally, four settings are frozen when the endpoint is created: the client IPv4 CIDR range, the authentication options, the client certificate and the transport protocol. Everything else, including split tunnel, routes, rules, DNS, the port number and session duration, can be changed later. That asymmetry sets the order of the work. The client CIDR in particular is not a local choice about one VPC. It becomes a prefix that six VPC route tables and two on-premises routers all need a return path for, and it cannot overlap anything attached to the transit gateway. Getting the editable settings wrong means an afternoon of resets. Getting the frozen ones wrong means a new endpoint with a new DNS name, and a new configuration file for four hundred laptops.
What we’ll filter on
- Carries only the routed prefixes, leaving personal internet traffic on the home connection, with no proxy or inspection fleet to build.
- Reaches six VPCs and two on-premises ranges from one place, without a separate endpoint per VPC and without a VPC that exists only to terminate tunnels.
- Authorises per identity-provider group, so sixty contractors get one
/24and four hundred engineers get the rest. - Preserves enough client identity in the logs to answer who held a given address at a given minute.
- Costs in proportion to people connected, with the availability increment on its own line so it is a deliberate choice.
- Consumes one client address block, chosen once from reserved space, that eight route tables have to carry.
The landscape
One option is not in the running and is worth naming so it stays out. AWS Site-to-Site VPN connects networks to networks over IPsec, terminating on customer gateway devices at fixed, known addresses, with BGP or static routing. It fits the offices Wexley is closing and the datacentre link that remains. It does not fit four hundred roaming laptops with no fixed address between them. The three encapsulations are easy to conflate. The datacentre legs run IPsec; a software-defined WAN Connect attachment onto a transit gateway runs GRE with BGP inside it and carries no encryption of its own; remote access here runs OpenVPN’s TLS-based protocol. Client VPN listens on port 443 or 1194, over TCP or UDP, defaulting to 443. The port can be changed later; the transport protocol is one of the four settings frozen at creation.
AWS Client VPN is the managed remote-access service, and it comes in two shapes. In both, clients use the AWS-provided desktop application or any OpenVPN client, a server certificate in ACM is mandatory whichever authentication method you pick, and each connection gets a maximum baseline bandwidth of 50 Mbps. Access is governed by an endpoint route table plus authorization rules that can name an Active Directory group SID or an identity-provider group.
The first shape associates the endpoint with subnets in a VPC, one subnet per Availability Zone, all in the same VPC. To reach anything beyond that VPC you add endpoint routes pointing at an associated subnet and let the subnet’s own route table carry the traffic onward. For this estate that means a hosting VPC attached to the transit gateway. Source NAT is applied to IPv4 traffic on the way through, so destinations see the endpoint’s network interface address rather than the client’s.
The second shape attaches the endpoint directly to a transit gateway. You name the transit gateway at creation, AWS creates an attachment of type client-vpn, and you pick up to five Availability Zones (two are chosen for you if you say nothing). There is no hosting VPC and no subnet to associate. Routes carry no target subnet because everything goes via the attachment, and SNAT is not applied, so the client’s own address appears in transit gateway flow logs. The two shapes cannot be mixed on one endpoint, the transit gateway must have an IPv4 CIDR block assigned, and it can belong to another account provided it has been shared through RAM.
Six endpoints, one per VPC, is the shape people reach for when they think of an endpoint as belonging to a VPC. It fails on arithmetic first: the default quota is five Client VPN endpoints per Region. Each one also needs its own client CIDR, certificate, rules and configuration file on every laptop.
AWS Verified Access removes the tunnel entirely. It puts a policy engine in front of named applications, evaluates identity and device posture on every request, and logs each attempt. Applications that are not HTTP are reached over TCP, through a load balancer, a network interface, a network CIDR block or an RDS endpoint, with the Connectivity Client installed on the device. It bills per application per hour plus data processed, which suits a short catalogue of named applications. Wexley’s requirement is a routing plan across eight networks rather than a catalogue. The test rigs alone speak a dozen instrument protocols on ports nobody has written down, several of them UDP, which those TCP endpoints do not cover.
A third-party remote-access appliance from Marketplace, run as an HA pair on EC2, brings an interface the network team may already know and features AWS does not ship. It also brings instances sized for four hundred concurrent encryption sessions, vendor licences charged per tunnel, and a patch cycle. With no feature requirement driving it, that is a fleet to run for nothing this scenario needs.
Evaluation
Side by side
| Option | Routed prefixes only | One place for 6 VPCs + 2 on-prem | Group-scoped authorisation | Client identity in logs | Cost tracks connected users | One address block, chosen once |
|---|---|---|---|---|---|---|
| Client VPN on a native transit gateway attachment | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Client VPN into a hosting VPC on the transit gateway | ✓ | ✓ | ✓ | ✗ | ✓ | ✓ |
| One Client VPN endpoint per VPC | ✓ | ✗ | ✓ | ✗ | ✓ | ✗ |
| AWS Verified Access | ✓ | ✗ | ✓ | ✓ | ✗ | ✓ |
| Third-party appliance on EC2 | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ |
Every row can be made to carry only the routed prefixes, so the first column separates nothing and the decision lives in the other five. Six endpoints fails on two of them and on a quota. Verified Access clears four and loses on the shape of the requirement: a routing plan across eight networks is not an application catalogue, and per-application-hour billing is the wrong unit for it. The appliance loses on cost shape, with fixed capacity sized for a peak that exists six hours a day.
That leaves the two Client VPN shapes, and the column that separates them is client identity. The hosting-VPC shape translates every client to a network interface address before the traffic reaches the transit gateway. The native attachment preserves the source address end to end, and removes a VPC, its route tables and its subnet associations from the estate at the same time. It gives up security-group-based authorization, which matters when the plan was to write spoke security groups against a Client VPN security group. Wexley’s plan is to write them against the client CIDR instead.
The solution
One Client VPN endpoint, attached natively to the existing transit gateway in the network account, split tunnel enabled, authenticating against the company identity provider over SAML, with authorization rules written per group.
The client CIDR, chosen once
Block size has to be between /22 and /12 and it cannot be changed after creation. AWS’s own guidance is to allocate twice as many addresses as the maximum concurrent connections you intend to support, because part of the range serves the availability model and is never handed to a client. Four hundred people implies eight hundred addresses at that ratio, which a /22 covers with almost nothing spare. Take 10.60.0.0/20 out of the reserved half of the address plan instead: 4,096 addresses, roughly two thousand usable, room for the headcount to triple and for an acquisition to arrive without a rebuild. The check is overlap with anything attached to this transit gateway, now or in the plan, and it is the check that gets skipped when the range is picked from whatever looked free.
Concurrency is bounded by the number of associations, one per Availability Zone: 7,000 concurrent connections at one, 36,500 at two, and 126,000 at five. Four hundred people never approaches any of those, so the number of zones is an availability decision rather than a capacity one.
Routes and rules
Nothing propagates from the transit gateway into the endpoint route table, so eight routes get written by hand, one per destination network, with no target subnet because the attachment is the path. The transit gateway route table associated with the client-vpn attachment needs routes to the six VPC attachments and the Direct Connect gateway. In the other direction, the six VPC route tables need 10.60.0.0/20 pointed at the transit gateway and the two on-premises routers need it in their BGP tables. A return path missing in one spoke produces an application that works for everyone until they open the one tool that lives in that spoke.
Then the rules, and the ordering that settles the contractor requirement:
| Destination | Group | Effect |
|---|---|---|
10.40.0.0/16 … 10.45.0.0/16 (six rules) |
wexley-engineering |
Engineers reach all six VPCs |
172.18.0.0/16, 172.19.0.0/16 |
wexley-engineering |
Engineers reach lab and plant |
10.42.7.0/24 |
wexley-contractors |
Contractors reach the simulation licence subnet |
10.42.7.0/24 |
wexley-engineering |
Engineers keep the subnet the contractors were granted |
The last row is the one that gets left out. Longest prefix wins across the whole rule set, not within a group, so the contractor /24 overrides the engineering /16 for those 256 addresses and removes the licence server from four hundred people who had it a minute earlier, with no error anywhere. Rules for 0.0.0.0/0 are always evaluated last whatever order they were created in. There is no such rule here, because there is no route to the internet on a split-tunnel endpoint and no reason to add one. The quotas are generous on both sides: 200 authorization rules per endpoint and 100 routes per target network association, both adjustable.
Authentication
Three methods are available, and the contractor requirement settles the choice before anything else does. Mutual certificate authentication carries no group: a rule can name an Active Directory SID or an identity-provider group, and a client certificate presents neither, so every rule ends up granting access to all users. Separating contractors from engineers then means a second endpoint with a second certificate chain, a second client CIDR and a second configuration file to distribute. That is a real design when you have a certificate authority you already run properly and two populations that never overlap. Wexley has neither.
Active Directory authentication through AWS Directory Service works and lets rules name group SIDs. The endpoint has to sit in the same account and Region as the directory resource, since Client VPN does not support multi-Region replication of AWS Managed Microsoft AD. SAML federated authentication points instead at the identity provider that already holds the contractor and engineer groups, already runs the company’s MFA, and already deprovisions a contractor on their last day; rules name the group it asserts. That is the method here, and it keeps the self-service portal available, which certificate authentication would not.
Either user-based method can be combined with mutual authentication, requiring both a valid client certificate and a valid login. That is how you bind access to a managed laptop rather than to a password, and it is worth doing when the device fleet is managed. It also freezes both halves at creation, so decide before you build rather than after.
Availability and what it costs
Two charges: one per endpoint association per hour, one per connected client per hour. At the rates in AWS’s own pricing example, the association is ten cents an hour and each connection five. An association bills around the clock whether anyone is connected or not, about USD$73 a month per Availability Zone. Four hundred people, nine hours a day, twenty-one working days: near enough USD$3,800 a month in connection charges.
That settles the redundancy question. The second zone is about two per cent of the connection charge, and it is the difference between a zonal event taking remote access away from the entire company and taking it away from nobody. A third zone is another USD$73 for a scenario the second one already covers.
What to watch
Connection logs in CloudWatch Logs record who connected, when, and with which assigned address; transit gateway flow logs record what that address then did. Correlating the two by address and timestamp is how the “who was on 10.42.7.19 at 14:05” question gets answered, and it is the concrete benefit of not translating the source. Endpoint modifications can take up to four hours to take effect. Changing routes, rules, DNS servers, the port, the split-tunnel setting or the server certificate resets every live connection, so those edits belong in a window.
Worked example
The endpoint as built:
- Client CIDR
10.60.0.0/20, from reserved space, checked against every attachment on the transit gateway and against the ranges held for the next two datacentre projects. - Native attachment to
tgw-wexley-hub, cross-account through the existing RAM share, Availability Zonesap-southeast-2aandap-southeast-2b. - Split tunnel enabled. No
0.0.0.0/0route and no0.0.0.0/0rule. - SAML federated authentication against the company identity provider, server certificate in ACM, self-service portal enabled so laptops can pull their own configuration file.
- Transport UDP, chosen at creation and unchangeable, on port 443, which can be changed later. A client on a network blocking UDP 443 cannot connect and would need a second endpoint on TCP if that turns out to be common.
- Eight endpoint routes, ten authorization rules, connection logging on, transit gateway flow logs on.
- Spoke security groups admit
10.60.0.0/20rather than referencing a Client VPN security group, which the attachment type does not support.
The bill, at the rates in AWS’s pricing example:
- Associations: 2 × USD$0.10 × 730 hours = about USD$146 a month.
- Connections: 400 people × 9 hours × 21 days × USD$0.05 = about USD$3,780 a month.
- Total: around USD$3,930 a month, plus transit gateway attachment and data processing on traffic that was crossing the transit gateway anyway.
The full-tunnel counterfactual belongs next to it. Four hundred people at a conservative 8 GB a month of personal traffic is 3.2 TB arriving inside the VPC, charged NAT gateway data processing on the way through and data transfer out on the way back, before anyone has looked at a single packet of it. The saving is real, and it is smaller than the reason for the decision: Wexley would otherwise be responsible for four hundred households’ browsing with three people to be responsible with.
What’s worth remembering
- Split tunnel is off by default, so the default configuration pulls every client’s internet traffic through your egress path and makes it yours to inspect, log and pay for.
- Four things freeze at creation on a Client VPN endpoint: the client IPv4 CIDR, the authentication options, the client certificate and the transport protocol; everything else, split tunnel included, is editable.
- The client CIDR must be between
/22and/12, cannot overlap anything the endpoint routes to, and should hold twice the addresses your peak concurrency needs. - Routes and authorization rules are separate controls, rules default to deny, and evaluation is longest-prefix-match across every rule regardless of group, so a narrow grant to one group removes that prefix from the others.
- Attaching the endpoint to a transit gateway removes the hosting VPC and preserves the client source address, and gives up security-group-based authorization and route propagation.
- Concurrency scales with association count (7,000 at one, 36,500 at two), and each association bills hourly around the clock, so the second zone is an availability decision rather than a capacity one.