diff --git a/caddy-proxy-manager/.env b/caddy-proxy-manager/.env new file mode 100755 index 0000000..5b6453a --- /dev/null +++ b/caddy-proxy-manager/.env @@ -0,0 +1,131 @@ +# Caddy Proxy Manager Environment Configuration +# Copy this file to .env and update with your secure values +# IMPORTANT: chmod 600 .env after creating it + +# ============================================================================= +# REQUIRED SECURITY SETTINGS (PRODUCTION) +# ============================================================================= + +# Session Secret (REQUIRED) +# Generate with: openssl rand -base64 32 +# Must be at least 32 characters in production +SESSION_SECRET=bSTmipbWFuZUNXWykEWG9pUhjvRp84XsmVY9Qpm8SLk= + +# Admin Credentials (REQUIRED) +# USERNAME: Any username (e.g., "admin" is fine) +# PASSWORD: Must be 12+ characters with: +# - Uppercase letters (A-Z) +# - Lowercase letters (a-z) +# - Numbers (0-9) +# - Special characters (!@#$%^&* etc.) +ADMIN_USERNAME=admin +ADMIN_PASSWORD=Theocs01e12! + +# ============================================================================= +# APPLICATION CONFIGURATION +# ============================================================================= + +# Public base URL for the application (IMPORTANT!) +# This is the URL where users access your Caddy Proxy Manager interface. +# +# ** REQUIRED FOR OAUTH: If using OAuth2/OIDC authentication, this MUST match +# the redirect URI configured in your OAuth provider exactly. +# The redirect URI will be: {BASE_URL}/api/auth/callback/oauth2 +# +# Examples: +# - Local development: http://localhost:3000 +# - Production with domain: https://caddy-manager.example.com +# - Production with IP: http://192.168.1.100:3000 +# +# IMPORTANT: Do not include a trailing slash +BASE_URL=http://192.168.1.4:3000 + +# ============================================================================= +# ROOTLESS OPERATION (OPTIONAL) +# ============================================================================= + +# User and Group IDs for running containers as non-root +# Set these to match your host user to avoid permission issues with volumes +# Find your UID/GID with: id -u / id -g +# +# Defaults: +# - Web service: PUID=10001, PGID=10001 +# - Caddy service: PUID=10000, PGID=10000 +# +# For matching your host user (recommended for development): +PUID=1000 +PGID=1000 + +# ============================================================================= +# OAUTH2/OIDC AUTHENTICATION (OPTIONAL) +# ============================================================================= + +# OAuth2/OIDC Provider (works with Authentik, Authelia, Keycloak, etc.) +# Enable OAuth2 authentication with any OIDC-compliant provider +OAUTH_ENABLED=false +OAUTH_PROVIDER_NAME=OAuth2 # Display name (e.g., "Authentik", "Keycloak") +OAUTH_CLIENT_ID= +OAUTH_CLIENT_SECRET= +OAUTH_ISSUER= # OIDC discovery URL (e.g., https://auth.example.com/application/o/app/) + +# Optional: Override auto-discovered URLs (only if OIDC discovery doesn't work) +# OAUTH_AUTHORIZATION_URL= +# OAUTH_TOKEN_URL= +# OAUTH_USERINFO_URL= + +# OAuth Settings +OAUTH_ALLOW_AUTO_LINKING=false # Auto-link OAuth to accounts without passwords + +# Example for Authentik: +# OAUTH_ENABLED=true +# OAUTH_PROVIDER_NAME=Authentik +# OAUTH_CLIENT_ID=your-client-id +# OAUTH_CLIENT_SECRET=your-client-secret +# OAUTH_ISSUER=https://auth.example.com/application/o/caddy-proxy/ +# +# IMPORTANT: Configure the redirect URI in your OAuth provider: +# Redirect URI = {BASE_URL}/api/auth/callback/oauth2 +# Example: http://localhost:3000/api/auth/callback/oauth2 +# or: https://caddy-manager.example.com/api/auth/callback/oauth2 + +# ============================================================================= +# OPTIONAL: ADVANCED CONFIGURATION +# ============================================================================= + +# Database configuration (usually no need to change) +# DATABASE_URL=file:/app/data/caddy-proxy-manager.db + +# Caddy Admin API endpoint (usually no need to change) +# CADDY_API_URL=http://caddy:2019 + +# Certificate storage directory (usually no need to change) +# CERTS_DIRECTORY=./data/certs + +# Login rate limiting (optional, for custom rate limit settings) +# LOGIN_MAX_ATTEMPTS=5 +# LOGIN_WINDOW_MS=300000 +# LOGIN_BLOCK_MS=900000 + + +# ============================================================================= +# CLICKHOUSE ANALYTICS (OPTIONAL) +# ============================================================================= + +# ClickHouse is used for analytics data (traffic events, WAF events). +# Data is automatically retained for 90 days via ClickHouse TTL. +# CLICKHOUSE_PASSWORD is required — generate with: openssl rand -base64 32 +CLICKHOUSE_PASSWORD=54zW44sUa6jUAZfWdz3f9YUBaFrfqUMa5kTZckpnhsg= +# CLICKHOUSE_URL=http://clickhouse:8123 +# CLICKHOUSE_USER=cpm +# CLICKHOUSE_DB=analytics + +# ============================================================================= +# GEOIP UPDATE (OPTIONAL) +# ============================================================================= + +# GeoIP Update (Optional - for geoblocking support) +# To enable the geoipupdate container, set COMPOSE_PROFILES=geoipupdate +# Get credentials at: https://www.maxmind.com/en/geolite2/signup +COMPOSE_PROFILES= +GEOIPUPDATE_ACCOUNT_ID= +GEOIPUPDATE_LICENSE_KEY= diff --git a/caddy-proxy-manager/compose.yml b/caddy-proxy-manager/compose.yml new file mode 100755 index 0000000..37c2f5c --- /dev/null +++ b/caddy-proxy-manager/compose.yml @@ -0,0 +1,219 @@ +services: + web: + container_name: caddy-proxy-manager-web + image: ghcr.io/fuomag9/caddy-proxy-manager-web:latest + build: + context: . + dockerfile: docker/web/Dockerfile + args: + # User and group IDs for rootless operation + # Set these to match your host user to avoid permission issues + # Find your UID/GID with: id -u / id -g + PUID: ${PUID:-10001} + PGID: ${PGID:-10001} + restart: unless-stopped + ports: + - "3001:3000" + environment: + # Node environment + NODE_ENV: production + + # REQUIRED: Session secret for encrypting cookies and sessions + # Generate with: openssl rand -base64 32 + # SECURITY: You MUST set this to a unique value in production! + SESSION_SECRET: ${SESSION_SECRET:?ERROR - SESSION_SECRET is required} + + # Caddy API endpoint (internal communication) + CADDY_API_URL: ${CADDY_API_URL:-http://caddy:2019} + + # Public base URL for the application + BASE_URL: ${BASE_URL:-http://localhost:3000} + + # Database configuration + DATABASE_PATH: /app/data/caddy-proxy-manager.db + DATABASE_URL: file:/app/data/caddy-proxy-manager.db + + # NextAuth configuration + NEXTAUTH_URL: ${BASE_URL:-http://localhost:3000} + + # REQUIRED: Admin credentials for login + # SECURITY: You MUST set these to secure values in production! + # Password must be 12+ chars with uppercase, lowercase, numbers, and special chars + ADMIN_USERNAME: ${ADMIN_USERNAME:?ERROR - ADMIN_USERNAME is required} + ADMIN_PASSWORD: ${ADMIN_PASSWORD:?ERROR - ADMIN_PASSWORD is required} + + # OAuth2/OIDC Authentication (Optional - works with Authentik, Authelia, Keycloak, etc.) + OAUTH_ENABLED: ${OAUTH_ENABLED:-false} + OAUTH_PROVIDER_NAME: ${OAUTH_PROVIDER_NAME:-OAuth2} + OAUTH_CLIENT_ID: ${OAUTH_CLIENT_ID:-} + OAUTH_CLIENT_SECRET: ${OAUTH_CLIENT_SECRET:-} + OAUTH_ISSUER: ${OAUTH_ISSUER:-} + OAUTH_AUTHORIZATION_URL: ${OAUTH_AUTHORIZATION_URL:-} + OAUTH_TOKEN_URL: ${OAUTH_TOKEN_URL:-} + OAUTH_USERINFO_URL: ${OAUTH_USERINFO_URL:-} + OAUTH_ALLOW_AUTO_LINKING: ${OAUTH_ALLOW_AUTO_LINKING:-false} + + # ClickHouse analytics database + CLICKHOUSE_URL: ${CLICKHOUSE_URL:-http://clickhouse:8123} + CLICKHOUSE_USER: ${CLICKHOUSE_USER:-cpm} + CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:?ERROR - CLICKHOUSE_PASSWORD is required} + CLICKHOUSE_DB: ${CLICKHOUSE_DB:-analytics} + group_add: + - "${CADDY_GID:-10000}" # caddy's GID — lets the web user read /logs/access.log + volumes: + - caddy-manager-data:/app/data + - geoip-data:/usr/share/GeoIP:ro,z + - caddy-logs:/logs:ro + depends_on: + caddy: + condition: service_healthy + clickhouse: + condition: service_healthy + networks: + - caddy-network + healthcheck: + test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/api/health',r=>{process.exit(r.statusCode<400?0:1)}).on('error',()=>process.exit(1))"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + caddy: + container_name: caddy-proxy-manager-caddy + image: ghcr.io/fuomag9/caddy-proxy-manager-caddy:latest + build: + context: . + dockerfile: docker/caddy/Dockerfile + args: + # User and group IDs for rootless operation + # Set these to match your host user to avoid permission issues + # Find your UID/GID with: id -u / id -g + PUID: ${PUID:-10000} + PGID: ${PGID:-10000} + restart: unless-stopped + ports: + - "80:80" + - "80:80/udp" + - "443:443" + - "443:443/udp" + # - "2019:2019" # Admin API + # Admin API (port 2019) is only exposed on internal network for security, enable at your risk + # Web UI accesses via http://caddy:2019 internally + # Uncomment the line below to expose metrics externally for Grafana/Prometheus + # - "9090:9090" # Metrics available at http://localhost:9090/metrics (configure in Settings first) + environment: + # Primary domain for Caddy configuration + PRIMARY_DOMAIN: akanealw.com + volumes: + - caddy-data:/data + - caddy-config:/config + - caddy-logs:/logs + - geoip-data:/usr/share/GeoIP:ro,z + networks: + - caddy-network + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "-O", "/dev/null", "http://localhost:2019/config/"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 10s + + # Docker socket proxy — restricts API surface exposed to l4-port-manager. + # Only allows GET, POST to /containers/ and /compose/ endpoints. + docker-socket-proxy: + container_name: caddy-proxy-manager-docker-proxy + image: tecnativa/docker-socket-proxy:latest + restart: unless-stopped + environment: + CONTAINERS: 1 + POST: 1 + # Deny everything else by default + IMAGES: 0 + NETWORKS: 0 + VOLUMES: 0 + EXEC: 0 + SWARM: 0 + AUTH: 0 + SECRETS: 0 + BUILD: 0 + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + networks: + - caddy-network + + # L4 Port Manager sidecar — automatically recreates the caddy container + # when L4 proxy host ports change. + # Uses Docker socket proxy instead of direct Docker socket access. + l4-port-manager: + container_name: caddy-proxy-manager-l4-ports + image: ghcr.io/fuomag9/caddy-proxy-manager-l4-port-manager:latest + build: + context: . + dockerfile: docker/l4-port-manager/Dockerfile + restart: unless-stopped + environment: + DATA_DIR: /data + COMPOSE_DIR: /compose + POLL_INTERVAL: "${L4_PORT_MANAGER_POLL_INTERVAL:-2}" + DOCKER_HOST: tcp://docker-socket-proxy:2375 + volumes: + - caddy-manager-data:/data + - .:/compose:ro + depends_on: + caddy: + condition: service_healthy + docker-socket-proxy: + condition: service_started + networks: + - caddy-network + + clickhouse: + container_name: caddy-proxy-manager-clickhouse + image: clickhouse/clickhouse-server:latest-alpine + restart: always + environment: + CLICKHOUSE_DB: ${CLICKHOUSE_DB:-analytics} + CLICKHOUSE_USER: ${CLICKHOUSE_USER:-cpm} + CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:?ERROR - CLICKHOUSE_PASSWORD is required} + CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: 1 + volumes: + - clickhouse-data:/var/lib/clickhouse + networks: + - caddy-network + healthcheck: + test: ["CMD-SHELL", "clickhouse-client --user ${CLICKHOUSE_USER:-cpm} --password ${CLICKHOUSE_PASSWORD:?ERROR - CLICKHOUSE_PASSWORD is required} --query 'SELECT 1'"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 30s + ulimits: + nofile: + soft: 262144 + hard: 262144 + + geoipupdate: + container_name: geoipupdate-${HOSTNAME} + image: ghcr.io/maxmind/geoipupdate + profiles: [geoipupdate] + restart: always + environment: + - GEOIPUPDATE_ACCOUNT_ID=${GEOIPUPDATE_ACCOUNT_ID:-} + - GEOIPUPDATE_LICENSE_KEY=${GEOIPUPDATE_LICENSE_KEY:-} + - 'GEOIPUPDATE_EDITION_IDS=GeoLite2-ASN GeoLite2-City GeoLite2-Country' + - GEOIPUPDATE_FREQUENCY=72 + volumes: + - geoip-data:/usr/share/GeoIP:z + networks: + - caddy-network + +networks: + caddy-network: + driver: bridge + +volumes: + caddy-manager-data: + caddy-data: + caddy-config: + caddy-logs: + geoip-data: + clickhouse-data: