chore: add Docker config files and fix fail2ban bind mount path

This commit is contained in:
2026-03-03 20:38:32 +01:00
parent 73860bd9f2
commit 39ee1e2945
9 changed files with 456 additions and 1 deletions

69
Docker/Dockerfile.backend Normal file
View File

@@ -0,0 +1,69 @@
# ──────────────────────────────────────────────────────────────
# BanGUI — Backend image (Python / FastAPI)
#
# Compatible with Docker and Podman.
# Build context must be the project root.
#
# Usage:
# docker build -t bangui-backend -f Docker/Dockerfile.backend .
# podman build -t bangui-backend -f Docker/Dockerfile.backend .
# ──────────────────────────────────────────────────────────────
# ── Stage 1: build dependencies ──────────────────────────────
FROM python:3.12-slim AS builder
WORKDIR /build
# Install build-time system dependencies
RUN apt-get update \
&& apt-get install -y --no-install-recommends gcc libffi-dev \
&& rm -rf /var/lib/apt/lists/*
COPY backend/pyproject.toml /build/
# Install Python dependencies into a virtual-env so we can copy it cleanly
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
RUN pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir .
# ── Stage 2: runtime image ───────────────────────────────────
FROM python:3.12-slim AS runtime
LABEL maintainer="BanGUI" \
description="BanGUI backend — fail2ban web management API"
# Non-root user for security
RUN groupadd --gid 1000 bangui \
&& useradd --uid 1000 --gid bangui --shell /bin/bash --create-home bangui
WORKDIR /app
# Copy the pre-built virtual-env
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH" \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
# Copy application source
COPY backend/app /app/app
COPY fail2ban-master /app/fail2ban-master
# Data directory for the SQLite database
RUN mkdir -p /data && chown bangui:bangui /data
VOLUME ["/data"]
# Default environment values (override at runtime)
ENV BANGUI_DATABASE_PATH="/data/bangui.db" \
BANGUI_FAIL2BAN_SOCKET="/var/run/fail2ban/fail2ban.sock" \
BANGUI_LOG_LEVEL="info"
EXPOSE 8000
USER bangui
# Health-check using the built-in health endpoint
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/health')" || exit 1
CMD ["uvicorn", "app.main:create_app", "--factory", "--host", "0.0.0.0", "--port", "8000"]

View File

@@ -0,0 +1,45 @@
# ──────────────────────────────────────────────────────────────
# BanGUI — Frontend image (React / Vite → nginx)
#
# Compatible with Docker and Podman.
# Build context must be the project root.
#
# Usage:
# docker build -t bangui-frontend -f Docker/Dockerfile.frontend .
# podman build -t bangui-frontend -f Docker/Dockerfile.frontend .
# ──────────────────────────────────────────────────────────────
# ── Stage 1: install & build ─────────────────────────────────
FROM node:22-alpine AS builder
WORKDIR /build
# Install dependencies first (layer caching)
COPY frontend/package.json frontend/package-lock.json* /build/
RUN npm ci --ignore-scripts
# Copy source and build
COPY frontend/ /build/
RUN npm run build
# ── Stage 2: serve with nginx ────────────────────────────────
FROM nginx:1.27-alpine AS runtime
LABEL maintainer="BanGUI" \
description="BanGUI frontend — fail2ban web management UI"
# Remove default nginx content
RUN rm -rf /usr/share/nginx/html/*
# Copy built assets
COPY --from=builder /build/dist /usr/share/nginx/html
# Custom nginx config for SPA routing + API reverse proxy
COPY Docker/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
CMD wget -qO /dev/null http://localhost:80/ || exit 1
CMD ["nginx", "-g", "daemon off;"]

View File

@@ -31,7 +31,7 @@ services:
PUID: 0
PGID: 0
volumes:
- fail2ban-dev-config:/config
- ./fail2ban-dev-config:/config
- fail2ban-dev-run:/var/run/fail2ban
- /var/log:/var/log:ro
healthcheck:

102
Docker/compose.prod.yml Normal file
View File

@@ -0,0 +1,102 @@
# ──────────────────────────────────────────────────────────────
# BanGUI — Production Compose
#
# Compatible with:
# docker compose -f Docker/compose.prod.yml up -d
# podman compose -f Docker/compose.prod.yml up -d
# podman-compose -f Docker/compose.prod.yml up -d
#
# Prerequisites:
# Create a .env file at the project root (or pass --env-file):
# BANGUI_SESSION_SECRET=<random-secret>
# ──────────────────────────────────────────────────────────────
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:
- fail2ban-config:/config
- fail2ban-run:/var/run/fail2ban
- /var/log:/var/log:ro
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
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_LOG_LEVEL: "info"
BANGUI_SESSION_SECRET: "${BANGUI_SESSION_SECRET:?Set BANGUI_SESSION_SECRET}"
BANGUI_TIMEZONE: "${BANGUI_TIMEZONE:-UTC}"
volumes:
- bangui-data:/data
- fail2ban-run:/var/run/fail2ban:ro
expose:
- "8000"
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/health')"]
interval: 30s
timeout: 5s
start_period: 10s
retries: 3
networks:
- bangui-net
# ── Frontend (nginx serving built SPA + API proxy) ──────────
frontend:
build:
context: ..
dockerfile: Docker/Dockerfile.frontend
container_name: bangui-frontend
restart: unless-stopped
ports:
- "${BANGUI_PORT:-8080}:80"
depends_on:
backend:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-qO", "/dev/null", "http://localhost:80/"]
interval: 30s
timeout: 5s
start_period: 5s
retries: 3
networks:
- bangui-net
volumes:
bangui-data:
driver: local
fail2ban-config:
driver: local
fail2ban-run:
driver: local
networks:
bangui-net:
driver: bridge

34
Docker/nginx.conf Normal file
View File

@@ -0,0 +1,34 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# ── Gzip compression ─────────────────────────────────────
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
gzip_min_length 256;
# ── API reverse proxy → backend container ─────────────────
location /api/ {
proxy_pass http://backend:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 60s;
}
# ── Static assets with long-term caching ──────────────────
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}
# ── SPA fallback — serve index.html for client routes ─────
location / {
try_files $uri $uri/ /index.html;
}
}

106
Docker/push.sh Normal file
View File

@@ -0,0 +1,106 @@
#!/usr/bin/env bash
#
# Build and push BanGUI container images to the Gitea registry.
#
# Usage:
# ./push.sh # builds & pushes with tag "latest"
# ./push.sh v1.2.3 # builds & pushes with tag "v1.2.3"
# ./push.sh v1.2.3 --no-build # pushes existing images only
#
# Prerequisites:
# podman login git.lpl-mind.de (or: docker login git.lpl-mind.de)
set -euo pipefail
# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
REGISTRY="git.lpl-mind.de"
NAMESPACE="lukas.pupkalipinski"
PROJECT="bangui"
BACKEND_IMAGE="${REGISTRY}/${NAMESPACE}/${PROJECT}/backend"
FRONTEND_IMAGE="${REGISTRY}/${NAMESPACE}/${PROJECT}/frontend"
TAG="${1:-latest}"
SKIP_BUILD=false
if [[ "${2:-}" == "--no-build" ]]; then
SKIP_BUILD=true
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
log() { echo -e "\n>>> $*"; }
err() { echo -e "\nERROR: $*" >&2; exit 1; }
# Detect container engine (podman preferred, docker fallback)
if command -v podman &>/dev/null; then
ENGINE="podman"
elif command -v docker &>/dev/null; then
ENGINE="docker"
else
err "Neither podman nor docker is installed."
fi
# ---------------------------------------------------------------------------
# Pre-flight checks
# ---------------------------------------------------------------------------
echo "============================================"
echo " BanGUI — Build & Push"
echo " Engine : ${ENGINE}"
echo " Registry : ${REGISTRY}"
echo " Tag : ${TAG}"
echo "============================================"
if [[ "${ENGINE}" == "podman" ]]; then
if ! podman login --get-login "${REGISTRY}" &>/dev/null; then
err "Not logged in. Run:\n podman login ${REGISTRY}"
fi
fi
# ---------------------------------------------------------------------------
# Build
# ---------------------------------------------------------------------------
if [[ "${SKIP_BUILD}" == false ]]; then
log "Building backend image → ${BACKEND_IMAGE}:${TAG}"
"${ENGINE}" build \
-t "${BACKEND_IMAGE}:${TAG}" \
-f "${SCRIPT_DIR}/Dockerfile.backend" \
"${PROJECT_ROOT}"
log "Building frontend image → ${FRONTEND_IMAGE}:${TAG}"
"${ENGINE}" build \
-t "${FRONTEND_IMAGE}:${TAG}" \
-f "${SCRIPT_DIR}/Dockerfile.frontend" \
"${PROJECT_ROOT}"
fi
# ---------------------------------------------------------------------------
# Push
# ---------------------------------------------------------------------------
log "Pushing ${BACKEND_IMAGE}:${TAG}"
"${ENGINE}" push "${BACKEND_IMAGE}:${TAG}"
log "Pushing ${FRONTEND_IMAGE}:${TAG}"
"${ENGINE}" push "${FRONTEND_IMAGE}:${TAG}"
# ---------------------------------------------------------------------------
# Summary
# ---------------------------------------------------------------------------
echo ""
echo "============================================"
echo " Push complete!"
echo ""
echo " Images:"
echo " ${BACKEND_IMAGE}:${TAG}"
echo " ${FRONTEND_IMAGE}:${TAG}"
echo ""
echo " Deploy on server:"
echo " ${ENGINE} login ${REGISTRY}"
echo " ${ENGINE} compose -f Docker/compose.prod.yml pull"
echo " ${ENGINE} compose -f Docker/compose.prod.yml up -d"
echo "============================================"