Names stop resolving the moment the VPN connects, while a ping to a public address still answers and the firewall status shows nothing that looks wrong. Nothing changed in the policy; what changed is that a new interface appeared, and the default deny covers it from the first packet. UFW decides by direction and interface, so a rule written for the wireless link says nothing about traffic leaving the tunnel. The repair is a handful of narrow rules applied in a particular order.

The symptom appears only after the tunnel is up

A default-deny policy is doing exactly what it was told: anything without a matching rule is refused, on every interface that exists. The tunnel did not exist when the rules were written, so none of them can match it, and the deny that was always there now applies to name resolution. Direct traffic to a literal address can still work when a broader allow covers it, which is why pinging a public resolver succeeds while looking up a name fails. The gap is a rule for an interface the policy has never seen.

That also explains why the failure reverses cleanly: drop the tunnel and names resolve again, because the query returns to an interface that does have rules. A policy that only fails while the tunnel is up is not a DNS problem, and tuning the resolver itself wastes the most time here. Read the interface list before reading the resolver configuration.

Three directions, three different rule sets

A single session touches rules in more than one direction, and a tunnel adds a second interface to almost every one of them. Sorting the traffic by direction and interface first makes the rest mechanical, because each row below needs its own rule in its own place. The examples use tun0 and a pool of 10.8.0.0/24, so substitute the names and ranges from the client's own output.

DirectionExample packetRule that admits itWritten on
Client to the pushed resolverA DNS query to 10.8.0.1Destination port 53 over the tunnelThe client's tun0
Client to the VPN serverA handshake to the server's public addressServer address and port on the physical linkThe client's physical interface
Server to a serviceSSH from the pool to port 22Source pool, destination port, tunnel interfaceThe server's tun0
Anyone to the listenerThe VPN listener itself1194/udp on the public sideThe server's public interface

Two rows are commonly confused because both mention the tunnel. A rule for the resolver admits traffic the client sends into the tunnel, while a rule for SSH admits traffic the server receives from it; they live on different machines and fail independently. The row that is most often forgotten is the second, because the client cannot establish the tunnel at all until its traffic to the server's public address is allowed. Work top to bottom and every failure has one candidate cause.

A pushed resolver needs a rule of its own

Whether the tunnel carries DNS depends on what the server sends. A server that pushes a resolver option installs a private address in the client's configuration, and that address is routable only through the tunnel. If the server pushes nothing, the client keeps the resolver it already had and the rules below have nothing to admit. Check the pushed state and the current query result before writing anything:

dig +short @10.8.0.1 example.com
resolvectl status tun0
sudo ufw status numbered

The status output names the resolver in use and the link it is bound to, and the route lookup behind it shows which interface would carry a query to that address. When the resolver sits on another link, the problem is earlier than the firewall and no rule will help. When both agree on the tunnel, two rules are needed, because UDP carries most queries and TCP takes over when an answer is truncated. Record both values, since they are the ones the rules will name:

sudo ufw allow out on tun0 to 10.8.0.1 port 53 proto udp comment 'pushed resolver'
sudo ufw allow out on tun0 to 10.8.0.1 port 53 proto tcp comment 'truncated answers'

Open the outer path before the inner one

The client has to reach the server's public address before a tunnel exists, so the rule for it sits on the physical link and has to be in place first. A policy that admits the resolver but forgets the server leaves the client unable to build the tunnel at all, and that symptom usually gets blamed on a kill switch. Then admit the services you actually wanted through the tunnel, one narrow rule at a time, rather than opening the interface wholesale. The narrow rules are the reason the deny can stay in place.

sudo ufw allow out to 198.51.100.10 port 1194 proto udp comment 'vpn server'
sudo ufw allow out on tun0 to any port 443 proto tcp comment 'web through tunnel'

Order matters while you test, because the first rule is what lets the tunnel come up at all. Add it, connect, and confirm the tunnel is up before adding the service rule, since doing both at once means a failure tells you nothing about which half is wrong. Substitute the endpoint the client is configured to use for the documentation address above.

Finish the client side by disconnecting on purpose. The deny should still refuse the traffic you deliberately excluded, because a resolver that answers while the tunnel is up covers only half of a fail-closed policy.

A client that connects is not yet admitted

The server has its own direction to consider, and a client that completes the handshake still cannot reach anything running on the server until the server admits it. The public listener rule only lets the tunnel exist; it does not let the tunnel's traffic in. On the server that is two rules plus one check that the service is actually listening:

sudo ufw allow 1194/udp comment 'openvpn listener'
sudo ufw limit in on tun0 from 10.8.0.0/24 to any port 22 proto tcp comment 'admin over tunnel'
ss -ltn | grep ':22'

The pool in the rule must equal the range the server allocates, and the tun number must match the one the server created, because a rule for a different interface is indistinguishable from no rule. limit is deliberate for an administrative port, since it throttles repeated connections instead of admitting them without bound. The listener check separates a closed port from a dropped packet, which look identical from the client. Numbered rules are not the whole picture either: UFW's before-rules and any hand-written nftables lines match earlier and can still refuse a packet the list appears to allow.

Nsɛm a ɛfa ho