Files
fips_setup/plans/laantungir-tor-fips-plan.md
2026-04-11 20:40:01 -04:00

16 KiB

laantungir.net: Tor + FIPS + Clearnet Access Plan

Current Server State (discovered via SSH)

Component Status
OS Ubuntu 24.04.3 LTS (VM410)
nginx 1.24.0, running, with RTMP module
Tor 0.4.8.10, running, SOCKS on 127.0.0.1:9050
Onion service kn2jam4kyz6s5wacyozo3do3d2zsvh4to45uh7xliulwdust6zjuvnad.onion — forwards ports 80/443 to 127.0.0.1:80/443
FIPS Not installed
Nostr relay c-relay-pg (C Nostr Relay, PostgreSQL backend) on port 8888
Blossom didactyl.service on 127.0.0.1:8484
Website Static site served by nginx
Gitea Port 3000 (web) + 2222 (SSH)
PostgreSQL 18-main on 127.0.0.1:5432
RTMP nginx-rtmp on port 1935 with auth + HLS
Streaming OBS → RTMP :1935/publish (auth via rtmp-auth.service on :8091) → ffmpeg forward → :1935/live → HLS at /var/www/html/hls/
Stream stats stream-stats.service on 127.0.0.1:8092, nginx internal at 127.0.0.1:8080/stat
simon.service 127.0.0.1:8485 (unknown purpose)

Video Streaming Architecture

OBS (remote)
    │
    │  RTMP push to :1935/publish/{show}/{variant}/{secret_key}
    │
    ▼
nginx-rtmp :1935
    ├── on_publish → 127.0.0.1:8091/auth (rtmp-auth.service, validates secret key)
    ├── exec_push → /usr/local/bin/rtmp-forward.sh
    │       └── ffmpeg -c copy → rtmp://127.0.0.1:1935/live/{show}/{variant}
    │
    ▼
nginx-rtmp :1935/live
    ├── HLS output → /var/www/html/hls/{show}/{variant}/index.m3u8
    ├── generate-master-playlist.py → /var/www/html/hls/{show}/master.m3u8
    └── RTMP playback (allow play all)

Viewer access (clearnet): https://laantungir.net/hls/{show}/{variant}/index.m3u8 (HLS) or rtmp://laantungir.net/live/{show}/{variant} (RTMP direct)

Current shows in HLS: beat-topia, dub-techno, music, the-world-is-coming-to-an-end

Current nginx virtual hosts (from /etc/nginx/conf.d/default.conf)

Domain Port 80 Port 443 Backend
laantungir.net (+ .com, .org, www) Redirect to HTTPS TLS, static site Local files
relay.laantungir.net (+ .com, .org) Redirect to HTTPS TLS, WebSocket proxy 127.0.0.1:8888 (c-relay-pg)
blossom.laantungir.net Redirect to HTTPS TLS, proxy 127.0.0.1:8484 (didactyl)
git.laantungir.net (+ .com, .org) Redirect to HTTPS TLS, proxy 127.0.0.1:3000 (gitea)
auth.laantungir.net (+ .com, .org) Redirect to HTTPS TLS, proxy 127.0.0.1:3000 (gitea)

Current Tor onion service

HiddenServiceDir /var/lib/tor/nostr_client/
HiddenServicePort 80 127.0.0.1:80
HiddenServicePort 443 127.0.0.1:443

Onion address: kn2jam4kyz6s5wacyozo3do3d2zsvh4to45uh7xliulwdust6zjuvnad.onion

Problem: The onion service forwards to nginx ports 80/443, but nginx virtual hosts use server_name matching. Tor clients send the .onion hostname, which doesn't match any server_name directive — so nginx falls through to the default server block. This needs to be fixed.


