halkyon/deploy/forward/haproxy.cfg
Tizian 「ティツィアーン」 29844c7fa6
feat(deploy): halkyon.li's forward through ramiel and its host at home (ADR 0039)
ramiel's HAProxy passes halkyon.li's TLS connections, unopened, through a
WireGuard tunnel to the instance at home, with the visitor's address in a
PROXY header, and everything else to ramiel's own Caddy, moved to the
loopback. Each end's nftables table lets the tunnel carry HAProxy's 443 and
the SSH jump's 22 alone, and nothing from home to ramiel.

The host at home: sshd with keys alone, the root disk unlocked over SSH at
boot (dropbear-initramfs, D20), Docker's and containerd's data bind-mounted
from the HDD, and home's end of the tunnel before Docker. An instance behind
the forward (compose.forwarded.yaml) publishes 443 on the tunnel's address
alone, trusts the PROXY header from ramiel alone, and gets its certificates
through TLS-ALPN, without HTTP/3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-08 19:59:39 +01:00

45 lines
1.8 KiB
INI

# ramiel's front on 443 (ADR 0039, D3, D10): HAProxy reads the name a TLS connection
# asks for (SNI) and passes halkyon.li's, unopened, through the tunnel to its instance
# at home; every other name goes to ramiel's own Caddy on the loopback, which serves
# code.halkyon.app and halkyon.app as before. Both get the visitor's address in a PROXY
# header (v2). As /etc/haproxy/haproxy.cfg on ramiel; check it with
# `haproxy -c -f /etc/haproxy/haproxy.cfg`. Setting it up: forward/README.md.
#
# Nothing is logged: no `log` line, so neither the visitors of halkyon.li nor those of
# the forge are kept here (Caddy behind each side logs what it logs, as before).
global
user haproxy
group haproxy
daemon
maxconn 2000
defaults
mode tcp
timeout connect 5s
# Jellyfin's streams, Serverpod's WebSockets and a forge's long clones stay open:
# an hour without a byte either way, as a tunnel's idle time.
timeout client 1h
timeout server 1h
timeout tunnel 1h
frontend tls
bind :::443 v4v6
# Wait for the ClientHello, which names the host; a connection that sends none
# within 5 s goes to the local Caddy, which answers it as before.
tcp-request inspect-delay 5s
tcp-request content accept if { req.ssl_hello_type 1 }
# halkyon.li's names exactly, as its DNS zone has them (D7): no wildcard, so no
# other name reaches home.
acl halkyon_li req.ssl_sni -i halkyon.li learn.halkyon.li todo.halkyon.li train.halkyon.li meditate.halkyon.li jellyfin.halkyon.li
use_backend home if halkyon_li
default_backend local
# halkyon.li's Caddy at home, at its address in the tunnel (D8), which it publishes
# 443 on alone.
backend home
server balthasar 10.61.97.2:443 send-proxy-v2
# ramiel's own Caddy, moved off 443 (Caddyfile.halkyon-li).
backend local
server caddy 127.0.0.1:8443 send-proxy-v2