chore: add Docker config files and fix fail2ban bind mount path
This commit is contained in:
49
.containerignore
Normal file
49
.containerignore
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# BanGUI — .dockerignore / .containerignore
|
||||||
|
# Works with both Docker and Podman.
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
# Version control
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
.venv
|
||||||
|
venv
|
||||||
|
env
|
||||||
|
|
||||||
|
# IDE / editor
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# Python caches
|
||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
.mypy_cache
|
||||||
|
.ruff_cache
|
||||||
|
.pytest_cache
|
||||||
|
.coverage
|
||||||
|
htmlcov
|
||||||
|
|
||||||
|
# Node
|
||||||
|
frontend/node_modules
|
||||||
|
frontend/.vite
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
dist
|
||||||
|
build
|
||||||
|
*.egg-info
|
||||||
|
|
||||||
|
# Documentation (keep README at root if needed)
|
||||||
|
Docs
|
||||||
|
|
||||||
|
# Tests (not needed in production images)
|
||||||
|
backend/tests
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
49
.dockerignore
Normal file
49
.dockerignore
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# BanGUI — .dockerignore / .containerignore
|
||||||
|
# Works with both Docker and Podman.
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
# Version control
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
.venv
|
||||||
|
venv
|
||||||
|
env
|
||||||
|
|
||||||
|
# IDE / editor
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# Python caches
|
||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
.mypy_cache
|
||||||
|
.ruff_cache
|
||||||
|
.pytest_cache
|
||||||
|
.coverage
|
||||||
|
htmlcov
|
||||||
|
|
||||||
|
# Node
|
||||||
|
frontend/node_modules
|
||||||
|
frontend/.vite
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
dist
|
||||||
|
build
|
||||||
|
*.egg-info
|
||||||
|
|
||||||
|
# Documentation (keep README at root if needed)
|
||||||
|
Docs
|
||||||
|
|
||||||
|
# Tests (not needed in production images)
|
||||||
|
backend/tests
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
69
Docker/Dockerfile.backend
Normal file
69
Docker/Dockerfile.backend
Normal 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"]
|
||||||
45
Docker/Dockerfile.frontend
Normal file
45
Docker/Dockerfile.frontend
Normal 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;"]
|
||||||
@@ -31,7 +31,7 @@ services:
|
|||||||
PUID: 0
|
PUID: 0
|
||||||
PGID: 0
|
PGID: 0
|
||||||
volumes:
|
volumes:
|
||||||
- fail2ban-dev-config:/config
|
- ./fail2ban-dev-config:/config
|
||||||
- fail2ban-dev-run:/var/run/fail2ban
|
- fail2ban-dev-run:/var/run/fail2ban
|
||||||
- /var/log:/var/log:ro
|
- /var/log:/var/log:ro
|
||||||
healthcheck:
|
healthcheck:
|
||||||
|
|||||||
102
Docker/compose.prod.yml
Normal file
102
Docker/compose.prod.yml
Normal 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
34
Docker/nginx.conf
Normal 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
106
Docker/push.sh
Normal 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 "============================================"
|
||||||
1
Docs/test.md
Normal file
1
Docs/test.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
https://lists.blocklist.de/lists/all.txt
|
||||||
Reference in New Issue
Block a user