Target Architecture

                    ┌─────────────────────────────────────────────────────────┐
                    │                  laantungir.net (VM410)                  │
                    │                                                         │
  Clearnet user ───►│─── eth0 :80/:443 ──► nginx ──┬── / ──► static site     │
                    │                               ├── relay ──► :8888       │
                    │                               └── blossom ──► :8484     │
                    │                                                         │
  Tor user ────────►│─── tor :80/:443 ──► nginx ──┬── / ──► static site      │
                    │    (via .onion)              ├── relay ──► :8888        │
                    │                              └── blossom ──► :8484     │
                    │                                                         │
  FIPS user ───────►│─── fips0 :80 ──► nginx ──┬── / ──► static site         │
                    │    (via fd00::/8)         ├── relay ──► :8888           │
                    │                           └── blossom ──► :8484        │
                    │                                                         │
  FIPS+Tor user ───►│─── tor transport ──► fips0 ──► nginx (same as FIPS)    │
                    │    (FIPS peering over Tor)                              │
                    │                                                         │
                    │  FIPS daemon: UDP :2121, TCP :8443, Tor .onion:8443     │
                    └─────────────────────────────────────────────────────────┘

How each access path works

Path User needs Connects via Encryption
Clearnet Browser https://relay.laantungir.net TLS (Let's Encrypt)
Tor Tor Browser http://kn2jam...nad.onion/relay/ or subdomain routing Tor onion encryption
FIPS FIPS node ws://npub1<server-npub>.fips Noise IK + Noise XK (double)
FIPS over Tor FIPS node + Tor FIPS peers via Tor transport Tor + Noise IK + Noise XK (triple)

Implementation Plan

Phase 1: Fix Tor Onion Service Routing

The existing onion service already forwards ports 80/443 to nginx, but nginx doesn't know how to route .onion hostnames. Two approaches:

Option A: Path-based routing (single .onion, recommended)

Add the .onion hostname to each server_name directive:

# In the relay server block:
server_name relay.laantungir.com relay.laantungir.net relay.laantungir.org
            kn2jam4kyz6s5wacyozo3do3d2zsvh4to45uh7xliulwdust6zjuvnad.onion;

Tor users would access:

  • Website: http://kn2jam...nad.onion/
  • Relay: ws://kn2jam...nad.onion/ (if relay is the default server)
  • Blossom: http://kn2jam...nad.onion/blossom/ (needs path-based routing)

Problem: relay and blossom are separate subdomains, not paths. With a single .onion, we need a way to distinguish them.

Option B: Separate onion services per service (cleaner)

Create three onion services in torrc:

# Website
HiddenServiceDir /var/lib/tor/hs_website/
HiddenServicePort 80 127.0.0.1:80
HiddenServicePort 443 127.0.0.1:443

# Relay
HiddenServiceDir /var/lib/tor/hs_relay/
HiddenServicePort 80 127.0.0.1:80
HiddenServicePort 443 127.0.0.1:443

# Blossom
HiddenServiceDir /var/lib/tor/hs_blossom/
HiddenServicePort 80 127.0.0.1:80
HiddenServicePort 443 127.0.0.1:443

Then add each .onion to the corresponding nginx server_name. Each service gets its own .onion address. Tor users connect to the right service by using the right .onion.

Option C: Single .onion with default-server routing (simplest)

