How does it work?
Recall the architecture:
graph LR
LE(("Let's<br>Encrypt")) --ACME--- bCaddy
p3>"p3.cse561.cfm.yers.net"] --CNAME--> front
ICMP>"ICMP"] --DNAT--> fWireGuard
HTTP>"HTTP<br>tcp/80"] --DNAT--> fWireGuard
HTTPS>"HTTPS<br>tcp/443"] --DNAT--> fWireGuard
HTTP/1337>"HTTP<br>tcp/1337"] --> OliveTin
subgraph front["front [1]"]
fWireGuard["WireGuard<br>10.0.0.2/30"] --> fUdp2Tcp["udp2tcp<br>udp/51280"] --> fSocat["socat<br>tcp/51280"] --> fTor["Tor<br>tcp/9050"]
OliveTin["OliveTin [3]"]
end
fTor -.-> bTor
subgraph "back [2]"
bTor["Tor"] --> bTcp2Udp["tcp2udp<br>tcp/51280"] --> bWireGuard["WireGuard<br>udp/51280<br>10.0.0.1/30"] --> bCaddy --> bMdBook
bTor -."ypg...xid.onion [4]".-> bWireGuard
bCaddy["Caddy<br>tcp/80<br>tcp/443"]
bMdBook["mdBook [5]<br>tcp/3000"]
end
Tunneling
back is a WireGuard server exposing WireGuard’s port 51280 only via the onion
service ypgjydaneoergw5mfvktrw24mbxflv545xd2zaehoklg3fm4ddh5mxid.onion.
front establishes a WireGuard tunnel to back:
sequenceDiagram autonumber participant you box front participant fIptables as iptables participant fWireGuard as wg0<br>10.0.0.2 participant fUdp2tcp as udp2tcp participant fSocat as socat participant fTor as tor end box back participant bTor as tor participant bTcp2udp as tcp2udp participant bWireGuard as wg0<br>10.0.0.1 end fWireGuard ->> fUdp2tcp: IP 127.0.0.1.54321 > 127.0.0.1.51280: UDP fUdp2tcp ->> fSocat: IP 127.0.0.1.54321 > 127.0.0.1.51280: TCP fSocat ->> fTor: IP 127.0.0.1.54321 > ypg...xid.onion.51280: TCP activate fTor fTor -->> bTor: Tor circuit via<br>onion-service rendezvous activate bTor bTor ->> bTcp2udp: IP 127.0.0.1.54321 > 127.0.0.1.51280: TCP bTcp2udp ->> bWireGuard: IP 127.0.0.1.54321 > 127.0.0.1.51280: UDP bWireGuard ->> bTcp2udp: IP 127.0.0.1.51280 > 127.0.0.1.54321: UDP bTcp2udp ->> bTor: IP 127.0.0.1.51280 > 127.0.0.1.54321: TCP deactivate bTor bTor -->> fTor: ... fTor ->> fSocat: IP ypg...xid.onion.51280 > 127.0.0.1.54321: TCP deactivate fTor fSocat ->> fUdp2tcp: IP 127.0.0.1.51280 > 127.0.0.1.54321: UDP fUdp2tcp ->> fWireGuard: IP 127.0.0.1.51280 > 127.0.0.1.54321: UDP
You may notice something a little funky here: Why is it okay for udp2tcp to
listen on UDP port 51280 and forward to socat on TCP port 51280? Per (at
least) RFC 6335 §7.2, each transport protocol technically has its own
multiplexting namespace, even if each draws their multiplexing keys from the
same numerical range of ports. So, as notated in the architecture diagram above,
udp2tcp and socat aren’t both listening on “port 51280” but on ports
udp/51280 and tcp/51820, respectively. This makes the translation they’re
performing more transparent.
Forwarding and NAT
Consider the path of a humble ICMP echo request you might send:
$ ping -c 1 p3.cse561.cfm.yers.net
sequenceDiagram autonumber participant you box front participant fIptables as iptables participant fWireGuard as wg0<br>10.0.0.2 participant fUdp2tcp as udp2tcp participant fSocat as socat participant fTor as tor end box back participant bTor as tor participant bTcp2udp as tcp2udp participant bWireGuard as wg0<br>10.0.0.1 end you ->> fIptables: IP A.B.C.D > 147.182.239.175: ICMP echo request fIptables ->> fWireGuard: IP 10.0.0.2 > 10.0.0.1: ICMP echo request activate fWireGuard Note over fWireGuard, bWireGuard: Tunneled as above: fWireGuard ->> bWireGuard: IP 10.0.0.2 > 10.0.0.1: ICMP echo request activate bWireGuard bWireGuard ->> fWireGuard: IP 10.0.0.1 > 10.0.0.2: ICMP echo reply deactivate bWireGuard fWireGuard ->> fIptables: IP 10.0.0.1 > 10.0.0.2: ICMP echo reply deactivate fWireGuard fIptables ->> you: IP 147.182.239.175 > A.B.C.D: ICMP echo reply
See “What” for how you can tell that the machine actually replying to your ICMP echo request is behind a Tor onion service.