opencode UI on 443 via traefik (Host routing), nginx basic auth on the docker bridge
Operator: standard 443, routed by domain. Traefik already owns 443 with the *.ci.commoninternet.net cert, so opencode-ui-route.service drops a dynamic config into the traefik stack's watched file-providers volume routing oc.ci.commoninternet.net -> http://172.18.0.1:8080, where nginx (reachable only on docker_gwbridge) enforces the basic auth and logs 401s with the real client IP (real_ip from X-Forwarded-For). The fail2ban nginx jail bans in DOCKER-USER, since that traffic is forwarded, not INPUT. 8443 and the ACME-host variant are gone. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FqkQq3CDmFWcQ7u1LzoyRz
This commit is contained in:
@@ -27,13 +27,14 @@
|
||||
# ---- orchestrator identity --------------------------------------------------------------
|
||||
# The CI server is this very host, so `ssh cc-ci` goes to loopback (the module default).
|
||||
cc-ci-orchestrator.ciSshHost = "127.0.0.1";
|
||||
# The opencode UI rides the CI server's Let's Encrypt wildcard (*.ci.commoninternet.net).
|
||||
# The opencode UI: traefik (public 443, the *.ci.commoninternet.net cert) → nginx basic auth.
|
||||
cc-ci-orchestrator.opencodeUiHost = "oc.ci.commoninternet.net";
|
||||
cc-ci-orchestrator.opencodeUiAcmeHost = "ci.commoninternet.net";
|
||||
cc-ci-orchestrator.opencodeUiTraefikProvidersDir =
|
||||
"/var/lib/docker/volumes/traefik_ci_commoninternet_net_file-providers/_data";
|
||||
|
||||
# ---- no tailscale on this host (operator 2026-09-07) --------------------------------------
|
||||
# Everything is reached over the public IP: ssh (keys only), the CI front doors via traefik,
|
||||
# and the opencode UI on 8443 behind TLS + basic auth. fail2ban below guards the two logins.
|
||||
# and the opencode UI on 443 (traefik → nginx basic auth). fail2ban below guards the two logins.
|
||||
|
||||
# ---- ssh ----------------------------------------------------------------------------------
|
||||
services.openssh = {
|
||||
@@ -50,16 +51,18 @@
|
||||
services.openssh.settings.KbdInteractiveAuthentication = false;
|
||||
|
||||
# ---- firewall -------------------------------------------------------------------------------
|
||||
# 80/443 (traefik) and 53 (acme-dns) are opened by the cc-ci-server module. 8443 is the
|
||||
# opencode UI (nginx, LE cert, basic auth — see orchestrator-host.nix).
|
||||
# 80/443 (traefik) and 53 (acme-dns) are opened by the cc-ci-server module; the opencode UI
|
||||
# rides 443 through traefik (orchestrator-host.nix), so only ssh is opened here.
|
||||
networking.firewall = {
|
||||
enable = true;
|
||||
allowedTCPPorts = [ 22 8443 ];
|
||||
allowedTCPPorts = [ 22 ];
|
||||
};
|
||||
|
||||
# ---- fail2ban: sshd (password auth is off, this stops the log noise and slow brute force) and
|
||||
# the opencode UI's basic auth (nginx logs 401s to its error log; the built-in
|
||||
# nginx-http-auth filter matches them).
|
||||
# the opencode UI's basic auth (nginx logs 401s with the real client IP to its error log; the
|
||||
# built-in nginx-http-auth filter matches them). Those clients arrive through traefik's
|
||||
# docker-published 443, which iptables FORWARDs rather than INPUTs, so the ban for that jail
|
||||
# goes into the DOCKER-USER chain — an INPUT rule would never see the traffic.
|
||||
services.fail2ban = {
|
||||
enable = true;
|
||||
maxretry = 5;
|
||||
@@ -71,7 +74,8 @@
|
||||
filter = "nginx-http-auth";
|
||||
logpath = "/var/log/nginx/error.log";
|
||||
backend = "auto";
|
||||
port = "8443";
|
||||
banaction = "iptables-allports";
|
||||
chain = "DOCKER-USER";
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user