Andrew Mercer

DNS Encryption Audit via Passive LAN Tap (OPNsense ↔ ISP Modem)

Guide for using a passive Ethernet tap ("ninja star" / Throwing Star LAN Tap style) to verify whether DNS traffic leaving your network toward your ISP is actually encrypted.


1. What the Tap Gives You

A passive Ethernet tap splits the TX/RX pairs onto separate monitor ports.

  • Passive, one direction per port. No power needed on cheap resistor-based taps, but each monitor port only sees traffic in one direction. Capture from both monitor ports simultaneously to see full-duplex traffic.
  • Speed limits. Cheap resistor taps are often 10/100 only. On Gigabit WAN links you may need to force the link down to 100baseTX <full-duplex> to get a reliable passive tap. Skip this if you have an active/gig-capable tap.
  • Placement. Insert the tap between the OPNsense WAN interface and the ISP modem, so you capture OPNsense's actual egress traffic — after any outbound rules/NAT, i.e., ground truth of what's hitting the wire.
[Modem] <--> [Tap Port A/B] <--> [OPNsense WAN NIC]
                   |    |
              Monitor1 Monitor2
                   |    |
              [Capture Laptop - 2 NICs, or 2 capture devices]

If your capture machine only has one NIC:
- Add a USB-to-Ethernet adapter for the second monitor port, or
- Capture only the modem-facing (upstream) monitor port — sufficient for the DNS-encryption question, since it shows exactly what leaves toward your ISP.

If you need to force 100Mbps: OPNsense → Interfaces > [WAN] > Speed and Duplex → set to 100baseTX <full-duplex>, run your capture, then set back to autoselect.


2. Capture the Traffic

# identify the interface connected to the tap
ip link show

# capture everything, write to file for later analysis in Wireshark
sudo tcpdump -i eth1 -w dns_audit.pcap -s 0

# or filter live to just DNS-relevant ports
sudo tcpdump -i eth1 -w dns_audit.pcap -s 0 'port 53 or port 853 or port 443'

Let it run while generating DNS activity from inside your network (browse a few sites, or trigger manual lookups — see Section 5).


3. What Encrypted DNS Looks Like on the Wire

Protocol Port Wireshark signature
Plain DNS UDP/TCP 53 Protocol column shows DNS; query names readable in plaintext (Standard query A example.com)
DNS-over-TLS (DoT) TCP 853 Protocol shows TLS; handshake visible, then encrypted application data — no query names visible
DNS-over-HTTPS (DoH) TCP 443 Protocol shows TLS/HTTP2; indistinguishable from normal HTTPS by port alone — check SNI in ClientHello

The real test: any traffic on port 53 leaving toward the WAN is plaintext DNS, regardless of what your resolver config claims.


4. One-Shot Summary Command (tshark)

This scans the whole capture and prints any plaintext DNS query names found, plus a breakdown of DoT/DoH/plain-53 traffic — no manual paging through Wireshark required.

# 1. Any plaintext DNS queries leaving the WAN side (the critical leak check)
tshark -r dns_audit.pcap -Y "dns.flags.response == 0" \
  -T fields -e frame.time -e ip.src -e ip.dst -e dns.qry.name \
  > plaintext_dns_queries.txt

echo "=== Plaintext DNS queries found: ==="
wc -l < plaintext_dns_queries.txt
cat plaintext_dns_queries.txt

# 2. DoT sessions and their SNI (confirms who you're actually talking to over 853)
echo "=== DoT (port 853) sessions and SNI: ==="
tshark -r dns_audit.pcap -Y "tcp.port == 853 && tls.handshake.type == 1" \
  -T fields -e frame.time -e ip.dst -e tls.handshake.extensions_server_name

# 3. DoH candidates on 443 (SNI matching common DoH providers)
echo "=== Possible DoH (port 443) sessions: ==="
tshark -r dns_audit.pcap -Y "tcp.port == 443 && tls.handshake.type == 1 && \
  (tls.handshake.extensions_server_name contains \"dns\" || \
   tls.handshake.extensions_server_name == \"1.1.1.1\" || \
   tls.handshake.extensions_server_name == \"dns.google\" || \
   tls.handshake.extensions_server_name == \"cloudflare-dns.com\")" \
  -T fields -e frame.time -e ip.dst -e tls.handshake.extensions_server_name

# 4. Raw counts, for a quick pass/fail
echo "=== Packet counts by category: ==="
echo -n "Plain DNS (53): "; tshark -r dns_audit.pcap -Y "udp.port==53 or tcp.port==53" -T fields -e frame.number | wc -l
echo -n "DoT (853):      "; tshark -r dns_audit.pcap -Y "tcp.port==853" -T fields -e frame.number | wc -l
echo -n "TLS/443 total:  "; tshark -r dns_audit.pcap -Y "tcp.port==443 && tls" -T fields -e frame.number | wc -l

Reading the output:
- plaintext_dns_queries.txt non-empty → you have a leak. The file shows exactly which queries and which source IP sent them, so you can trace it to a specific device/service.
- DoT section shows SNI not matching your configured resolver → misconfigured forwarder or unexpected upstream.
- DoH section populated → some client (often a browser with built-in DoH) is doing encrypted DNS outside your local resolver — not a leak, but worth knowing about since it bypasses local logging.


5. Verification Workflow (Wireshark GUI)

Open dns_audit.pcap and apply these filters in sequence:

a. Plaintext leakage (most important):

dns

Any hits with a destination outside your LAN = unencrypted DNS leaving your network.

b. DoT:

tcp.port == 853

Right-click a stream → Follow → TLS Stream. Should show a TLS handshake with no cleartext DNS payload. Check tls.handshake.extensions_server_name in the ClientHello to confirm the SNI matches your intended DoT resolver.

c. DoH:

tls.handshake.extensions_server_name contains "dns"

or target known providers directly:

tls.handshake.extensions_server_name == "cloudflare-dns.com" or tls.handshake.extensions_server_name == "dns.google" or tls.handshake.extensions_server_name == "1.1.1.1"

d. Confirm the fix:

udp.port == 53 or tcp.port == 53

Should return zero packets on the WAN-side capture once DNS is fully encrypted and non-leaking.


6. Generate Known Lookups for Correlation

While capturing, trigger deliberate lookups so you have known timestamps to search for:

dig @<your-configured-resolver> example.com

7. Cross-Reference Against OPNsense Config

  • Services > Unbound DNS > General — confirm "DNS over TLS" is enabled for outgoing queries, and check the forwarding server format.
  • Services > Unbound DNS > Query Forwarding — verify forwarding targets use IP@853 (DoT), not the default port 53. This is the most common misconfig: DoT "enabled" in name only, still forwarding on 53.
  • Confirm no other service (container, IoT device, app with hardcoded resolver) is bypassing Unbound and going straight to 8.8.8.8:53 on the WAN — the pcap catches this even when the OPNsense config looks correct.

8. Common Findings

Observation Meaning
Zero UDP/TCP 53 packets on WAN-side capture DNS is encrypted end-to-end, working as intended
Plaintext query names visible on port 53 Unbound isn't actually forwarding over TLS (check @853 syntax), or a LAN client is bypassing your resolver
TLS on 853 but SNI doesn't match configured resolver Misconfigured forwarder, or worth checking for unexpected MITM/rogue CA
Unexpected DoH-shaped TLS on 443 Likely a browser (Firefox/Chrome ship DoH defaults) doing its own encrypted DNS independent of OPNsense — not a leak, but bypasses local resolver/logging

Tap type assumed: passive resistor-based ("Throwing Star" style). Adjust the 100Mbps forcing step if using a gig-capable active tap.