From 44542b93c05578579cc3ef07895c6d90f666d532 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 23 May 2026 21:27:52 +0200 Subject: [PATCH] chore(release): bump version to 0.9.19-rc.4 - Add production Docker Compose configuration - Add check_auth.py diagnostic script for session 401 debugging --- Docker/compose.prod.yml | 105 ++++++++++++++++++++++++++ backend/pyproject.toml | 2 +- check_auth.py | 147 +++++++++++++++++++++++++++++++++++++ frontend/package-lock.json | 4 +- 4 files changed, 255 insertions(+), 3 deletions(-) create mode 100644 Docker/compose.prod.yml create mode 100644 check_auth.py diff --git a/Docker/compose.prod.yml b/Docker/compose.prod.yml new file mode 100644 index 0000000..dc91cad --- /dev/null +++ b/Docker/compose.prod.yml @@ -0,0 +1,105 @@ +# ────────────────────────────────────────────────────────────── +# BanGUI — Production Compose +# +# Usage: +# docker compose -f Docker/compose.prod.yml up -d +# podman compose -f Docker/compose.prod.yml up -d +# +# Features: +# - Multi-stage built images (no volume-mounted source code) +# - Frontend served by nginx with API reverse proxy +# - Backend running uvicorn without --reload +# - Only port 8080 exposed to host +# ────────────────────────────────────────────────────────────── + +name: bangui + +services: + # ── fail2ban ───────────────────────────────────────────────── + fail2ban: + image: lscr.io/linuxserver/fail2ban:latest + container_name: bangui-fail2ban + restart: unless-stopped + cap_add: + - NET_ADMIN + - NET_RAW + network_mode: host + environment: + TZ: "${BANGUI_TIMEZONE:-UTC}" + PUID: 0 + PGID: 0 + volumes: + - ../data/fail2ban-dev-config:/config + - fail2ban-run:/var/run/fail2ban + - /var/log:/var/log:ro + - ../data/log:/remotelogs/bangui + healthcheck: + test: ["CMD", "fail2ban-client", "ping"] + interval: 30s + timeout: 5s + start_period: 15s + retries: 3 + + # ── Backend (FastAPI + uvicorn) ───────────────────────────── + backend: + build: + context: .. + dockerfile: Docker/Dockerfile.backend + target: runtime + container_name: bangui-backend + restart: unless-stopped + depends_on: + fail2ban: + condition: service_healthy + environment: + BANGUI_DATABASE_PATH: "/data/bangui.db" + BANGUI_FAIL2BAN_SOCKET: "/var/run/fail2ban/fail2ban.sock" + BANGUI_FAIL2BAN_CONFIG_DIR: "/config/fail2ban" + BANGUI_LOG_FILE: "/data/log/bangui.log" + BANGUI_LOG_LEVEL: "${BANGUI_LOG_LEVEL:-info}" + BANGUI_SESSION_SECRET: "${BANGUI_SESSION_SECRET:?BANGUI_SESSION_SECRET must be set — generate with: python -c 'import secrets; print(secrets.token_hex(32))'}" + BANGUI_TIMEZONE: "${BANGUI_TIMEZONE:-UTC}" + BANGUI_SESSION_COOKIE_SECURE: "${BANGUI_SESSION_COOKIE_SECURE:-true}" + BANGUI_CORS_ALLOWED_ORIGINS: "${BANGUI_CORS_ALLOWED_ORIGINS:-}" + volumes: + - ../data:/data + - ../fail2ban-master:/app/fail2ban-master:ro + - fail2ban-run:/var/run/fail2ban:ro + - ../data/fail2ban-dev-config:/config:rw + networks: + - bangui-net + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:8000/api/v1/health/live || exit 1"] + interval: 30s + timeout: 10s + start_period: 40s + retries: 3 + + # ── Frontend (nginx serving built SPA) ────────────────────── + frontend: + build: + context: .. + dockerfile: Docker/Dockerfile.frontend + container_name: bangui-frontend + restart: unless-stopped + depends_on: + backend: + condition: service_healthy + ports: + - "${BANGUI_PORT:-8080}:80" + networks: + - bangui-net + healthcheck: + test: ["CMD-SHELL", "wget -qO /dev/null http://localhost:80/ || exit 1"] + interval: 30s + timeout: 5s + start_period: 5s + retries: 3 + +volumes: + fail2ban-run: + driver: local + +networks: + bangui-net: + driver: bridge diff --git a/backend/pyproject.toml b/backend/pyproject.toml index c28ed5e..4009727 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "bangui-backend" -version = "0.9.19-rc.3" +version = "0.9.19-rc.4" description = "BanGUI backend — fail2ban web management interface" requires-python = ">=3.12" dependencies = [ diff --git a/check_auth.py b/check_auth.py new file mode 100644 index 0000000..f5b990f --- /dev/null +++ b/check_auth.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python3 +"""Diagnostic script for BanGUI auth/session 401 issue. + +Tests the full auth flow against http://192.168.178.43:8080/api/v1/auth +using password "Hallo123!". + +Usage: + python3 check_auth.py +""" + +import json +import urllib.error +import urllib.request + +BASE_URL = "http://192.168.178.43:8080/api/v1" +PASSWORD = "Hallo123!" + + +def make_request(url, method="GET", data=None, headers=None, cookie=None): + """Make an HTTP request and return (status, headers, body, cookies).""" + req_headers = headers or {} + if data: + req_headers["Content-Type"] = "application/json" + if cookie: + req_headers["Cookie"] = cookie + + req = urllib.request.Request( + url, + data=json.dumps(data).encode("utf-8") if data else None, + headers=req_headers, + method=method, + ) + + try: + with urllib.request.urlopen(req) as resp: + body = resp.read().decode("utf-8") + cookies = resp.headers.get_all("Set-Cookie") or [] + return resp.status, dict(resp.headers), body, cookies + except urllib.error.HTTPError as e: + body = e.read().decode("utf-8") + cookies = e.headers.get_all("Set-Cookie") or [] + return e.code, dict(e.headers), body, cookies + except Exception as e: + return None, {}, str(e), [] + + +def extract_cookie_value(set_cookie_headers, cookie_name): + """Extract cookie value from Set-Cookie headers.""" + for header in set_cookie_headers: + if header.startswith(cookie_name + "="): + return header.split(";")[0] + return None + + +def main(): + print("=" * 60) + print("BanGUI Auth Diagnostic Script") + print("Target:", BASE_URL) + print("=" * 60) + + # 1. Check health endpoint (no auth needed) + print("\n[1] GET /health") + status, headers, body, _ = make_request(f"{BASE_URL}/health") + print(f" Status: {status}") + print(f" Response: {body[:200]}") + + # 2. Check CORS preflight for login + print("\n[2] OPTIONS /auth/login (CORS preflight)") + status, headers, body, _ = make_request( + f"{BASE_URL}/auth/login", + method="OPTIONS", + headers={ + "Origin": "http://192.168.178.43:8080", + "Access-Control-Request-Method": "POST", + "Access-Control-Request-Headers": "Content-Type", + }, + ) + print(f" Status: {status}") + print(f" Access-Control-Allow-Origin: {headers.get('Access-Control-Allow-Origin', 'MISSING')}") + print(f" Access-Control-Allow-Credentials: {headers.get('Access-Control-Allow-Credentials', 'MISSING')}") + + # 3. Login + print(f"\n[3] POST /auth/login (password: {PASSWORD})") + status, headers, body, cookies = make_request( + f"{BASE_URL}/auth/login", + method="POST", + data={"password": PASSWORD}, + headers={"Origin": "http://192.168.178.43:8080"}, + ) + print(f" Status: {status}") + print(f" Response: {body}") + print(f" Set-Cookie headers: {cookies}") + + session_cookie = extract_cookie_value(cookies, "bangui_session") + if session_cookie: + print(f" Extracted session cookie: {session_cookie[:50]}...") + else: + print(" WARNING: No bangui_session cookie received!") + + # 4. Validate session with cookie + print("\n[4] GET /auth/session (with cookie)") + if session_cookie: + status, headers, body, _ = make_request( + f"{BASE_URL}/auth/session", + cookie=session_cookie, + headers={"Origin": "http://192.168.178.43:8080"}, + ) + print(f" Status: {status}") + print(f" Response: {body}") + else: + print(" SKIPPED (no cookie from login)") + + # 5. Validate session WITHOUT cookie (should be 401) + print("\n[5] GET /auth/session (without cookie)") + status, headers, body, _ = make_request(f"{BASE_URL}/auth/session") + print(f" Status: {status}") + print(f" Response: {body}") + + # 6. Check backend settings (if available via /setup or other endpoint) + print("\n[6] GET /setup (check if setup is complete)") + status, headers, body, _ = make_request(f"{BASE_URL}/setup") + print(f" Status: {status}") + print(f" Response: {body[:200]}") + + print("\n" + "=" * 60) + print("DIAGNOSIS SUMMARY") + print("=" * 60) + + if session_cookie and "Secure" in str(cookies): + print("\n PROBLEM FOUND: Session cookie has 'Secure' flag set,") + print(" but you are accessing over HTTP (not HTTPS).") + print(" Browsers will NOT send Secure cookies over HTTP!") + print("\n FIX: Set SESSION_COOKIE_SECURE=false in your backend .env") + print(" and restart the backend.") + + if not session_cookie and status == 401: + print("\n PROBLEM FOUND: Login succeeded but no session cookie was set.") + print(" This usually means the cookie is being rejected by the browser") + print(" due to Secure flag on HTTP, or SameSite restrictions.") + + print("\n If CORS Access-Control-Allow-Origin is missing or wrong,") + print(" add your frontend origin to CORS_ALLOWED_ORIGINS in .env") + print("=" * 60) + + +if __name__ == "__main__": + main() diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8c67db8..08ab938 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "bangui-frontend", - "version": "0.9.19", + "version": "0.9.19-rc.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bangui-frontend", - "version": "0.9.19", + "version": "0.9.19-rc.4", "dependencies": { "@fluentui/react-components": "^9.55.0", "@fluentui/react-icons": "^2.0.257",