Reaching private resources
On this page
Databases, internal tools, and Kubernetes nodes live in private subnets on purpose, so nothing on the internet can reach them. People still need to, though, and so do other networks. The options run from permanent links between whole networks down to a tunnel to one port that lasts as long as a terminal window.
This leans on AWS VPCs, subnets, and routing, especially route tables and the checklist for when traffic doesn’t get through.
The options at a glance
| Option | Connects | Good for | Setup effort (roughly) | Lasts |
|---|---|---|---|---|
| VPC peering | network ↔ network | Two or three VPCs | Low: one connection and a route on each side | Permanent |
| Transit Gateway | network ↔ network | Many VPCs, plus VPNs and Direct Connect | Medium: an attachment per network and its own route tables | Permanent |
| Site-to-site VPN | network ↔ network | An office or data center, over the internet | Medium: a VPN device at the far end and two tunnels | Permanent |
| Direct Connect | network ↔ network | An office or data center that needs steady bandwidth | High: a physical cable and a provider | Permanent |
| PrivateLink | person or network ↔ one service | One service, even when the address ranges overlap | Medium: a load balancer, an endpoint service, and an endpoint on the other side | Permanent |
| Client VPN | person ↔ network | People who need regular access | Medium: an endpoint, client software, and sign-in | While connected |
| Port forwarding | person ↔ one port | One person, one port, right now | Low: one command, if you already have a way in | While the terminal is open |
Each one gets a section below, in that order (PrivateLink is covered under VPCs in different AWS accounts), and the last section covers what to check when you’re connected and the database still doesn’t answer.
Connecting whole networks
These connect one network to another, so everything on one side can reach (whatever the firewalls allow on) the other side. Peering and Transit Gateway also work between VPCs that belong to different AWS accounts.
VPC peering
A peering connection links two VPCs directly. It doesn’t route anything by itself: each side adds a route for the other’s range with the peering connection as the target [1]. With VPC A on 10.0.0.0/16 and VPC B on 10.1.0.0/16, peered through a connection AWS names something like pcx-…:
| Route table | Destination | Target |
|---|---|---|
| VPC A’s subnets | 10.0.0.0/16 | local |
10.1.0.0/16 | pcx-… | |
| VPC B’s subnets | 10.1.0.0/16 | local |
10.0.0.0/16 | pcx-… |
An instance at 10.0.1.25 in A sends a packet to 10.1.20.10 in B. A’s table matches 10.1.0.0/16 and hands it to the peering connection, and in B the local route delivers it. The reply goes from 10.1.20.10 back to 10.0.1.25, so B’s table needs the 10.0.0.0/16 row. Without that row the request still arrives and the reply has no way back, which looks exactly like the database being down. (If destination and target are fuzzy, the VPC primer walks through them.)
Peering is simple, with two limits that shape bigger designs:
- No overlapping ranges. Two VPCs whose CIDR blocks overlap can’t be peered at all [2], which is why giving every VPC its own range matters.
- Not transitive. If A is peered with B and B with C, A still can’t reach C through B [2]. Connecting a lot of VPCs means a peering connection for every pair, and the count grows fast: 5 VPCs need 10, and 10 VPCs need 45.
Peering only connects the two VPCs at its ends, so a full mesh needs a connection for every pair. A Transit Gateway needs one attachment per VPC.
Transit Gateway
A Transit Gateway is a hub that VPCs, VPNs, and Direct Connect links all attach to, so instead of wiring every pair together, each network connects once to the hub. AWS describes it as a virtual router for the region, and it has its own route tables deciding which attachments can reach which [3].
An attachment is one network’s connection to the Transit Gateway: a VPC, a VPN connection, or a Direct Connect gateway. Packets come into the Transit Gateway from an attachment and leave through another one. For a VPC, the attachment puts a network interface (a virtual network card with its own private address [4]) into one subnet in each availability zone you choose, and the Transit Gateway uses those interfaces to send traffic into and out of the VPC [3]. An availability zone is, roughly, one data center in the region, and every subnet lives in exactly one.
Following a packet through it
Take the same two VPCs, A on 10.0.0.0/16 and B on 10.1.0.0/16, but attached to a Transit Gateway (tgw-…) instead of peered. Three route tables are involved, two in the VPCs and one in the Transit Gateway:
| Route table | Destination | Target |
|---|---|---|
| VPC A’s subnets | 10.0.0.0/16 | local |
10.1.0.0/16 | tgw-… | |
| The Transit Gateway’s | 10.0.0.0/16 | the attachment for VPC A |
10.1.0.0/16 | the attachment for VPC B | |
| VPC B’s subnets | 10.1.0.0/16 | local |
10.0.0.0/16 | tgw-… (the return route) |
The Transit Gateway’s two rows usually aren’t typed in by hand. Each VPC attachment propagates its VPC’s CIDR blocks into the Transit Gateway route table, which is AWS’s word for adding them automatically. The VPC route tables are different: you add the tgw-… routes yourself [3].
Now 10.0.1.25 in A connects to a database at 10.1.20.10 in B:
- In VPC A, the subnet’s route table matches
10.1.0.0/16and hands the packet to the Transit Gateway. - In the Transit Gateway, the packet arrives from the attachment for A. The Transit Gateway looks up
10.1.20.10in its own route table, matches10.1.0.0/16, and sends the packet out through the attachment for B. - In VPC B, the packet comes out of the attachment’s network interface, and the
localroute delivers it to10.1.20.10. - The reply goes from
10.1.20.10to10.0.1.25. B’s subnet matches10.0.0.0/16and hands it to the Transit Gateway, the Transit Gateway matches10.0.0.0/16and sends it out through the attachment for A, and A’slocalroute delivers it.
Every hop only looks at the destination address, and neither address changes along the way. Adding a third VPC means one more attachment and one more row in each VPC that should reach it, not a new connection to every other VPC.
Three details catch people out:
- A VPC attachment needs a subnet in each availability zone. Resources in a zone with no attachment subnet can’t reach the Transit Gateway at all, even with a route to it [3], [5].
- Return routes live in the VPC. Each subnet with resources that should be reachable needs a route back to the far side’s range pointing at the Transit Gateway, like the last row in the table above. Without it, requests arrive and replies go nowhere [5].
- Address ranges can’t overlap, same as with peering. Transit Gateway lets you attach a VPC whose range matches or overlaps one that’s already attached, but it won’t route between them: the new VPC’s routes just never get added to the gateway’s route table [5].
For two overlapping networks that really do need to talk, AWS documents a workaround with a private NAT gateway [6]. PrivateLink is usually simpler for one service.
Site-to-site VPN
A site-to-site VPN is an encrypted tunnel between two whole networks, usually an office or data center and AWS. A tunnel is a packet wrapped inside another packet. The original packet, still addressed from one private address to another, becomes the payload of a new packet addressed between the two VPN devices, and the far device unwraps it and sends the original on. It’s the same encapsulation every layer of the network already does, with a second IP header on the outside [7].
AWS’s site-to-site VPN uses IPsec for the tunnel [8]. IPsec works at the IP layer, so it encrypts every packet between the two networks whatever program sent it [7], and any protocol can ride inside: HTTP, database connections, anything. TLS, the encryption behind HTTPS (see the TLS handshake), works differently: it protects one application’s connection, set up by that application [9]. A site-to-site VPN protects all the traffic between two places, and TLS protects one conversation wherever it goes.
In AWS, the far end is described by a customer gateway (the office’s VPN device), and the AWS end is a virtual private gateway on one VPC or a Transit Gateway. Each VPN connection comes with two tunnels that end in different availability zones, and the office device should have both up, because AWS takes one down from time to time for maintenance [8], [10]. The routes for the office’s ranges are either typed in as static routes or learned over BGP (Border Gateway Protocol, which lets the office device announce its ranges itself), and AWS recommends BGP when the device supports it because its checks help traffic fail over to the second tunnel [11].
Direct Connect
Direct Connect is a dedicated physical connection into AWS instead of a tunnel over the internet: a fiber-optic Ethernet cable with your router on one end and an AWS Direct Connect router on the other, at a Direct Connect location. To use one, your equipment is either in that facility already (colocated) or you reach it through a Direct Connect partner or another network provider [12].
It costs more and takes longer to set up, and in return you get more bandwidth and much steadier latency. It isn’t encrypted by default. The traffic is private, but if it needs to be encrypted you add MACsec (on supported connections) or run a site-to-site VPN over the Direct Connect link [13]. MACsec is an IEEE standard that encrypts at layer 2, one Ethernet link at a time: here, between your router and AWS’s device at the Direct Connect location. It protects that cable, not the whole path end to end [14]. Large setups often use Direct Connect as the main path with a VPN as the backup.
VPCs in different AWS accounts
Companies often give each team or environment its own AWS account, so the VPCs that need to talk end up in different accounts. Peering and Transit Gateway both work across accounts, and the difference from the single-account case is mostly about who has to agree to what.
With peering, one account asks and the other accepts [15], [16]:
- The requester (say account
111111111111, which owns VPC A) creates a peering request, giving the other account’s ID and its VPC’s ID, plus that VPC’s region if it’s in a different one. - The accepter (account
222222222222, which owns VPC B) sees the request as pending and accepts it. A request nobody acts on expires after 7 days. - Each account adds its own route, exactly as in the peering table above. Neither account can change the other’s route tables, so both have to do their half.
- Each account opens its own security groups. In the same region, a rule can name the other account’s security group directly, as
222222222222/sg-…. Across regions it has to use the other VPC’s address range instead [17].
macOS / Linux
# in account 111111111111: ask to peer VPC A with VPC B in the other account
aws ec2 create-vpc-peering-connection --vpc-id vpc-aaaa1111 \
--peer-vpc-id vpc-bbbb2222 --peer-owner-id 222222222222
# in account 222222222222: accept it, using the pcx- ID from the request
aws ec2 accept-vpc-peering-connection --vpc-peering-connection-id pcx-0123456789abcdef0
Windows (PowerShell)
# in account 111111111111: ask to peer VPC A with VPC B in the other account
aws ec2 create-vpc-peering-connection --vpc-id vpc-aaaa1111 `
--peer-vpc-id vpc-bbbb2222 --peer-owner-id 222222222222
# in account 222222222222: accept it, using the pcx- ID from the request
aws ec2 accept-vpc-peering-connection --vpc-peering-connection-id pcx-0123456789abcdef0
Add --peer-region to the first command when VPC B is in another region. Everything else about peering still applies: the two address ranges can’t overlap, and the connection doesn’t carry traffic on to a third VPC [16].
Public DNS names across a peering connection
If instances should find each other by their public DNS names, turn on DNS resolution for the peering connection, which makes those names resolve to private addresses. Both VPCs need DNS hostnames and DNS resolution enabled for that to work [18].
With Transit Gateway, one account owns the gateway and shares it through AWS Resource Access Manager (RAM), with specific accounts or the whole organization [19]:
- The owning account shares the transit gateway in RAM.
- The other account accepts the share and creates a VPC attachment from its own VPC to the shared gateway.
- The owning account accepts the attachment, unless the gateway was created with Auto accept shared attachments turned on [20].
- Routes split by ownership. Only the owning account can change the transit gateway’s route tables [19], and each account adds the routes in its own VPC pointing at the gateway, the same as in the walkthrough above.
Which one to use follows the same logic as in one account. For a couple of VPCs, peering is simpler. For many accounts, Transit Gateway scales better: each account attaches once instead of peering with every other VPC.
What each one costs
Peering has no hourly charge. Data that crosses an availability zone over a peering connection is billed at $0.01 per GB in each direction [21]. Transit Gateway is billed per attachment per hour plus per GB of data it processes [22].
When only one service needs to be reachable, connecting whole networks is more than the job needs. With PrivateLink, the account that runs the service puts it behind a Network Load Balancer and publishes it as an endpoint service, then allows the other account to use it. The other account creates an interface endpoint in its own VPC and reaches the service through that endpoint’s local address [23], [24]. Connections only go one way, from the consumer to the service, and the two VPCs’ address ranges are allowed to overlap [25], which helps when two accounts happened to pick the same 10.0.0.0/16.
Connecting people: client VPN
A client VPN connects one person’s laptop to a network instead of connecting two networks. AWS has its own (AWS Client VPN), and plenty of teams run their own VPN server (OpenVPN, WireGuard, and so on) in a VPC instead.
Either way, the laptop gets an address from the VPN’s own client range, a block set aside for VPN users, like 10.250.0.0/22. It can’t overlap with the networks it connects to, and AWS Client VPN requires it to be between a /22 and a /12 [26]. In AWS Client VPN, the endpoint is the resource you create that every VPN session ends at [27]. Associating it with a subnet puts network interfaces for it in that subnet, with addresses from the subnet’s range [28].
Which source address the destination sees
A reply goes to whatever source address the packet carried when it arrived, and VPNs differ on what that address is:
The database answers whatever source address it sees. With AWS Client VPN that's an address inside the VPC, and with a VPN that passes the client address through it's an address from the client range, which the VPC knows nothing about until you add a route for it.
- AWS Client VPN translates the address. It applies source NAT (network address translation, rewriting the packet’s source address) as traffic leaves the endpoint’s network interface, so the database sees
10.0.1.25, not the laptop’s10.250.0.7[28]. Inside that VPC no extra return route is needed, because the VPC’slocalroute covers the interface’s address. The database’s security group allows the endpoint’s subnet (or the endpoint’s security group), not the client range. AWS Client VPN also has its own authorization rules (which client groups may reach which ranges) and its own route table, and both have to allow a destination before traffic even leaves the VPN [29], [28]. - Many self-run VPNs pass the client address through. The database sees
10.250.0.7itself. Then every subnet the client needs to reach has to have a route for10.250.0.0/22pointing at the VPN instance, and every security group has to allow10.250.0.0/22. The VPN instance also needs its source/destination check turned off. By default AWS checks that an instance is the source or the destination of all the traffic it handles, and a VPN server forwarding the laptop’s packets is neither [4].
That difference explains a lot of “I’m connected but can’t reach anything.” The right return route and security group rule depend on which address the destination actually sees, and the last section walks through a full example.
One port, for now: port forwarding
Sometimes you only need to reach one database for an afternoon, and a VPN is more than the job needs. Port forwarding makes a port on your laptop connect through something that’s already inside the network.
Through a Kubernetes cluster
If you can already reach a Kubernetes cluster inside the VPC with kubectl, the cluster can act as the way in. kubectl port-forward can only forward to a pod, or to a pod picked by a service or deployment, not to an arbitrary host like a database [30]. So the first step runs a tiny relay pod that forwards to the database, and the second forwards a local port to that pod:
macOS / Linux
# terminal 1: a throwaway pod that relays port 5432 to the database (leave it running)
kubectl run pg-proxy --rm -i --restart=Never --image=alpine/socat -- \
TCP-LISTEN:5432,fork,reuseaddr \
TCP:my-db.xxxxxxxxxxxx.us-east-1.rds.amazonaws.com:5432
# terminal 2, once the pod is running: connect local port 5433 to the pod's port 5432
kubectl port-forward pod/pg-proxy 5433:5432
Windows (PowerShell)
# window 1: a throwaway pod that relays port 5432 to the database (leave it running)
kubectl run pg-proxy --rm -i --restart=Never --image=alpine/socat -- `
TCP-LISTEN:5432,fork,reuseaddr `
TCP:my-db.xxxxxxxxxxxx.us-east-1.rds.amazonaws.com:5432
# window 2, once the pod is running: connect local port 5433 to the pod's port 5432
kubectl port-forward pod/pg-proxy 5433:5432
Then the database client connects to localhost on port 5433. The connection takes four hops, and only the last two happen inside the VPC:
The teal hop is the encrypted connection to the API server that kubectl already uses for everything else. The database itself is only ever reached from the pod, inside the VPC.
The pieces of the first command:
kubectl run pg-proxycreates a pod namedpg-proxyfrom thealpine/socatimage [31].--rmdeletes the pod after it exits. It only works whenkubectlis attached to the pod, and-iattaches it [31].-i(--stdin) keeps the pod’s input open and attacheskubectlto it, so the command keeps running for as long as the pod does [31].--restart=Neversets the pod’s restart policy. The default isAlways, which would start socat again if it stopped.Neverleaves it stopped, which suits a throwaway pod [31].--endskubectl’s own options. Everything after it goes to the container as arguments to its default command [31], and this image’s default command is socat [32].- socat (SOcket CAT) connects two streams of bytes, and takes two addresses [33]:
TCP-LISTEN:5432listens on port 5432 inside the pod.forkhandles each new connection in its own child process and keeps listening, so more than one connection (and a reconnect) works [33].reuseaddris harmless to include, and recent socat versions set it on listening addresses already [33].TCP:my-db…:5432connects each one to the database on port 5432. Thexxxxxxxxxxxxandus-east-1are placeholders for your own database’s endpoint name and region, which the RDS console shows.
- Two terminals, because the first command stays attached to the pod and keeps running. Start the second once the pod is up (
kubectl get pod pg-proxyshowsRunning).
And the second:
kubectl port-forward pod/pg-proxy 5433:5432listens on local port 5433 and forwards each connection to port 5432 in the pod. The order is always local first, then the pod’s [30]. The traffic travels through the encrypted connection to the Kubernetes API thatkubectlalready has, and the API server passes it to the pod through the node’s kubelet, so the database never has to be reachable from outside the VPC [34], [35].- Local port 5433 instead of 5432, so it doesn’t clash with a Postgres you might have running locally.
It only lasts as long as both commands keep running. Stop the port forward with Ctrl+C, then stop the first command, and --rm deletes the pod. If a pod outlives it anyway, kubectl delete pod pg-proxy removes it. The database’s security group still has to allow traffic from the pod, which usually means allowing the cluster nodes’ security group.
Through Session Manager or SSH
The same idea works with other ways in:
-
AWS Systems Manager Session Manager can forward a local port through an EC2 instance to another host in the VPC, with no SSH keys and no inbound ports open on the instance [36], [37]:
macOS / Linux
aws ssm start-session --target i-xxxxxxxxxxxxxxxxx \ --document-name AWS-StartPortForwardingSessionToRemoteHost \ --parameters '{"host":["my-db.xxxxxxxxxxxx.us-east-1.rds.amazonaws.com"],"portNumber":["5432"],"localPortNumber":["5433"]}'Windows (PowerShell)
aws ssm start-session --target i-xxxxxxxxxxxxxxxxx ` --document-name AWS-StartPortForwardingSessionToRemoteHost ` --parameters 'host=my-db.xxxxxxxxxxxx.us-east-1.rds.amazonaws.com,portNumber=5432,localPortNumber=5433'The pieces:
--targetis the instance the session goes through, by its instance ID.i-xxxxxxxxxxxxxxxxxis a placeholder for yours [38].--document-namepicks what kind of session to start. In Systems Manager a document isn’t a file you pass in. It’s a named, predefined set of actions that Systems Manager runs, and the ones starting withAWS-are written and maintained by AWS [39].AWS-StartPortForwardingSessionToRemoteHostforwards a port to some other host [37].hostis the database’s name (or IP address), which the instance has to be able to resolve and reach.portNumberis the port on that host, andlocalPortNumberis the port on your laptop, so the database client connects tolocalhost:5433[37].
A few things have to be in place first. Your laptop needs the AWS CLI and its Session Manager plugin installed [37]. The instance needs the SSM Agent running, recent enough to support port forwarding to a remote host [37], plus outbound HTTPS to the Systems Manager endpoints and permission to talk to Systems Manager, usually through an instance profile (the IAM role attached to the instance) with the
AmazonSSMManagedInstanceCorepolicy [40], [41]. The database’s security group has to allow the instance, because the instance opens the connection to it.The PowerShell version uses the AWS CLI’s shorthand instead of JSON, because Windows PowerShell 5.1 drops the JSON’s inner double quotes [42].
-
An SSH bastion host does the same with
ssh -L, at the cost of keeping an SSH port open and managing keys. Forwarding a port through a bastion walks through the command.
How safe is a port forward?
A port forward doesn’t open anything new to the internet. It rides on access you already have, to the cluster or to the instance, so it’s exactly as safe as that access and the pieces around it, hop by hop:
-
Laptop to the cluster or instance. This hop is encrypted and needs a login.
kubectluses its existing TLS connection to the Kubernetes API [35]. Session Manager encrypts the session with TLS and only lets in the people and instances an IAM policy allows [36]. In Kubernetes, starting the relay means you can create pods in that namespace, which already gives you a lot: a pod can mount the namespace’s Secrets and run as any of its service accounts [43]. -
The local port.
kubectl port-forwardlistens onlocalhostunless you pass--address[30], so other machines on your Wi-Fi can’t use port 5433. Don’t pass--address 0.0.0.0, which would hand your access to the whole network. Every program on your own laptop can still connect to it, though, and the forward has no password of its own, so the database’s login is the only check left. Close it when you’re done. -
Pod or instance to the database. The last hop is plain TCP. socat only relays bytes, so the traffic is encrypted end to end only if the database client asks for TLS itself. Postgres clients default to
sslmode=prefer, which uses TLS when the server offers it but quietly carries on without it otherwise [44].sslmode=requireinsists on encryption, and RDS for PostgreSQL 15 and later reject unencrypted connections by default [45]:psql "host=localhost port=5433 dbname=app user=me sslmode=require"requiredoesn’t check which server answered.verify-fulldoes, by matching the name you connected to against the certificate [44], and through a port forward that fails: you connected tolocalhost, and the certificate names the RDS endpoint [45].verify-cachecks the certificate’s issuer without the name, or you can point the RDS endpoint’s name at127.0.0.1in your hosts file and connect to that name on port 5433. -
The relay pod. socat listens on the pod’s own address, not just for your forward. Kubernetes allows every inbound connection to a pod by default, until a NetworkPolicy selects it [46], so while
pg-proxyruns, any other pod in the cluster can reach the database through it. That’s the main reason to keep it short-lived. Thealpine/socatimage also comes from a community account on Docker Hub, and a tag can be moved to point at different code. Pinning the image by its digest (alpine/socat@sha256:…) runs the same image every time [47], and an image from your own registry is safer still. -
The database’s security group. Allowing the cluster nodes’ security group lets every pod on those nodes reach the database, all the time and not just during your session. Running the relay on a dedicated node group, and allowing only that group’s security group, is tighter.
Session Manager avoids most of the relay pod’s problems. Nothing extra runs for other workloads to find, and the database only has to allow that one instance. Session Manager records who started each session in CloudTrail, but it can’t log what passes through a port forward, since it only tunnels the bytes [36].
Connected but can’t reach it
Work through the path in order, from the laptop to the resource and back. The steps marked general are the same checks as for any traffic in a VPC, covered in when traffic doesn’t get through.
- The VPN allows it. For AWS Client VPN, an authorization rule covers the destination range, and the endpoint’s route table has a route for it. For a self-run VPN, the laptop sends that range into the tunnel at all.
- A route toward it (general). From the VPN’s network to the destination, through the peering connection or Transit Gateway if it’s in another VPC.
- The Transit Gateway path, if there is one. The destination VPC’s attachment has a subnet in the destination’s availability zone, and the Transit Gateway’s route tables route both directions.
- A route back (general). Every subnet the destination lives in has a route for the source range (the client range or the VPN’s own range, depending on the VPN) pointing back the way it came. This one is easy to miss, especially when an environment has more subnets than the ones you checked.
- The NACLs, both ways (general). Including ephemeral ports on the way back: the port the client picked for its end of the connection, which the reply is addressed to.
- The security group on the destination (general). It allows the source the destination actually sees, on the right port.
- The name resolves to the private address. If the hostname only resolves inside the VPC (a private hosted zone), the laptop’s queries have to reach a resolver that can see the zone, and the zone has to be associated with the VPC that resolver answers for. The VPC’s own
.2resolver is only for queries from inside the VPC, so when the VPN’s clients reach it from outside (a self-run VPN that passes the client address through, say), the VPN should hand out a Route 53 Resolver inbound endpoint, an address in the VPC that accepts queries from outside it, or a DNS forwarder running inside the VPC [48], [49], [50]. - Something is listening (general). On that address and port, not just on
localhost.
A worked example
A self-run VPN server runs on an instance at 10.0.1.30 in VPC A (10.0.0.0/16) and hands out addresses from 10.250.0.0/22 without translating them. A laptop on the VPN at 10.250.0.7 tries to reach a Postgres database at 10.1.20.10:5432 in VPC B (10.1.0.0/16), and both VPCs are attached to one Transit Gateway. The connection hangs. Going down the list:
| Step | What to check here | Result |
|---|---|---|
| 1. The VPN allows it | The laptop’s routing table sends 10.1.0.0/16 into the tunnel. | Yes: the VPN pushes that range to clients. |
| 2. A route toward it | The VPN instance’s subnet in A has 10.1.0.0/16 → tgw-…. | Yes. |
| 3. The Transit Gateway path | Its route table has 10.1.0.0/16 → the attachment for B (propagated), and B’s attachment has a subnet in the database’s availability zone. For the reply it also needs 10.250.0.0/22 → the attachment for A. | No. 10.250.0.0/22 isn’t one of A’s CIDR blocks, so it never propagated [3]. Add it as a static route. |
| 4. A route back | The database’s subnet in B has 10.250.0.0/22 → tgw-…. In A, the attachment’s subnets have 10.250.0.0/22 → the VPN instance, and the instance’s source/destination check is off [5], [4]. | No for B’s subnet: it only had 10.0.0.0/16 → tgw-…. Add the route. |
| 5. The NACLs | B’s database subnet allows 5432 in from 10.250.0.0/22 and ephemeral ports out to it. | Yes: the default NACL allows everything. |
| 6. The security group | The database’s security group allows 10.250.0.0/22 on 5432. | No: it allowed 10.0.0.0/16, which covers the VPN instance but not the laptop, and the database sees the laptop’s address. Add the rule. |
| 7. The name | db.internal.example.com resolves to 10.1.20.10 on the laptop. The private zone is associated with VPC A, and the DNS server the VPN hands out can see it. | No: the VPN handed out 10.0.0.2, but queries from 10.250.0.7 come from outside the VPC, where the .2 resolver isn’t supported [49], and the zone was only associated with VPC B. Associate the zone with VPC A and point the VPN’s DNS setting at an inbound endpoint in VPC A, or at a forwarder on the VPN instance that passes queries to 10.0.0.2 [48], [50], [51]. An inbound endpoint sees the laptop’s own address, like the database does, so its subnet needs the same 10.250.0.0/22 route back to the VPN instance. |
| 8. Something is listening | Postgres listens on 10.1.20.10:5432. | Yes: other things in the VPC connect fine. |
Four fixes. Three are on the way back to an address from the client range, and the fourth is DNS, for the same reason: the laptop’s queries arrive from outside the VPC. With AWS Client VPN in the same spot, the database would see the endpoint’s address in VPC A, and the existing 10.0.0.0/16 routes and security group rule would already cover the reply. The endpoint would only need a route and an authorization rule for 10.1.0.0/16, and its DNS setting could be 10.0.0.2, as long as the zone is associated with VPC A [51].
References
Show all 51 sourcesHide sources
- [1] ↩ Amazon Web Services, “Update your route tables for a VPC peering connection,” Amazon VPC Peering Guide. Accessed: Oct. 7, 2026. [Online]. Available: https://docs.aws.amazon.com/vpc/latest/peering/vpc-peering-routing.html
- [2] ↩ a b Amazon Web Services, “How VPC peering connections work,” Amazon VPC Peering Guide. Accessed: Oct. 5, 2026. [Online]. Available: https://docs.aws.amazon.com/vpc/latest/peering/vpc-peering-basics.html
- [3] ↩ a b c d e Amazon Web Services, “How AWS Transit Gateway works,” Amazon VPC Transit Gateways. Accessed: Oct. 5, 2026. [Online]. Available: https://docs.aws.amazon.com/vpc/latest/tgw/how-transit-gateways-work.html
- [4] ↩ a b c Amazon Web Services, “Elastic network interfaces,” Amazon EC2 User Guide. Accessed: Oct. 8, 2026. [Online]. Available: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-eni.html
- [5] ↩ a b c d Amazon Web Services, “Amazon VPC attachments in AWS Transit Gateway,” Amazon VPC Transit Gateways. Accessed: Oct. 5, 2026. [Online]. Available: https://docs.aws.amazon.com/vpc/latest/tgw/tgw-vpc-attachments.html
- [6] ↩ Amazon Web Services, “NAT gateway use cases,” Amazon VPC User Guide. Accessed: Oct. 7, 2026. [Online]. Available: https://docs.aws.amazon.com/vpc/latest/userguide/nat-gateway-scenarios.html
- [7] ↩ a b S. Kent and K. Seo, “Security Architecture for the Internet Protocol,” RFC 4301, Internet Engineering Task Force, Dec. 2005. Accessed: Oct. 8, 2026. [Online]. Available: https://www.rfc-editor.org/rfc/rfc4301
- [8] ↩ a b Amazon Web Services, “What is AWS Site-to-Site VPN?” AWS Site-to-Site VPN User Guide. Accessed: Oct. 5, 2026. [Online]. Available: https://docs.aws.amazon.com/vpn/latest/s2svpn/VPC_VPN.html
- [9] ↩ E. Rescorla, “The Transport Layer Security (TLS) Protocol Version 1.3,” RFC 9846, Internet Engineering Task Force, Jul. 2026. Accessed: Oct. 5, 2026. [Online]. Available: https://www.rfc-editor.org/rfc/rfc9846
- [10] ↩ Amazon Web Services, “Resilience in AWS Site-to-Site VPN,” AWS Site-to-Site VPN User Guide. Accessed: Oct. 5, 2026. [Online]. Available: https://docs.aws.amazon.com/vpn/latest/s2svpn/disaster-recovery-resiliency.html
- [11] ↩ Amazon Web Services, “Static and dynamic routing in AWS Site-to-Site VPN,” AWS Site-to-Site VPN User Guide. Accessed: Oct. 7, 2026. [Online]. Available: https://docs.aws.amazon.com/vpn/latest/s2svpn/vpn-static-dynamic.html
- [12] ↩ Amazon Web Services, “What is Direct Connect?” AWS Direct Connect User Guide. Accessed: Oct. 7, 2026. [Online]. Available: https://docs.aws.amazon.com/directconnect/latest/UserGuide/Welcome.html
- [13] ↩ Amazon Web Services, “Encryption in AWS Direct Connect,” AWS Direct Connect User Guide. Accessed: Oct. 5, 2026. [Online]. Available: https://docs.aws.amazon.com/directconnect/latest/UserGuide/encryption-in-transit.html
- [14] ↩ Amazon Web Services, “MAC Security in Direct Connect,” AWS Direct Connect User Guide. Accessed: Oct. 7, 2026. [Online]. Available: https://docs.aws.amazon.com/directconnect/latest/UserGuide/MACsec.html
- [15] ↩ Amazon Web Services, “Create a VPC peering connection,” Amazon VPC Peering Guide. Accessed: Oct. 7, 2026. [Online]. Available: https://docs.aws.amazon.com/vpc/latest/peering/create-vpc-peering-connection.html
- [16] ↩ a b Amazon Web Services, “How VPC peering connections work,” Amazon VPC Peering Guide. Accessed: Oct. 7, 2026. [Online]. Available: https://docs.aws.amazon.com/vpc/latest/peering/vpc-peering-basics.html
- [17] ↩ Amazon Web Services, “Update your security groups to reference peer security groups,” Amazon VPC Peering Guide. Accessed: Oct. 7, 2026. [Online]. Available: https://docs.aws.amazon.com/vpc/latest/peering/vpc-peering-security-groups.html
- [18] ↩ Amazon Web Services, “Enable DNS resolution for a VPC peering connection,” Amazon VPC Peering Guide. Accessed: Oct. 7, 2026. [Online]. Available: https://docs.aws.amazon.com/vpc/latest/peering/vpc-peering-dns.html
- [19] ↩ a b Amazon Web Services, “Shared transit gateways,” Amazon VPC Transit Gateways. Accessed: Oct. 7, 2026. [Online]. Available: https://docs.aws.amazon.com/vpc/latest/tgw/transit-gateway-share.html
- [20] ↩ Amazon Web Services, “Create a transit gateway in AWS Transit Gateway,” Amazon VPC Transit Gateways. Accessed: Oct. 7, 2026. [Online]. Available: https://docs.aws.amazon.com/vpc/latest/tgw/create-tgw.html
- [21] ↩ Amazon Web Services, “Amazon VPC pricing.” Accessed: Oct. 7, 2026. [Online]. Available: https://aws.amazon.com/vpc/pricing/
- [22] ↩ Amazon Web Services, “AWS Transit Gateway pricing.” Accessed: Oct. 7, 2026. [Online]. Available: https://aws.amazon.com/transit-gateway/pricing/
- [23] ↩ Amazon Web Services, “Share your services through AWS PrivateLink,” AWS PrivateLink. Accessed: Oct. 7, 2026. [Online]. Available: https://docs.aws.amazon.com/vpc/latest/privatelink/privatelink-share-your-services.html
- [24] ↩ Amazon Web Services, “AWS PrivateLink concepts,” AWS PrivateLink. Accessed: Oct. 7, 2026. [Online]. Available: https://docs.aws.amazon.com/vpc/latest/privatelink/concepts.html
- [25] ↩ Amazon Web Services, “AWS PrivateLink,” Building a Scalable and Secure Multi-VPC AWS Network Infrastructure (AWS Whitepaper). Accessed: Oct. 7, 2026. [Online]. Available: https://docs.aws.amazon.com/whitepapers/latest/building-scalable-secure-multi-vpc-network-infrastructure/aws-privatelink.html
- [26] ↩ Amazon Web Services, “Rules and best practices for using AWS Client VPN,” AWS Client VPN Administrator Guide. Accessed: Oct. 7, 2026. [Online]. Available: https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/what-is-best-practices.html
- [27] ↩ Amazon Web Services, “What is AWS Client VPN?” AWS Client VPN Administrator Guide. Accessed: Oct. 7, 2026. [Online]. Available: https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/what-is.html
- [28] ↩ a b c Amazon Web Services, “Provide Client VPN users with access to AWS resources,” AWS re:Post Knowledge Center. Accessed: Oct. 5, 2026. [Online]. Available: https://repost.aws/knowledge-center/client-vpn-give-users-resource-access
- [29] ↩ Amazon Web Services, “AWS Client VPN authorization rules,” AWS Client VPN Administrator Guide. Accessed: Oct. 5, 2026. [Online]. Available: https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/cvpn-working-rules.html
- [30] ↩ a b c The Kubernetes Authors, “kubectl port-forward,” Kubernetes Documentation. Accessed: Oct. 7, 2026. [Online]. Available: https://kubernetes.io/docs/reference/kubectl/generated/kubectl_port-forward/
- [31] ↩ a b c d e The Kubernetes Authors, “kubectl run,” Kubernetes Documentation. Accessed: Oct. 7, 2026. [Online]. Available: https://kubernetes.io/docs/reference/kubectl/generated/kubectl_run/
- [32] ↩ alpine-docker, “socat/Dockerfile,” alpine-docker/multi-arch-libs, GitHub repository. Accessed: Oct. 7, 2026. [Online]. Available: https://github.com/alpine-docker/multi-arch-libs/blob/master/socat/Dockerfile
- [33] ↩ a b c G. Rieger, “socat - Multipurpose relay,” socat documentation. Accessed: Oct. 5, 2026. [Online]. Available: http://www.dest-unreach.org/socat/doc/socat.html
- [34] ↩ The Kubernetes Authors, “Use Port Forwarding to Access Applications in a Cluster,” Kubernetes Documentation. Accessed: Oct. 5, 2026. [Online]. Available: https://kubernetes.io/docs/tasks/access-application-cluster/port-forward-access-application-cluster/
- [35] ↩ a b The Kubernetes Authors, “Communication between Nodes and the Control Plane,” Kubernetes Documentation. Accessed: Oct. 5, 2026. [Online]. Available: https://kubernetes.io/docs/concepts/architecture/control-plane-node-communication/
- [36] ↩ a b c Amazon Web Services, “AWS Systems Manager Session Manager,” AWS Systems Manager User Guide. Accessed: Oct. 5, 2026. [Online]. Available: https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html
- [37] ↩ a b c d e Amazon Web Services, “Start a session,” AWS Systems Manager User Guide. Accessed: Oct. 5, 2026. [Online]. Available: https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-sessions-start.html
- [38] ↩ Amazon Web Services, “start-session,” AWS CLI Command Reference. Accessed: Oct. 7, 2026. [Online]. Available: https://docs.aws.amazon.com/cli/latest/reference/ssm/start-session.html
- [39] ↩ Amazon Web Services, “AWS Systems Manager Documents,” AWS Systems Manager User Guide. Accessed: Oct. 7, 2026. [Online]. Available: https://docs.aws.amazon.com/systems-manager/latest/userguide/documents.html
- [40] ↩ Amazon Web Services, “Step 1: Complete Session Manager prerequisites,” AWS Systems Manager User Guide. Accessed: Oct. 7, 2026. [Online]. Available: https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-prerequisites.html
- [41] ↩ Amazon Web Services, “Step 2: Verify or add instance permissions for Session Manager,” AWS Systems Manager User Guide. Accessed: Oct. 7, 2026. [Online]. Available: https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-getting-started-instance-profile.html
- [42] ↩ Microsoft, “about_Parsing,” PowerShell 7.5 Documentation, Jun. 29, 2026. Accessed: Oct. 6, 2026. [Online]. Available: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_parsing
- [43] ↩ The Kubernetes Authors, “Role Based Access Control Good Practices,” Kubernetes Documentation. Accessed: Oct. 8, 2026. [Online]. Available: https://kubernetes.io/docs/concepts/security/rbac-good-practices/
- [44] ↩ a b The PostgreSQL Global Development Group, “SSL Support,” PostgreSQL Documentation. Accessed: Oct. 8, 2026. [Online]. Available: https://www.postgresql.org/docs/current/libpq-ssl.html
- [45] ↩ a b Amazon Web Services, “Using SSL with a PostgreSQL DB instance,” Amazon RDS User Guide. Accessed: Oct. 8, 2026. [Online]. Available: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/PostgreSQL.Concepts.General.SSL.html
- [46] ↩ The Kubernetes Authors, “Network Policies,” Kubernetes Documentation. Accessed: Oct. 8, 2026. [Online]. Available: https://kubernetes.io/docs/concepts/services-networking/network-policies/
- [47] ↩ The Kubernetes Authors, “Images,” Kubernetes Documentation. Accessed: Oct. 8, 2026. [Online]. Available: https://kubernetes.io/docs/concepts/containers/images/
- [48] ↩ a b Amazon Web Services, “Working with private hosted zones,” Amazon Route 53 Developer Guide. Accessed: Oct. 5, 2026. [Online]. Available: https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/hosted-zones-private.html
- [49] ↩ a b Amazon Web Services, “Resolving DNS queries between VPCs and your network,” Amazon Route 53 Developer Guide. Accessed: Oct. 5, 2026. [Online]. Available: https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resolver-overview-DSN-queries-to-vpc.html
- [50] ↩ a b Amazon Web Services, “Forwarding inbound DNS queries to your VPCs,” Amazon Route 53 Developer Guide. Accessed: Oct. 8, 2026. [Online]. Available: https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resolver-forwarding-inbound-queries.html
- [51] ↩ a b Amazon Web Services, “How do I resolve resource records in my private hosted zone using Client VPN?” AWS re:Post Knowledge Center. Accessed: Oct. 8, 2026. [Online]. Available: https://repost.aws/knowledge-center/client-vpn-resolve-resource-records
Every source cited across the primers is collected on one page.