Both tunnels connect, each client can reach the gateway, and the moment one tries the other's address the packets disappear. Nothing about either handshake is wrong, and no amount of reissuing keys changes it. The gateway is holding ordinary unencrypted IP packets between two of its own interfaces, so the protocols never need to understand each other; what they need is three things that no handshake provides. Destination coverage on the originating client, source ownership at the gateway, and a return path on the far side. The symptom is the same for all three, which is why they get diagnosed together.

The Symptom Has One Missing Piece at a Time

This design uses a WireGuard pool of 10.71.0.0/24 on wg1 and an OpenVPN pool of 10.72.0.0/24 on tun1, both terminating on one gateway. The request under test runs from 10.71.0.5 to HTTPS on 10.72.0.30. Every step below is about that one flow. Each check has a different failure signature even though the user experiences them identically, and only one of the three is usually missing at a time.

Start by confirming that both pools really are on this gateway and that forwarding is enabled in the namespace where the tunnels live. A gateway that terminates both protocols in different namespaces forwards nothing between them, and that is worth ruling out before reading any peer configuration.

Extend the Spoke's Destination Coverage

On the WireGuard client, the existing peer entry has to select the gateway for the OpenVPN destination, which means the destination prefix must appear in that peer's allowed list. Adding it does not create a second tunnel; it changes which peer the client chooses when it looks up that address, and with wg-quick the route normally follows the list automatically.

AllowedIPs = 10.71.0.0/24, 10.72.0.0/24

Keep the real key and endpoint lines untouched, and remember that a manager using Table = off takes no route from this list at all. In that case the same prefix has to be installed in the system's route table by whatever owns it, and a missing route there looks exactly like a missing allowed prefix.

Keep Source Ownership on the Gateway

The gateway's peer entry for this client should continue to own exactly that client's source address. Allowed addresses are checked in both directions: they choose a peer for outgoing traffic and they validate that an incoming packet's source belongs to the peer that sent it. Putting the OpenVPN pool into the spoke's entry because the spoke wants to reach it is the mistake that makes traffic head toward the wrong participant.

AllowedIPs = 10.71.0.5/32

Read that entry as a statement of ownership rather than a routing convenience. Anything else the client needs to reach belongs in the client's destination list, not in the gateway's idea of what this peer is allowed to send.

Give the OpenVPN Side a Return Route

The target now needs a route back to the WireGuard source, and for pooled clients that usually arrives as a pushed route from the OpenVPN server. Push it only to clients that should have it, and confirm on the client that it was accepted rather than assuming the directive was applied.

push "route 10.71.0.0 255.255.255.0"

Route distribution is not authorisation. When the destination sits behind another OpenVPN client instead of in the pool, that client's own LAN ownership and forwarding requirements join the design, and the pushed route is only the first half. Rephrase the requirement as a graph before adding directives: which node owns which prefix, and which node forwards toward it.

Permit the One Service You Meant to Permit

With forwarding enabled and both pools routable, the remaining question is policy. Write the rule for the specific flow rather than for the interface pair in general, preview it before applying, and keep the stateful return path so replies are not blocked as new connections. A dry run costs nothing and shows exactly what would be added.

sudo ufw --dry-run route allow in on wg1 out on tun1 proto tcp from 10.71.0.5 to 10.72.0.30 port 443

These are forwarded rules, distinct from the gateway's listener rules for the two VPN services. Opening the outer UDP ports does not authorise the inner request, and authorising the inner request does not require unrestricted SSH to the gateway or a default allow on forwarding. The target's own firewall and listening address also have to agree, since a service bound to loopback refuses the most carefully routed packet. Policy that only covers one direction of the conversation tends to fail with a successful request and a silent reply, which is the most confusing outcome of the three.

Reciprocal routes usually mean no source translation is needed, which preserves the WireGuard address at the target and makes logs readable. Do not add blanket masquerading because two protocols are involved, and do not remove an existing translation rule without knowing what depends on it. Then verify the single flow end to end, and test one disallowed source or port as well: a working handshake on each side proves neither the cross route nor the isolation.

འབྲེལ་བའི་རྩོམ་ཡིག