URI:
       Directory listing for: tech/gopher/gopher_tor_server
   DIR Parent directory (..)
       
  TEXT README.txt (3.6KB, 2026-05-23):
       Same gopherhole served two ways at once: clearnet Gopher on port 70 and a Tor
       hidden service. The catch is that a Tor visitor must get *.onion* links back ---
       otherwise the first click dumps them onto the clearnet host. So the onion side
       runs through a tiny shim that rewrites menu links to the `.onion`.
       
       This descends from my earlier [reverse-proxy setup](/tech/gopher/gopher-routing).
       The change: Venusia now binds :70 directly for clearnet, and the rewriting shim
       sits only in front of Tor.
       
       
       ## Shape
       
       ```
       clearnet:  client ──────────────────────────────► Venusia :70   (xinetd not involved)
       onion:     Tor ─► :7072 (xinetd) ─► gopher_router.sh ─► nc :70 Venusia
                         rewrites menu-link host -> .onion
       ```
       
       
       ## Venusia binds :70 itself
       
       Nothing to do here: the current Venusia package ships a systemd unit that
       already binds privileged port 70 as the unprivileged `venusia` user. The trick
       is one line --- `AmbientCapabilities=CAP_NET_BIND_SERVICE` --- which lets the
       process bind a port < 1024 without root and, unlike `setcap` on the binary,
       survives package upgrades:
       
       ```
       User=venusia
       AmbientCapabilities=CAP_NET_BIND_SERVICE
       ExecStart=/usr/bin/venusia watch /var/gopher gopher.example.com 70
       ```
       
       The only thing you change is the `ExecStart` host --- point it at your own
       hostname (it's what Venusia prints in menu links). Check it with
       `ss -ltnp 'sport = :70'`.
       
       
       ## Tor side
       
       Clearnet bypasses everything below --- it hits Venusia on :70 directly. xinetd
       exists *only* to wrap the onion.
       
       torrc:
       
       ```
       HiddenServiceDir /var/lib/tor/someodd_gopher
       HiddenServicePort 70 127.0.0.1:7072
       ```
       
       The onion's port 70 forwards to `127.0.0.1:7072` --- *not* straight to Venusia
       on :70, because :7072 is where the rewriter lives. (Point it at :70 and Tor
       visitors get clearnet links.) Your address is in `.../someodd_gopher/hostname`.
       
       xinetd answers :7072 and runs the shim (file `gopher_onion`):
       
       ```
       service gopher_onion
       {
           type        = UNLISTED
           port        = 7072
           bind        = 127.0.0.1
           socket_type = stream
           protocol    = tcp
           wait        = no
           user        = root
           server      = /bin/bash
           server_args = /usr/local/bin/gopher_router.sh
           disable     = no
       }
       ```
       
       Apply with `sudo systemctl restart xinetd`.
       
       The shim (`gopher_router.sh` --- copy to `/usr/local/bin`, `chmod +x`, and set
       `HOST`/`ONION` to your own) forwards the request to Venusia with `nc localhost
       70` and pipes the reply
       through `sed --unbuffered`, rewriting the `gopher.someodd.zip` host to the
       `.onion`. `--unbuffered` keeps responses streaming instead of buffering until
       the backend closes.
       
       
       ## Tor erases the client IP
       
       Every onion request reaches xinetd from Tor's local forward, so `$REMOTE_IP` is
       always `127.0.0.1`. Anything keyed on the client address --- geolocation, per-IP
       limits, bans --- sees only loopback over Tor and won't work. You also can't use
       the peer IP to *detect* Tor --- local clearnet tools are loopback too.
       
       
       ## Testing
       
       ```
       printf '\r\n' | nc localhost 70   | head    # clearnet -> links say gopher.someodd.zip
       printf '\r\n' | nc localhost 7072 | head    # onion path -> links say .onion
       torsocks gopher $(sudo cat /var/lib/tor/someodd_gopher/hostname)   # end to end
       ```
       
       
       ## Caveat
       
       The shim runs every byte through `sed`, so it can mangle *binary* downloads over
       the onion (clearnet is a direct connection and unaffected). A cleaner rewriter
       would touch only menu (type 1) responses.
       
       
       ## Files here
       
         * torrc            --- the hidden-service mapping
         * gopher_onion     --- xinetd service for the onion side (:7072)
         * gopher_router.sh --- the link-rewriting shim
       
   BIN gopher_onion                                  587B  2026-05-23
   BIN gopher_router.sh                              932B  2026-05-23
   BIN torrc                                         379B  2026-05-23