One gateway runs WireGuard for your own devices and an OpenVPN client to somebody else's network. Forwarding between the two inner interfaces works, and it is easy to conclude that the downstream subnet is now part of the upstream service. It is not. A route pulled by the client tells your gateway where to send requests; it says nothing about how the remote network returns traffic to devices it has never heard of. Pick a return model the upstream actually supports before you add a single rule.

Decide the return model before you touch routing

For the walkthrough, downstream clients use 10.90.0.0/24, the gateway's assigned upstream tunnel address is 10.80.0.6, and the approved remote resource is 172.20.0.30. Two models fit that shape: preserve the downstream source and get the far side to route it, or translate the source to the gateway's own tunnel address. Everything downstream of this decision is mechanical, and the decision itself needs the service's owner. One row above is a routing agreement with the far side; the other is a local policy change you can make alone. Confusing the two is what turns a ten-minute change into a negotiation.

preserved:  10.90.0.23 -> 172.20.0.30
translated: 10.80.0.6  -> 172.20.0.30

Pick one row and commit to it. The rows are alternatives, not steps, and mixing them produces a design nobody can explain at review.

Routed source preservation

The far side receives a request from a downstream address, so it needs a supported route and peer ownership for that subnet, plus authorization to accept it. Those are remote settings, and no local Linux route creates them. Ask for them explicitly, in writing, with the prefix and the tunnel peer that will carry it, and expect the answer to depend on whether the service supports site-to-site arrangements at all.

If the answer is yes, the local configuration is small: forwarding enabled, the WireGuard peer allowed-ips covering the downstream range, and a firewall policy that permits the crossing in both directions.

Deliberate source translation

If the service returns traffic only to the connected client address, translate the source to that address on the way out and keep the conntrack state that reverses it for replies. Scope the rule to the intended downstream sources, the intended remote destinations and the upstream interface. A rule that translates everything leaving every interface is a much larger policy than this requirement, and it will surprise you later.

IPv6 needs its own design; copying an IPv4 masquerade line is not an architecture. Test the translated path with a fresh connection rather than a cached one.

The local data path still has to work

Translation does not remove the local work: the gateway still needs a route for downstream destinations, the correct WireGuard peer source ownership, forwarding enabled, and a narrow cross-interface firewall policy. A request generated on the gateway itself exercises none of those permissions, which is why gateway-local tests pass and downstream devices still fail.

Test from an actual downstream device, not from the gateway. The source address is the whole point of the exercise.

One source means one session identity

The remote service sees the gateway's authenticated session and its translated address, not independent authentication for each downstream device. Keep per-device permissions and application identities explicit, and confirm with the service's owner that a gateway acting for several devices is permitted at all. Translation is not a way to bypass a per-device enrollment requirement; if the service requires each device to enroll, use that model or negotiate a site-to-site arrangement.

Write that confirmation into the runbook next to the rule, because the next reviewer will ask whether the design was approved or merely convenient.

Define the disconnected state, then test three requests

Decide what happens when the upstream tunnel drops. Authorized remote traffic should not silently follow an ordinary WAN fallback where that is prohibited, so the firewall policy that permits the crossing should also fail closed. Then test three cases from a real downstream device: an allowed request that works, a disallowed request that fails, and an upstream failure that stops the allowed one.

Record the rollback in one line per rule, so the change can be undone without reconstructing intent.

Nsɛm a ɛfa ho