Nested IPsec is easier to reason about when the two layers terminate on different machines. Four gateways, two tunnels, and a single question that decides whether the design works: what does the outer tunnel actually see when the inner one has finished with the packet? Answer that and the selectors, the routes and the troubleshooting order follow. Get it wrong and you will spend a day debugging a tunnel that is technically up.

The four-gateway model

Outer gateways A and B build the first tunnel between the two sites. Behind A sits inner gateway C with transit address 10.60.0.2; behind B sits inner gateway D with transit address 10.70.0.2. The application networks live behind C and D, and every address in the example is deliberately non-overlapping.

application net -> C -> A == outer IPsec == B -> D -> application net
                       \---------- inner IPsec ----------/

C transit: 10.60.0.2      D transit: 10.70.0.2
A and B:    underlay addresses only

Splitting the layers across four hosts means each machine enforces one boundary. A single host running both layers has to reconcile two sets of policies, two route tables and two failure modes on one machine, which is possible and much harder to observe.

Each layer removes only its own protection

C encrypts an application packet for D. The result is addressed from C to D, and that is what reaches A. Everything downstream depends on that rewrite.

A encrypts it again for B. B decrypts the outer layer, sees a packet from C to D, and forwards it still encrypted. D removes the inner protection and delivers the application packet.

So A and B see the inner endpoints and their metadata, but they are not the parties that decrypt the application payload. That is a real property of the design and not a claim that C, D or the application hosts are trustworthy.

The outer selectors name the inner gateways

This is where most of these builds fail. For a policy-based outer tunnel, the selectors you negotiate have to contain the addresses A and B actually handle: 10.60.0.2/32 and 10.70.0.2/32, in the right local and remote order.

Putting the final application subnets in the outer selectors does not work, because by the time a packet reaches A the inner encapsulation has already rewritten what A must protect. The outer policy would then match nothing, and the tunnel would come up while carrying no data.

Do not narrow the outer permission to UDP 500 either. The inner connection has its own data path, which uses ESP or UDP encapsulation depending on what it negotiated, and the outer policy has to allow that protocol.

Check the result with swanctl --list-sas on each gateway and confirm the child security associations report the addresses you intended, not the ones you assumed. If the list shows the application subnets, the outer policy was written for the wrong layer. Fix the selectors before you debug routing, because routing will look fine and the counters will not move.

Forwarding and the recursion trap

Selectors do not replace routing. A and B must forward traffic between C and D in both directions, which means IP forwarding enabled, routes that point at the tunnel, and a return path that exists on the far side.

If the outer tunnel uses XFRM interfaces, routes select the interface while the negotiated policies still constrain what it carries. An interface does not widen what is protected; it only changes how the kernel picks the packet up.

The trap is recursion. A's route to B's outer endpoint must follow the underlay, not the tunnel A is establishing, or the encapsulation tries to travel inside itself and the connection never completes.

Inspect translation before adding layers

Look at any source NAT on A and B before you tune anything else. A rule that rewrites C's address before the IPsec policy applies can break the agreed selectors, and it can also change what D receives on the other side.

Broad masquerading is not made safer by having two layers. It hides which address the far end sees, which is exactly the information you need when the tunnel is up and the application is not.

If a NAT rule is genuinely required, write down which address each side believes it is talking to. That one line resolves most arguments about whether the tunnel or the translation is at fault. It also tells the next engineer where to look when the rule is removed a year later. A ping will not do any of that.

Prove fail-closed and check the MTU

A route disappearing is not a fail-closed policy by itself. Another table, a default route or a policy rule can quietly provide a fallback, and the only way to know is to take the outer tunnel down and observe what the application traffic does. Do that on purpose, in a window, rather than discovering it by accident.

Test three states: outer tunnel down, inner tunnel down, and recovery after both come back. Use policy counters at each gateway, because two established IKE sessions prove only that control plane negotiation succeeded.

Budget for both encapsulations too. Working IKE is a small-packet result, and full-size application packets can still fail once two headers are added; test with a known payload size on the real path instead of assuming the numbers add up. The arithmetic is easy and the path is not.

Artitgels cun il medem tema