This commit is contained in:
Mike McFetridge
2026-07-20 09:23:17 -04:00
parent c1315882da
commit 72272e4006
3179 changed files with 562960 additions and 14 deletions
@@ -0,0 +1,65 @@
# =============================================================================
# Nginx Proxy Manager Platform — Environment Variables
# =============================================================================
MYSQL_ROOT_PASSWORD=rhDGjilZm6XEMJ
MYSQL_DATABASE=npm
MYSQL_USER=npm
MYSQL_PASSWORD=rhDGjilZm6XEMJ
# TZ for container timezone
TZ=America/New_York
# =============================================================================
# WAF Management Platform — Environment Variables
# Copy this file to .env and fill in all CHANGE_ME values before first run.
# Never commit .env to source control.
# =============================================================================
# --- Image version -------------------------------------------------------
# Pin a release tag (e.g. v1.0.0) for reproducible deploys. "latest" tracks
# the newest published image.
WAF_IMAGE_TAG=latest
# --- GeoIP (MaxMind GeoLite2) --------------------------------------------
# Host path to your own GeoLite2-City.mmdb. MaxMind licensing prevents us
# from bundling it. See the README "GeoIP setup" section to obtain one free.
GEOIP_DB_PATH=./GeoLite2-City.mmdb
# --- PostgreSQL ----------------------------------------------------------
POSTGRES_DB=wafdb
POSTGRES_USER=wafuser
POSTGRES_PASSWORD=ioGjIVo4zu4b6Bj
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
# --- Redis ---------------------------------------------------------------
REDIS_URL=redis://redis:6379/0
# --- API Security --------------------------------------------------------
# Generate with: python3 -c "import secrets; print(secrets.token_hex(32))"
SECRET_KEY=ef210e8acc8c0ecdafe3b2096c8923fed5b766d2e86885fa387f3ad600e2de0e
# Comma-separated list of allowed CORS origins (no wildcard).
# For the default local deploy, use the UI's HTTPS URL:
ALLOWED_ORIGINS=https://localhost:8443
# TOTP encryption key — generate with:
# python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
TOTP_ENCRYPTION_KEY=G1WN75rpulrIprSgn56Ctb8qiXdZM7K2m2FjksRsmu8=
# --- Caddy Admin API (container-internal) --------------------------------
CADDY_ADMIN_URL=http://caddy-waf:2019
# --- SMTP (for alert notifications) --------------------------------------
SMTP_HOST=mail.mmcfetridge.net
SMTP_PORT=465
SMTP_USER=miker@mmcfetridge.net
SMTP_PASSWORD="!Sucyetat123"
SMTP_FROM=miker@mmcfetridge.net
# --- Bootstrap superadmin (seeded on first run only) ---------------------
# Change the password immediately after first login.
BOOTSTRAP_ADMIN_EMAIL=miker@mmcfetridge.net
BOOTSTRAP_ADMIN_PASSWORD=PVGpjJOId5djMO5
@@ -0,0 +1,252 @@
---
services:
db:
image: 'jc21/mariadb-aria:latest'
container_name: npm-db
restart: unless-stopped
env_file: .env
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=${MYSQL_DATABASE}
- MYSQL_USER=${MYSQL_USER}
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
- MARIADB_AUTO_UPGRADE=1
volumes:
- ./npm/data/mysql:/var/lib/mysql
networks:
# - proxydb
- waf-internal
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u${MYSQL_USER}", "-p${MYSQL_PASSWORD}"]
interval: 20s
timeout: 5s
retries: 5
start_period: 30s
app:
image: 'jc21/nginx-proxy-manager:latest'
container_name: nginx-proxy-manager
restart: unless-stopped
env_file: .env
environment:
- TZ=${TZ}
- DB_MYSQL_HOST=db
- DB_MYSQL_PORT=3306
- DB_MYSQL_USER=${MYSQL_USER}
- DB_MYSQL_PASSWORD=${MYSQL_PASSWORD}
- DB_MYSQL_NAME=${MYSQL_DATABASE}
ports:
- '81:81' # Kept so you can access the NPM dashboard on host-ip:81
# '80:80' and '443:443' REMOVED here. They are reachable internally via the network.
volumes:
- ./npm/data/npm:/data
- ./npm/data/letsencrypt:/etc/letsencrypt
depends_on:
- db
networks:
# - proxy
# - proxydb
- waf-internal
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:81/api/status"]
interval: 20s
timeout: 5s
retries: 5
start_period: 40s
# ---------------------------------------------------------------------------
# Caddy + Coraza: WAF engine and reverse proxy
# ---------------------------------------------------------------------------
caddy-waf:
image: ghcr.io/socfortress/waf-caddy:${WAF_IMAGE_TAG:-latest}
container_name: caddy-waf
ports:
- "80:80"
- "443:443"
# Lets the WAF proxy to apps running on the Docker host (e.g. an nginx
# upstream) via http://host.docker.internal:<port>. See demo/README.md.
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- coraza-rules:/etc/coraza/rules
- caddy-config:/etc/caddy
- coraza-custom:/etc/coraza/custom
- crs-data:/etc/coraza/crs-rules # live CRS dir (seeded by admin-api)
- coraza-logs:/var/log/coraza
networks:
- waf-internal
depends_on:
http-echo:
condition: service_started
healthcheck:
test: ["CMD", "wget", "-q", "-O-", "http://localhost:2019/config/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 20s
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
restart: unless-stopped
# ---------------------------------------------------------------------------
# Dummy upstream — replace with your real protected app(s)
# ---------------------------------------------------------------------------
http-echo:
image: hashicorp/http-echo:0.2.3
container_name: http-echo
command: ["-text=upstream-ok"]
user: "65534:65534"
networks:
- waf-internal
healthcheck:
test: ["NONE"]
deploy:
resources:
limits:
cpus: "0.25"
memory: 64M
restart: unless-stopped
# ---------------------------------------------------------------------------
# FastAPI Admin API
# ---------------------------------------------------------------------------
admin-api:
image: ghcr.io/socfortress/waf-admin-api:${WAF_IMAGE_TAG:-latest}
container_name: admin-api
env_file: .env
volumes:
- tls-certs:/certs # shared TLS cert volume (see admin-ui)
# GeoLite2 DB is user-supplied — MaxMind licensing forbids redistribution.
# Point GEOIP_DB_PATH at your downloaded GeoLite2-City.mmdb (see README).
- ./GeoLite2-City/GeoLite2-City.mmdb:/etc/geoip-bundle/GeoLite2-City.mmdb:ro
- geoip-data:/etc/geoip
- coraza-rules:/etc/coraza/rules
- caddy-config:/etc/caddy
- coraza-custom:/etc/coraza/custom
- crs-data:/etc/coraza/crs-rules # live CRS dir (seeded from image bundle)
- coraza-logs:/var/log/coraza
networks:
- waf-internal
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-q", "-O-", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
restart: unless-stopped
# ---------------------------------------------------------------------------
# React Admin UI (served by Nginx, HTTPS on 8443)
# ---------------------------------------------------------------------------
admin-ui:
image: ghcr.io/socfortress/waf-admin-ui:${WAF_IMAGE_TAG:-latest}
container_name: admin-ui
ports:
- "8443:8080"
volumes:
- tls-certs:/etc/nginx/certs # shared with admin-api for cert upload + reload
networks:
- waf-internal
depends_on:
admin-api:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-q", "--no-check-certificate", "-O-", "https://localhost:8080/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
deploy:
resources:
limits:
cpus: "0.5"
memory: 128M
restart: unless-stopped
# ---------------------------------------------------------------------------
# PostgreSQL 16
# ---------------------------------------------------------------------------
postgres:
image: postgres:16.3-alpine
container_name: postgres
env_file: .env
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- waf-internal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
restart: unless-stopped
# ---------------------------------------------------------------------------
# Redis 7
# ---------------------------------------------------------------------------
redis:
image: redis:7.2.5-alpine
container_name: redis
user: redis
command: ["redis-server", "--appendonly", "yes"]
volumes:
- redis-data:/data
networks:
- waf-internal
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
start_period: 5s
deploy:
resources:
limits:
cpus: "0.5"
memory: 256M
restart: unless-stopped
# =============================================================================
# Networks
# =============================================================================
networks:
proxy:
external: true
proxydb:
external: true
waf-internal:
driver: bridge
# =============================================================================
# Named volumes
# =============================================================================
volumes:
coraza-rules:
coraza-custom:
coraza-logs:
caddy-config:
crs-data:
geoip-data:
postgres-data:
redis-data:
tls-certs:
@@ -0,0 +1,3 @@
Account ID: 1370812
License key: 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
Download link: https://download.maxmind.com/app/geoip_download_by_token?date=20260630&edition_id=GeoLite2-City&suffix=tar.gz&token=v2.local.tR9DihzEGIIYMbcbukGNpnBXVsi6EAxtwhQu147OaPcWj_fvkcboMf5hMqL3Wqn0rD8KpW6ujxku8yNaaL3Ylr9LsshJdIHWns1V14pzm82AKrQ9oldDCS-yR7a4kRow8Ofmhr4KBxo1DxoHfTiI8JJB_5qRDwI5ugbmiJr65lzkZKxfpQGCr9kMcM4ueZJzWlJqyQQ
@@ -0,0 +1,7 @@
MYSQL_ROOT_PASSWORD=rhDGjilZm6XEMJ
MYSQL_DATABASE=npm
MYSQL_USER=npm
MYSQL_PASSWORD=rhDGjilZm6XEMJ
# TZ for container timezone
TZ=America/New_York
@@ -0,0 +1,101 @@
# Docker Compose File Guide
## Overview
The Docker Compose file is a YAML file defining services, networks, and volumes. This file is used to spin up multiple containers at once using docker-compose CLI tool. It's designed to be a part of your development process rather than being deployed on its own.
In this guide, we will explore the `docker-compose.yml` file in detail and provide steps for its installation and usage.
## Understanding Docker Compose File
Let's take a look at an example of a docker compose file:
```yaml
version: '3'
services:
db:
image: 'jc21/mariadb-aria:latest'
container_name: npm-db
restart: unless-stopped
env_file: .env
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=${MYSQL_DATABASE}
- MYSQL_USER=${MYSQL_USER}
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
- MARIADB_AUTO_UPGRADE=1
volumes:
- ./data/mysql:/var/lib/mysql
networks:
- proxy
- proxydb
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u${MYSQL_USER}", "-p${MYSQL_PASSWORD}"]
interval: 20s
timeout: 5s
retries: 5
start_period: 30s
app:
image: 'jc21/nginx-proxy-manager:latest'
container_name: nginx-proxy-manager
restart: unless-stopped
env_file: .env
environment:
- TZ=${TZ}
- DB_MYSQL_HOST=db
- DB_MYSQL_PORT=3306
- DB_MYSQL_USER=${MYSQL_USER}
- DB_MYSQL_PASSWORD=${MYSQL_PASSWORD}
- DB_MYSQL_NAME=${MYSQL_DATABASE}
ports:
- '80:80'
- '81:81'
- '443:443'
volumes:
- ./data/npm:/data
- ./data/letsencrypt:/etc/letsencrypt
depends_on:
- db
networks:
- proxy
- proxydb
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:81/api/status"]
interval: 20s
timeout: 5s
retries: 5
start_period: 40s
networks:
proxy:
external: true
proxydb:
external: true
```
This Docker Compose file defines two services, `db` and `app`. Each service is defined with its own configuration such as the image to use, ports to expose, volumes to mount, etc. It also has a network section where it lists the networks that these services should connect to.
.env
```yaml
MYSQL_ROOT_PASSWORD=rhDGjilZm6XEMJ
MYSQL_DATABASE=npm
MYSQL_USER=npm
MYSQL_PASSWORD=rhDGjilZm6XEMJ
# TZ for container timezone
TZ=America/New_York
```
## Installation Guide
1. **Install Docker**: Follow instructions on the official [Docker website](https://docs.docker.com/get-docker/) for your specific operating system.
2. **Install Docker Compose**: On Linux, run `sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose` followed by `sudo chmod +x /usr/local/bin/docker-compose`. On Windows or MacOS, follow the instructions on [Docker Compose's Github page](https://github.com/docker/compose).
3. **Pull Docker Images**: Before running docker-compose up, make sure to pull all necessary images with `docker-compose pull` command.
## Running the Services
4. **Running the services**: In your terminal, navigate to the directory containing your docker-compose file and run `docker-compose up -d`. This will start all the services defined in the compose file.
5. **Checking the running status of services**: You can check the status of running services with `docker-compose ps` command.
Remember to replace placeholders like `${MYSQL_ROOT_PASSWORD}`, `${MYSQL_DATABASE}`, and `${MYSQL_USER}`, etc., in your environment variables files (.env) with actual values before starting the services. Also, ensure all paths mentioned in volumes section exist on your system or adjust them accordingly.
## Conclusion
With this guide you should now have a solid understanding of Docker Compose file and how to use it for running multi-container applications. It's worth noting that Docker Compose is just one tool among many, each having its own strengths and weaknesses depending on the specific use case.
@@ -0,0 +1,61 @@
---
services:
db:
image: 'jc21/mariadb-aria:latest'
container_name: npm-db
restart: unless-stopped
env_file: .env
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=${MYSQL_DATABASE}
- MYSQL_USER=${MYSQL_USER}
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
- MARIADB_AUTO_UPGRADE=1
volumes:
- ./data/mysql:/var/lib/mysql
networks:
- proxy
- proxydb
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u${MYSQL_USER}", "-p${MYSQL_PASSWORD}"]
interval: 20s
timeout: 5s
retries: 5
start_period: 30s
app:
image: 'jc21/nginx-proxy-manager:latest'
container_name: nginx-proxy-manager
restart: unless-stopped
env_file: .env
environment:
- TZ=${TZ}
- DB_MYSQL_HOST=db
- DB_MYSQL_PORT=3306
- DB_MYSQL_USER=${MYSQL_USER}
- DB_MYSQL_PASSWORD=${MYSQL_PASSWORD}
- DB_MYSQL_NAME=${MYSQL_DATABASE}
ports:
- '80:80'
- '81:81'
- '443:443'
volumes:
- ./data/npm:/data
- ./data/letsencrypt:/etc/letsencrypt
depends_on:
- db
networks:
- proxy
- proxydb
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:81/api/status"]
interval: 20s
timeout: 5s
retries: 5
start_period: 40s
networks:
proxy:
external: true
proxydb:
external: true
@@ -0,0 +1,19 @@
services:
zoraxy:
image: zoraxydocker/zoraxy:latest
container_name: zoraxy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "8000:8000"
environment:
FASTGEOIP: "true"
TZ: "America/New_York"
volumes:
- ./config:/opt/zoraxy/config
- ./plugin:/opt/zoraxy/plugin
- /var/run/docker.sock:/var/run/docker.sock
- /etc/localtime:/etc/localtime:ro
extra_hosts:
- "host.docker.internal:host-gateway"
@@ -0,0 +1,31 @@
# To see all available options, please visit the docs:
# https://docs.digpangolin.com/self-host/advanced/config-file
gerbil:
start_port: 51820
base_endpoint: "pangolin.mmcfetridge.net"
app:
dashboard_url: "https://pangolin.mmcfetridge.net"
log_level: "info"
telemetry:
anonymous_usage: true
domains:
domain1:
base_domain: "mmcfetridge.net"
cert_resolver: "letsencrypt"
server:
secret: "D0fDlOKHBRlaA7c88a9O89yd4anznpZd"
cors:
origins: [ "https://pangolin.mmcfetridge.net" ]
methods: [ "GET", "POST", "PUT", "DELETE", "PATCH" ]
allowed_headers: [ "X-CSRF-Token", "Content-Type" ]
credentials: false
flags:
require_email_verification: false
disable_signup_without_invite: true
disable_user_create_org: false
allow_raw_resources: true
@@ -0,0 +1,53 @@
http:
middlewares:
redirect-to-https:
redirectScheme:
scheme: https
routers:
# HTTP to HTTPS redirect router
main-app-router-redirect:
rule: "Host(`pangolin.mmcfetridge.net`)"
service: next-service
entryPoints:
- web
middlewares:
- redirect-to-https
# Next.js router (handles everything except API and WebSocket paths)
next-router:
rule: "Host(`pangolin.mmcfetridge.net`) && !PathPrefix(`/api/v1`)"
service: next-service
entryPoints:
- websecure
tls:
certResolver: letsencrypt
# API router (handles /api/v1 paths)
api-router:
rule: "Host(`pangolin.mmcfetridge.net`) && PathPrefix(`/api/v1`)"
service: api-service
entryPoints:
- websecure
tls:
certResolver: letsencrypt
# WebSocket router
ws-router:
rule: "Host(`pangolin.mmcfetridge.net`)"
service: api-service
entryPoints:
- websecure
tls:
certResolver: letsencrypt
services:
next-service:
loadBalancer:
servers:
- url: "http://pangolin:3002" # Next.js server
api-service:
loadBalancer:
servers:
- url: "http://pangolin:3000" # API/WebSocket server
@@ -0,0 +1,53 @@
api:
insecure: true
dashboard: true
providers:
http:
endpoint: "http://pangolin:3001/api/v1/traefik-config"
pollInterval: "5s"
file:
filename: "/etc/traefik/dynamic_config.yml"
experimental:
plugins:
badger:
moduleName: "github.com/fosrl/badger"
version: "v1.2.0"
log:
level: "INFO"
format: "common"
maxSize: 100
maxBackups: 3
maxAge: 3
compress: true
certificatesResolvers:
letsencrypt:
acme:
httpChallenge:
entryPoint: web
email: "mmcfetridg@aol.com"
storage: "/letsencrypt/acme.json"
caServer: "https://acme-v02.api.letsencrypt.org/directory"
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
transport:
respondingTimeouts:
readTimeout: "30m"
http:
tls:
certResolver: "letsencrypt"
serversTransport:
insecureSkipVerify: true
ping:
entryPoint: "web"
@@ -0,0 +1,65 @@
name: pangolin
services:
pangolin:
image: docker.io/fosrl/pangolin:latest
container_name: pangolin
restart: unless-stopped
volumes:
- ./config:/app/config
- pangolin-data:/var/certificates
- pangolin-data:/var/dynamic
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
interval: "10s"
timeout: "10s"
retries: 15
gerbil:
image: docker.io/fosrl/gerbil:latest
container_name: gerbil
restart: unless-stopped
depends_on:
pangolin:
condition: service_healthy
command:
- --reachableAt=http://gerbil:3003
- --generateAndSaveKeyTo=/var/config/key
- --remoteConfig=http://pangolin:3001/api/v1/
volumes:
- ./config/:/var/config
cap_add:
- NET_ADMIN
- SYS_MODULE
ports:
- 51820:51820/udp
- 21820:21820/udp
- 443:443
- 80:80
traefik:
image: docker.io/traefik:latest
container_name: traefik
restart: unless-stopped
network_mode: service:gerbil # Ports appear on the gerbil service
depends_on:
pangolin:
condition: service_healthy
command:
- --configFile=/etc/traefik/traefik_config.yml
volumes:
- ./config/traefik:/etc/traefik:ro # Volume to store the Traefik configuration
- ./config/letsencrypt:/letsencrypt # Volume to store the Let's Encrypt certificates
- ./config/traefik/logs:/var/log/traefik # Volume to store Traefik logs
# Shared volume for certificates and dynamic config in file mode
- pangolin-data:/var/certificates:ro
- pangolin-data:/var/dynamic:ro
networks:
default:
driver: bridge
name: pangolin
volumes:
pangolin-data:
@@ -0,0 +1 @@
mkdir -p config/traefik config/db config/letsencrypt config/logs