From 880bba86edbf0530545608077ae6b8264ac2676b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 21 Jul 2026 20:34:06 +0000 Subject: [PATCH] Fix security-dashboard binding to 127.0.0.1 instead of 0.0.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Confirmed live: Caddy (in a container) reaches this via host.docker.internal, a Docker bridge gateway IP, not localhost — a loopback-only bind refuses that connection outright ("dial tcp 172.17.0.1:8092: connect: connection refused"), even though curl from the host itself worked fine on 127.0.0.1. Bind to 0.0.0.0 and rely on UFW for the actual access scoping instead, matching every other host-network service in this repo (e.g. the Asterisk web admin, which already binds this way successfully with the same ufw_allow_from_caddy_net pattern). --- services/security-dashboard.sh | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/services/security-dashboard.sh b/services/security-dashboard.sh index 9601baf..ba05155 100644 --- a/services/security-dashboard.sh +++ b/services/security-dashboard.sh @@ -96,7 +96,7 @@ install_security-dashboard() { echo "[DRY-RUN] Would create system user $SVC_USER" echo "[DRY-RUN] Would write $APP_DIR/app.py" echo "[DRY-RUN] Would write /etc/sudoers.d/security-dashboard (scoped cscli/systemctl only)" - echo "[DRY-RUN] Would write a systemd unit and start it on 127.0.0.1:$DASHBOARD_PORT" + echo "[DRY-RUN] Would write a systemd unit and start it on 0.0.0.0:$DASHBOARD_PORT (firewalled via UFW, not interface binding)" echo "[DRY-RUN] Would configure Caddy + Authelia for a domain you'll be prompted for" return 0 fi @@ -190,7 +190,7 @@ SDSVC systemctl daemon-reload systemctl enable security-dashboard >/dev/null 2>&1 if systemctl restart security-dashboard; then - log_success "security-dashboard started on 127.0.0.1:$DASHBOARD_PORT" + log_success "security-dashboard started on port $DASHBOARD_PORT (all interfaces — UFW scopes actual access)" else log_warning "Failed to start — check: systemctl status security-dashboard" fi @@ -326,8 +326,9 @@ sudo journalctl -u security-dashboard -f - Sudo access is scoped to exactly three commands via \`/etc/sudoers.d/security-dashboard\`: \`cscli decisions delete --id \`, \`cscli decisions list -o json\`, and \`systemctl restart crowdsec\`. Nothing else. -- Listens on \`127.0.0.1:$DASHBOARD_PORT\` only — never exposed directly to the - internet, only reachable through Caddy. +- Listens on all interfaces (Caddy reaches it via \`host.docker.internal\`, a + Docker bridge IP — a loopback-only bind refuses that). Access is scoped by + UFW instead, allowed only from Caddy's internal network, not the internet. - **This page can delete active security bans.** Don't run it without Authelia (or equivalent) in front of it. README_MD @@ -672,8 +673,15 @@ class Handler(BaseHTTPRequestHandler): def main(): ThreadingHTTPServer.allow_reuse_address = True - with ThreadingHTTPServer(("127.0.0.1", PORT), Handler) as httpd: - print(f"Security dashboard running on 127.0.0.1:{PORT}") + # 0.0.0.0, not 127.0.0.1: Caddy runs in a container and reaches this via + # host.docker.internal (a Docker bridge gateway IP, not localhost) — a + # loopback-only bind refuses that connection outright. Confirmed live: + # "dial tcp 172.17.0.1:8092: connect: connection refused" even though + # curl from the host itself worked fine on 127.0.0.1. Access is scoped by + # UFW (see install_security-dashboard), not by which interface this binds + # to — same pattern every other host-network service in this repo uses. + with ThreadingHTTPServer(("0.0.0.0", PORT), Handler) as httpd: + print(f"Security dashboard running on 0.0.0.0:{PORT}") httpd.serve_forever()