From: Giorgio Ravera Date: Mon, 5 Jan 2026 15:02:54 +0000 (+0100) Subject: migrated to distroless & python entrypoint X-Git-Tag: v0.0.1~48 X-Git-Url: http://git.giorgioravera.it/?a=commitdiff_plain;h=41bf322e985a206a0a8d94c7e77f4f71128f1d83;p=network-manager.git migrated to distroless & python entrypoint --- diff --git a/Dockerfile b/Dockerfile index 63aadd3..f70afbe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,6 @@ # ---------- STAGE 1: BUILD ---------- FROM python:3.12-slim AS builder -WORKDIR /app - # Install build dependencies RUN apt-get update && \ apt-get install -y --no-install-recommends sqlite3 && \ @@ -12,23 +10,40 @@ RUN apt-get update && \ COPY requirements.txt . RUN pip install --prefix=/install -r requirements.txt -# ---------- STAGE 2: RUNTIME ---------- -FROM python:3.12-slim +WORKDIR /app + +# Copy backend, frontend, entrypoint +COPY backend backend +COPY frontend frontend +COPY entrypoint.py entrypoint.py +RUN chmod 755 entrypoint.py -WORKDIR /var/www/network-manager +# ---------- STAGE 2: DISTROLESS ---------- +FROM gcr.io/distroless/base-debian13 -# Copy only installed packages +# Copy Python runtime from builder +COPY --from=builder /usr/local /usr/local + +# Copy libs +COPY --from=builder /lib/x86_64-linux-gnu/libsqlite3.so.0 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libz.so.1 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libbz2.so.1.0 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/liblzma.so.5 /lib/x86_64-linux-gnu/ +COPY --from=builder /lib/x86_64-linux-gnu/libgcc_s.so.1 /lib/x86_64-linux-gnu/ + +# Copy installed Python packages COPY --from=builder /install /usr/local -# Copy backend and frontend -COPY backend/ /var/www/network-manager/backend/ -COPY frontend/ /var/www/network-manager/frontend/ +WORKDIR /app + +# Copy application +COPY --from=builder /app/backend backend +COPY --from=builder /app/frontend frontend +COPY --from=builder /app/entrypoint.py /usr/local/bin/entrypoint.py -# Copy entrypoint -COPY entrypoint.sh /entrypoint.sh -RUN chmod +x /entrypoint.sh +# Ensure Python sees the installed packages +ENV PYTHONPATH="/usr/local/lib/python3.12/site-packages" -# Default environment variables ENV DB_PATH=/data/database.db ENV DB_RESET=0 ENV HTTP_PORT=8000 @@ -40,6 +55,5 @@ ENV PUBLIC_IP=127.0.0.1 # Expose the port dynamically EXPOSE ${HTTP_PORT} -# Use the env var in the startup command -ENTRYPOINT ["/entrypoint.sh"] -CMD ["sh", "-c", "uvicorn backend.main:app --host 0.0.0.0 --port ${HTTP_PORT} --proxy-headers"] +ENTRYPOINT ["/usr/local/bin/entrypoint.py"] +CMD ["python3", "-u", "-m", "uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8000", "--proxy-headers"] diff --git a/backend/main.py b/backend/main.py index bbdd1d1..4d5cc81 100644 --- a/backend/main.py +++ b/backend/main.py @@ -70,7 +70,7 @@ def check_rate_limit(ip: str): # FRONTEND PATHS (absolute paths inside Docker) # --------------------------------------------------------- -FRONTEND_DIR = "/var/www/network-manager/frontend" +FRONTEND_DIR = "/app/frontend" # Homepage @app.get("/") diff --git a/entrypoint.py b/entrypoint.py new file mode 100755 index 0000000..e115de0 --- /dev/null +++ b/entrypoint.py @@ -0,0 +1,137 @@ +#!/usr/local/bin/python3 + +import os +import sys +import sqlite3 +import subprocess + +# ================================ +# Variables +# ================================ +DB_FILE = os.environ.get("DB_PATH", "database.db") +DB_RESET = os.environ.get("DB_RESET", "0") == "1" +DOMAIN = os.environ.get("DOMAIN", "example.com") +PUBLIC_IP = os.environ.get("PUBLIC_IP", "127.0.0.1") + +IMAGE_NAME = "network-manager-distroless" +IMAGE_VERSION = "1.0" + +# ================================ +# Create DB if needed +# ================================ +def create_db(): + # Reset database if requested + if DB_RESET and os.path.exists(DB_FILE): + print("INFO: Removing existing database...") + os.remove(DB_FILE) + + # Skip creation if DB already exists + if os.path.exists(DB_FILE): + print("INFO: Database already exists. Nothing to do.") + return + + print(f"INFO: Creating database: {DB_FILE}.") + + # Ensure directory exists + os.makedirs(os.path.dirname(DB_FILE) or ".", exist_ok=True) + + conn = sqlite3.connect(DB_FILE) + cur = conn.cursor() + + # Enable foreign keys + cur.execute("PRAGMA foreign_keys = ON;") + + # GLOBAL SETTINGS + cur.execute(""" + CREATE TABLE settings ( + key TEXT PRIMARY KEY, + value TEXT + ); + """) + cur.execute("INSERT INTO settings (key, value) VALUES (?, ?)", ("domain", DOMAIN)) + cur.execute("INSERT INTO settings (key, value) VALUES (?, ?)", ("external_ipv4", PUBLIC_IP)) + + # HOSTS + cur.execute(""" + CREATE TABLE hosts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE, + ipv4 TEXT, + ipv6 TEXT, + mac TEXT, + note TEXT, + ssl_enabled INTEGER NOT NULL DEFAULT 0 + ); + """) + cur.execute("CREATE INDEX idx_hosts_name ON hosts(name);") + + # ALIASES + cur.execute(""" + CREATE TABLE aliases ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + host_id INTEGER NOT NULL, + alias TEXT NOT NULL, + note TEXT, + ssl_enabled INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY (host_id) REFERENCES hosts(id) + ); + """) + cur.execute("CREATE INDEX idx_aliases_host ON aliases(host_id);") + + # TXT RECORDS + cur.execute(""" + CREATE TABLE txt_records ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + value TEXT NOT NULL, + note TEXT, + host_id INTEGER, + FOREIGN KEY (host_id) REFERENCES hosts(id) + ); + """) + cur.execute("CREATE INDEX idx_txt_host ON txt_records(host_id);") + + conn.commit() + conn.close() + + print(f"INFO: Database initialized successfully for {DOMAIN}.") + print(f"INFO: Public IP: {PUBLIC_IP}.") + +# ================================ +# Entry Point +# ================================ + +# Force flush +sys.stdout.reconfigure(line_buffering=True) + +print(f"INFO: Starting {IMAGE_NAME} docker image version {IMAGE_VERSION}.") + + +# Parse arguments +args = sys.argv[1:] +i = 0 +while i < len(args): + if args[i] == "--reset": + DB_RESET = True + i += 1 + elif args[i] == "--domain" and i + 1 < len(args): + DOMAIN = args[i + 1] + i += 2 + elif args[i] == "--public-ip" and i + 1 < len(args): + PUBLIC_IP = args[i + 1] + i += 2 + elif args[i] == "--": + args = args[i + 1:] + break + else: + break + +# Create DB +create_db() + +# Continue to CMD +if not args: + print("ERROR: No command provided to exec.") + sys.exit(1) + +os.execvp(args[0], args) diff --git a/entrypoint.sh b/entrypoint.sh deleted file mode 100755 index 098c29c..0000000 --- a/entrypoint.sh +++ /dev/null @@ -1,129 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# ================================ -# Variables -# ================================ -DB_FILE="${DB_PATH:-database.db}" -DB_RESET="${DB_RESET:-0}" -DOMAIN="${DOMAIN:-example.com}" -PUBLIC_IP="${PUBLIC_IP:-127.0.0.1}" - -IMAGE_NAME="network-manager" -IMAGE_VERSION="0.0" - -function create_db() { - # Reset database if requested - if [[ $DB_RESET -eq 1 && -f "$DB_FILE" ]]; then - echo "INFO: [✓]] Removing existing database." - rm -f "$DB_FILE" - fi - - # Skip creation if DB already exists - if [[ -f "$DB_FILE" ]]; then - echo "INFO: [✓] Database already exists. Nothing to do." - return 0 - fi - - echo "INFO: [✓] Creating database: $DB_FILE" - - # Create DB with dynamic settings - sqlite3 "$DB_FILE" <