Make the relay the default server (it's the most important service), and add the .onion to the website and blossom blocks too. Use a port-based split:

HiddenServiceDir /var/lib/tor/nostr_client/
HiddenServicePort 80 127.0.0.1:80      # website
HiddenServicePort 443 127.0.0.1:443    # website (TLS)
HiddenServicePort 7777 127.0.0.1:8888  # relay direct (no nginx)
HiddenServicePort 8484 127.0.0.1:8484  # blossom direct (no nginx)

Tor users connect to different ports on the same .onion:

  • http://kn2jam...nad.onion/ → website
  • ws://kn2jam...nad.onion:7777/ → relay (direct to c-relay-pg)
  • http://kn2jam...nad.onion:8484/ → blossom (direct to didactyl)

Decision: Option B — Separate .onion per service

This gives the best compatibility with existing programs. Nostr clients expect to connect to a relay at a WebSocket URL like ws://host/. Blossom clients expect http://host/. Separate .onion addresses mean each service gets its own hostname — no path rewriting, no port gymnastics. Existing clients work unmodified.

Steps for Phase 1

  1. Add three HiddenService blocks to /etc/tor/torrc (website, relay, blossom)
  2. Add each .onion hostname to the corresponding nginx server_name directive
  3. Add plain-HTTP server blocks for .onion access (Tor already encrypts; no TLS needed)
  4. Restart Tor (generates new onion keys), then restart nginx
  5. Test from Tor Browser

Phase 2: Install and Configure FIPS

2.1 Build and install FIPS

# Install Rust toolchain
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env

# Install build deps for BLE (optional, can skip with --no-default-features)
sudo apt install -y bluez libdbus-1-dev

# Clone and build
git clone https://github.com/jmcorgan/fips.git
cd fips
cargo build --release

# Install via .deb
cargo install cargo-deb
cargo deb
sudo dpkg -i target/debian/fips_*.deb

2.2 Configure FIPS

Edit /etc/fips/fips.yaml:

node:
  identity:
    persistent: true    # Stable npub for the server

tun:
  enabled: true
  name: fips0
  mtu: 1280

dns:
  enabled: true
  bind_addr: "127.0.0.1"
  port: 5354

transports:
  udp:
    bind_addr: "0.0.0.0:2121"
  tcp:
    bind_addr: "0.0.0.0:8443"
  tor:
    mode: "directory"
    socks_addr: "127.0.0.1:9050"
    directory_service:
      hostname_file: "/var/lib/tor/fips_mesh/hostname"
      bind_addr: "127.0.0.1:9443"

peers:
  - npub: "npub1qmc3cvfz0yu2hx96nq3gp55zdan2qclealn7xshgr448d3nh6lks7zel98"
    alias: "fips-test-node"
    addresses:
      - transport: udp
        addr: "217.77.8.91:2121"
    connect_policy: auto_connect
  - npub: "npub1zv58cn7v83mxvttl70w5fwjwuclfmntv9cnmv5wmz2nzz88u5urqvdx96n"
    alias: "fips.v0l.io"
    addresses:
      - transport: udp
        addr: "fips.v0l.io:2121"
    connect_policy: auto_connect

2.3 Add FIPS Tor onion service to torrc

Add a separate onion service for FIPS mesh peering:

# FIPS mesh peering (separate from web services)
HiddenServiceDir /var/lib/tor/fips_mesh/
HiddenServicePort 9443 127.0.0.1:9443

2.4 Open firewall for FIPS

sudo ufw allow 2121/udp comment "FIPS mesh UDP"
sudo ufw allow 8443/tcp comment "FIPS mesh TCP"

2.5 Start FIPS

sudo systemctl enable --now fips
sudo systemctl enable --now fips-dns

2.6 Configure DNS for .fips resolution on the server

sudo resolvectl dns fips0 127.0.0.1:5354
sudo resolvectl domain fips0 ~fips

Phase 3: Make Services Reachable Over FIPS

3.1 Make nginx listen on IPv6 (for FIPS fd00::/8 traffic)

Add listen [::]:80; and listen [::]:443 ssl; to each server block in /etc/nginx/conf.d/default.conf. This allows FIPS traffic (which arrives as IPv6 on the fips0 TUN interface) to reach nginx.

3.2 Decision: Separate server blocks per service (matching Tor approach)

For maximum compatibility with existing programs, we use the same approach as Tor: each service gets its own server block matched by hostname. FIPS users use different .fips hostnames (via /etc/fips/hosts) to reach each service — just like Tor users use different .onion addresses.

Since blossom (didactyl) is a FastCGI extension of nginx, all three services must go through nginx.

# FIPS: Website
server {
    listen [::]:80;
    server_name ~^npub1.+\.fips$ laantungir.fips;

    # Same config as the clearnet website server block
    # (static site root, locations, etc.)
}

# FIPS: Relay
server {
    listen [::]:80;
    server_name relay.fips;

    location / {
        proxy_pass http://127.0.0.1:8888;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}

# FIPS: Blossom
server {
    listen [::]:80;
    server_name blossom.fips;

    # Same FastCGI config as the clearnet blossom server block
}

FIPS users access:

  • http://laantungir.fips/ → website
  • ws://relay.fips/ → Nostr relay (standard WebSocket URL, works with all Nostr clients)
  • http://blossom.fips/ → Blossom (standard HTTP, works with all Blossom clients)

The direct npub also works: http://npub1<server-npub>.fips/ → website (default)

3.3 Add /etc/fips/hosts entries

laantungir  npub1<server-npub>
relay       npub1<server-npub>
blossom     npub1<server-npub>

This lets FIPS users access:

  • http://laantungir.fips/ → website
  • ws://relay.fips/ → relay (if using path-based routing)
  • http://blossom.fips/ → blossom

Phase 4: Enable FIPS-over-Tor (Combined)

This is already handled by Phase 2.3. The FIPS config includes a Tor transport with a dedicated onion service for mesh peering. Remote FIPS nodes can peer with this server through Tor by adding:

# On the remote FIPS node
peers:
  - npub: "npub1<server-npub>"
    alias: "laantungir"
    addresses:
      - transport: tor
        addr: "<fips-mesh-onion>.onion:9443"
    connect_policy: auto_connect

The remote node's FIPS traffic goes through Tor to reach this server's FIPS daemon, then the FIPS session provides end-to-end encryption on top. Triple encryption: Tor + Noise IK (hop) + Noise XK (session).


Summary of Access Paths After Implementation

Website (laantungir.net)

Path URL Encryption
Clearnet https://laantungir.net TLS
Tor http://kn2jam...nad.onion/ Tor
FIPS http://npub1<npub>.fips/ or http://laantungir.fips/ Noise IK+XK
FIPS+Tor Same as FIPS (transport is transparent) Tor + Noise IK+XK

Nostr Relay (relay.laantungir.net)

Path URL Encryption
Clearnet wss://relay.laantungir.net TLS
Tor ws://<relay-onion>.onion/ Tor
FIPS ws://relay.fips/ Noise IK+XK
FIPS+Tor Same as FIPS (transport is transparent) Tor + Noise IK+XK

Blossom (blossom.laantungir.net)

Path URL Encryption
Clearnet https://blossom.laantungir.net TLS
Tor http://<blossom-onion>.onion/ Tor
FIPS http://blossom.fips/ Noise IK+XK
FIPS+Tor Same as FIPS (transport is transparent) Tor + Noise IK+XK

Video Streaming (laantungir.net/hls/)

Path URL Encryption Notes
Clearnet https://laantungir.net/hls/{show}/master.m3u8 TLS HLS playback via static files
Clearnet RTMP rtmp://laantungir.net/live/{show}/{variant} None Direct RTMP playback
Tor http://<website-onion>.onion/hls/{show}/master.m3u8 Tor HLS over Tor (same website .onion)
FIPS http://laantungir.fips/hls/{show}/master.m3u8 Noise IK+XK HLS over FIPS
FIPS+Tor Same as FIPS Tor + Noise IK+XK HLS over FIPS+Tor

Streaming considerations:

  • HLS playback (HTTP-based) works naturally over Tor and FIPS — it's just static .m3u8 and .ts files served by nginx. No special config needed beyond the website server block.
  • RTMP ingest (OBS publishing on port 1935) stays clearnet-only. RTMP is a TCP protocol on a non-HTTP port; exposing it over Tor or FIPS would require additional transport config and is not needed — the publisher (you) controls the ingest path.
  • RTMP direct playback (port 1935/live) is clearnet-only for the same reason. Viewers over Tor/FIPS use HLS instead.
  • Stream stats (127.0.0.1:8092) and RTMP auth (127.0.0.1:8091) are internal-only and not exposed on any path.

Decisions (Resolved)

  1. Tor routing: Option B — separate .onion per service. Best compatibility with existing clients.
  2. FIPS service routing: Separate .fips hostnames per service via /etc/fips/hosts. Best compatibility with existing clients.
  3. Blossom: Stays behind nginx (it's a FastCGI extension, must go through nginx).
  4. Bootstrap peers: fips-test-node (217.77.8.91:2121) + fips.v0l.io (fips.v0l.io:2121).

Execution Order

  1. Fix Tor onion routing (nginx server_name + torrc)
  2. Test Tor access from Tor Browser
  3. Build and install FIPS
  4. Configure FIPS with persistent identity + Tor transport
  5. Add FIPS onion service to torrc, restart Tor
  6. Open firewall ports for FIPS
  7. Start FIPS, verify fips0 interface and peer connectivity
  8. Update nginx to listen on IPv6 and handle FIPS traffic
  9. Test FIPS access from another FIPS node
  10. Test FIPS-over-Tor access
  11. Publish server npub and .onion addresses