diff --git a/README.md b/README.md index 97465b3..6adca58 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ ScaleTail provides ready-to-run [Docker Compose](https://docs.docker.com/compose | ๐Ÿงฉ **Pi-hole** | A network-level ad blocker that acts as a DNS sinkhole. | [Details](services/pihole) | | ๐Ÿ†” **Pocket ID** | A self-hosted decentralized identity (OIDC) solution for secure authentication. | [Details](services/pocket-id) | | ๐ŸŒ **Rustdesk Server** | RustDesk is an open source remote control alternative for self-hosting and security. | [Details](services/rustdesk-server) | +| ๐Ÿฐ **SOCFortress WAF** | Self-hosted Web Application Firewall with a modern admin UI (using Caddy + Coraza). | [Details](services/socfortress-waf) | | ๐Ÿ”’ **Technitium DNS** | An open-source DNS server that can be used for self-hosted DNS services. | [Details](services/technitium) | | ๐ŸŒ **Traefik** | A modern reverse proxy and load balancer for microservices. | [Details](services/traefik) | | ๐ŸŒ **Tailscale App Connector Node** | Configure a device to act as a App connector node for your Tailscale network. | [Details](services/tailscale-app-connector-node) | @@ -206,6 +207,7 @@ ScaleTail provides ready-to-run [Docker Compose](https://docs.docker.com/compose | ๐Ÿ  Service | ๐Ÿ“ Description | ๐Ÿ”— Link | | -------------------- | ---------------------------------------------------------------------- | ---------------------------------- | | ๐Ÿก **Home Assistant** | An open-source home automation platform for controlling smart devices. | [Details](services/home-assistant) | +| **Home Bridge** | Homebridge is a lightweight Node.js server that emulates the HomeKit API. | [Details](services/homebridge) | ### ๐Ÿ“ฑ Utilities diff --git a/services/homebridge/.env b/services/homebridge/.env new file mode 100644 index 0000000..827da40 --- /dev/null +++ b/services/homebridge/.env @@ -0,0 +1,18 @@ +# ============================================================================= +# Homebridge + Tailscale Sidecar Environment +# Copy to .env and fill values. Keep TS_AUTHKEY secret! +# ============================================================================= + +# --- REQUIRED: Tailscale --- +# Get a reusable or ephemeral auth key from https://login.tailscale.com/admin/settings/keys +# Recommended: auth key with "reusable" + "ephemeral" for containers, tagged if using ACLs +TS_AUTHKEY=tskey-auth-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + +# --- REQUIRED: Service identity --- +SERVICE=homebridge +IMAGE_URL=homebridge/homebridge:latest + +# --- Common container settings --- +TZ=Europe/Amsterdam +PUID=1000 +PGID=1000 diff --git a/services/homebridge/README.md b/services/homebridge/README.md new file mode 100644 index 0000000..151d132 --- /dev/null +++ b/services/homebridge/README.md @@ -0,0 +1,77 @@ +# homebridge with Tailscale Sidecar + +This Docker Compose configuration sets up [Homebridge](https://github.com/homebridge/homebridge) with Tailscale as a sidecar container. Homebridge emulates the iOS HomeKit API, enabling HomeKit integration for non-Apple smart home devices. The Homebridge UI is reachable **only over your Tailnet** via HTTPS. + +## โš ๏ธ Critical Limitation + +**Homebridge requires `host` network mode for HomeKit mDNS discovery.** This sidecar configuration isolates Homebridge into a separate network namespace, which **breaks HomeKit discovery entirely**. HomeKit clients will not be able to find or control your devices. + +**What works:** +- Access the Homebridge UI web interface over HTTPS via Tailnet: `https://homebridge.your-tailnet.ts.net` +- Install plugins, edit config, view logs +- Full remote administration + +**What doesn't work:** +- HomeKit device discovery on your local network +- HomeKit app integration from iPhone/iPad +- Siri control via HomeKit + +If you need HomeKit discovery, run Homebridge with `network_mode: host` instead (standard Docker setup, no Tailscale sidecar). If you only need the web UI for administration from Tailnet, proceed with this configuration. + +## About Homebridge + +Homebridge is a lightweight Node.js server that emulates the HomeKit API. It translates between HomeKit and non-HomeKit smart home devices (Philips Hue, IKEA TRADFRI, etc.), allowing them to integrate into the Apple Home ecosystem. This image includes FFmpeg with libfdk-aac for camera streaming support. + +## Architecture + +- `tailscale`: Runs the Tailscale client, obtains a Tailnet IP, serves HTTPS on :443 proxying to Homebridge UI on internal port 8581. +- `homebridge`: Runs in the **same network namespace** as the tailscale container. Configuration and plugins persist in a dedicated volume. + +Access the UI at: `https://homebridge.your-tailnet.ts.net` (after auth and healthy state). + +## Prerequisites + +- Docker & Docker Compose v2.20+ +- Tailscale account + ability to create auth keys +- Host user in `docker` group (or use sudo) +- Pre-create volume directories (recommended): + ```bash + mkdir -p homebridge-data/config ts-homebridge-state + ``` + +## First Run & Setup + +On first boot, Homebridge generates a unique setup code in the logs. Find it with: +```bash +docker compose logs homebridge | grep -i "setup code" +``` + +You'll use this code if setting up HomeKit bridges (only if HomeKit discovery were enabled). For web UI access, no setup code is needed. + +## Configuration + +### Important Notes + +- **ENABLE_AVAHI=0**: Avahi (mDNS daemon) is disabled since the sidecar network prevents proper mDNS broadcasts. HomeKit discovery is not functional in this setup. +- **Tailscale Serve**: The sidecar automatically proxies port 443 to the Homebridge UI on 8581. HTTPS certificates are auto-provisioned by Tailscale. +- **Plugins**: Install via the Homebridge UI. They persist in the mounted volume. + +### Optional Environment Variables + +Add to `compose.yml` under the `homebridge` service `environment:` block if needed: +- `HB_PORT=8581` โ€” Web UI port (change if desired, but update compose.tailscale.yml Proxy port to match) + +## Upstream Documentation + +- [Homebridge GitHub](https://github.com/homebridge/homebridge) +- [Homebridge Docker Guide](https://github.com/homebridge/homebridge/wiki/Install-Homebridge-on-Docker) +- [Homebridge Plugins](https://www.npmjs.com/search?q=homebridge-plugin) +- [Homebridge UI](https://github.com/homebridge/homebridge-config-ui-x) +- [Tailscale Docker Guide](https://tailscale.com/blog/docker-tailscale-guide) + +## Troubleshooting + +- **Container unhealthy**: Check `docker compose logs tailscale` and `docker compose logs homebridge` +- **Can't reach UI over HTTPS**: Verify Tailscale connection with `docker compose exec tailscale tailscale status`. If not logged in, check TS_AUTHKEY. +- **UI timeout or 502**: Ensure Homebridge is running with `docker compose logs homebridge`. Check that internal port 8581 is correct. +- **HomeKit discovery not working**: This is expected. The sidecar breaks mDNS. Run with `network_mode: host` for full HomeKit support. \ No newline at end of file diff --git a/services/homebridge/compose.yml b/services/homebridge/compose.yml new file mode 100644 index 0000000..7532018 --- /dev/null +++ b/services/homebridge/compose.yml @@ -0,0 +1,71 @@ +# ============================================================================= +# Homebridge + Tailscale Sidecar Compose +# WARNING: This config breaks HomeKit discovery. See README for details. +# Always validate with: docker compose config +# ============================================================================= +configs: + ts-serve: + content: | + {"TCP":{"443":{"HTTPS":true}}, + "Web":{"$${TS_CERT_DOMAIN}:443": + {"Handlers":{"/": + {"Proxy":"http://127.0.0.1:8581"}}}}, + "AllowFunnel":{"$${TS_CERT_DOMAIN}:443":false}} + +services: + tailscale: + image: tailscale/tailscale:latest + container_name: ts-${SERVICE} + hostname: ${SERVICE} + environment: + - TS_AUTHKEY=${TS_AUTHKEY} + - TS_STATE_DIR=/var/lib/tailscale + - TS_SERVE_CONFIG=/config/serve.json # Tailscale Serve configuration to expose the web interface on your local Tailnet - remove this line if not required + #- TS_USERSPACE=false + - TS_ENABLE_HEALTH_CHECK=true # Enable healthcheck endpoint: "/healthz" + - TS_LOCAL_ADDR_PORT=127.0.0.1:41234 # The : for the healthz endpoint + #- TS_ACCEPT_DNS=true # Uncomment when using MagicDNS + - TS_AUTH_ONCE=true + configs: + - source: ts-serve + target: /config/serve.json + volumes: + - ./config:/config # Config folder used to store Tailscale files - you may need to change the path + - ./ts/state:/var/lib/tailscale # Tailscale requirement - you may need to change the path + devices: + - /dev/net/tun:/dev/net/tun # Network configuration for Tailscale to work + cap_add: + - net_admin + - sys_module + restart: always + ports: + # - "5353:5353" #Receive mDNS but does not do anything for Apple HomeBridge + # - "443:443" #Uncomment for local acces + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:41234/healthz"] # Check Tailscale has a Tailnet IP and is operational + interval: 1m # How often to perform the check + timeout: 10s # Time to wait for the check to succeed + retries: 3 # Number of retries before marking as unhealthy + start_period: 10s # Time to wait before starting health checks + + homebridge: + image: ${IMAGE_URL} + network_mode: service:tailscale + container_name: app-${SERVICE} + environment: + - PUID=${PUID} + - PGID=${PGID} + - TZ=${TZ} + - ENABLE_AVAHI=0 + volumes: + - ./${SERVICE}-data/config:/homebridge + depends_on: + tailscale: + condition: service_healthy + healthcheck: + test: ["CMD", "curl", "-f", "http://127.0.0.1:8581"] + interval: 60s + timeout: 10s + retries: 3 + start_period: 30s + restart: always diff --git a/services/socfortress-waf/.env b/services/socfortress-waf/.env new file mode 100644 index 0000000..017a15f --- /dev/null +++ b/services/socfortress-waf/.env @@ -0,0 +1,55 @@ +# ============================================================================= +# WAF Management Platform + Tailscale sidecar โ€” Environment +# Copy to .env and fill every CHANGE_ME value. Never commit .env. +# ============================================================================= + +# --- Tailscale sidecar (admin UI over your Tailnet) ---------------------- +# Reusable or ephemeral auth key: https://login.tailscale.com/admin/settings/keys +TS_AUTHKEY=tskey-auth-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +# MagicDNS hostname -> admin UI lands at https://..ts.net +TS_HOSTNAME=socfortressWAF +# Optional, e.g. if your tailnet uses ACL tags: --advertise-tags=tag:container +# TS_EXTRA_ARGS= + +# --- Image version ------------------------------------------------------- +# Pin a release tag (e.g. v1.0.0) for reproducible deploys; "latest" tracks newest. +WAF_IMAGE_TAG=latest + +# --- GeoIP (MaxMind GeoLite2) -------------------------------------------- +# Host path to your own GeoLite2-City.mmdb (see README -> GeoIP setup). +GEOIP_DB_PATH=./GeoLite2-City.mmdb + +# --- PostgreSQL ---------------------------------------------------------- +POSTGRES_DB=wafdb +POSTGRES_USER=wafuser +POSTGRES_PASSWORD=CHANGE_ME +POSTGRES_HOST=postgres +POSTGRES_PORT=5432 + +# --- Redis --------------------------------------------------------------- +REDIS_URL=redis://redis:6379/0 + +# --- API security -------------------------------------------------------- +# python3 -c "import secrets; print(secrets.token_hex(32))" +SECRET_KEY=CHANGE_ME_at_least_32_random_characters +# python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" +TOTP_ENCRYPTION_KEY=CHANGE_ME_generate_with_command_above + +# CORS โ€” MUST list the Tailnet URL you log in from (no wildcard allowed). +# Replace with your tailnet name (see README -> Gotchas). +ALLOWED_ORIGINS=https://waf-admin..ts.net + +# --- Caddy Admin API (container-internal) -------------------------------- +CADDY_ADMIN_URL=http://caddy-waf:2019 + +# --- SMTP (optional, for alert notifications) ---------------------------- +SMTP_HOST= +SMTP_PORT=587 +SMTP_USER= +SMTP_PASSWORD= +SMTP_FROM= + +# --- Bootstrap superadmin (seeded on first run only) --------------------- +# Change the password immediately after first login. +BOOTSTRAP_ADMIN_EMAIL=admin@example.com +BOOTSTRAP_ADMIN_PASSWORD=CHANGE_ME \ No newline at end of file diff --git a/services/socfortress-waf/README.md b/services/socfortress-waf/README.md new file mode 100644 index 0000000..b90d177 --- /dev/null +++ b/services/socfortress-waf/README.md @@ -0,0 +1,83 @@ +# SOCFortress WAF with Tailscale (admin UI on your Tailnet) + +This adds a Tailscale sidecar to the [SOCFortress WAF Management Platform](https://github.com/socfortress/waf-platform-public) so the **admin UI is reachable only over your Tailnet** at `https://..ts.net`, with a valid HTTPS certificate. The **WAF data plane is left exactly as upstream ships it** โ€” `caddy-waf` still listens on host ports **80/443** so it can front your protected sites with automatic per-site Let's Encrypt certs and see real client IPs. + +Funnel (public internet exposure) is **intentionally disabled**. + +## About this setup + +The WAF platform is a six-service stack (Caddy+Coraza engine, FastAPI admin API, React/Nginx admin UI, PostgreSQL, Redis, and a demo upstream). Only one piece โ€” the **admin console** โ€” benefits from being private-by-default, and that is exactly what a WAF operator wants: keep the control panel off the public internet, reach it securely from anywhere over Tailscale. The Tailscale container joins the stack's `waf-internal` network as a normal peer and reverse-proxies to `admin-ui:8080` by its Docker DNS name. Nothing about how the WAF protects traffic changes. + +## Architecture + +- `tailscale` (from `compose.tailscale.yml`): joins your Tailnet, terminates HTTPS on `:443` using the node's `*.ts.net` certificate, and proxies to the admin UI. **Tailnet-only** (`AllowFunnel: false`). +- `admin-ui`: serves the console on internal `:8080` over HTTPS with a self-signed cert. The sidecar reaches it via `https+insecure://admin-ui:8080` (the public-facing `*.ts.net` cert is valid, so your browser sees no warning). +- `caddy-waf` and the rest of the stack: unchanged. Data plane stays on host `80/443`. + +Access the admin UI at: `https://..ts.net` (after first auth + HTTPS enabled in your tailnet). + +## Quick-ish Start + +1. Drop these four files into the cloned `waf-platform` directory (next to the upstream `docker-compose.yml`): + - `.env`, `compose.yml`, `compose.tailscale.yml`, `README.md` +2. Copy and edit the env file: + ```bash + cp .env .env # already named .env here; edit it in place + ``` + Fill every `CHANGE_ME`, set a real `TS_AUTHKEY`, and set `ALLOWED_ORIGINS` to your Tailnet URL (see [Gotchas](#important-notes--gotchas)). Generate secrets with: + ```bash + openssl rand -hex 32 # POSTGRES_PASSWORD + python3 -c "import secrets; print(secrets.token_hex(32))" # SECRET_KEY + python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" # TOTP_ENCRYPTION_KEY + ``` +3. (Recommended) Make the admin UI **Tailnet-only** by removing its host port. In the upstream `docker-compose.yml`, comment out the two `ports:` lines under the `admin-ui` service: + ```yaml + admin-ui: + ... + # ports: + # - "8443:8080" + ``` + Leave them in if you also want LAN access on `https://:8443`. +4. Validate (see [Linting](#linting--validation)), then start everything: + ```bash + docker compose up -d + docker compose ps + docker compose logs -f tailscale # watch for "Logged in as..." and serve config applied + ``` +5. Find your node's URL (`tailscale status` inside the container, or the Tailscale admin console). It will be `https://..ts.net`. +6. Make sure `ALLOWED_ORIGINS` in `.env` matches that exact URL, then apply it: + ```bash + docker compose up -d admin-api + ``` +7. From any device on your Tailnet, open `https://..ts.net`, log in with the bootstrap admin, change the password, and enroll TOTP. + +## Important Notes & Gotchas + +- **CORS is the #1 thing people miss.** The admin API rejects origins not in `ALLOWED_ORIGINS` (no wildcard). When you move login to the Tailnet URL, you **must** set `ALLOWED_ORIGINS=https://..ts.net` and recreate `admin-api`. Symptoms if you forget: the page loads but login/API calls fail. +- **Enable HTTPS in your tailnet** (Admin console โ†’ DNS โ†’ MagicDNS + HTTPS Certificates). Without it, Tailscale Serve cannot provision the `*.ts.net` certificate and the serve config is skipped. Public DNS for a fresh tailnet name can take up to ~10 minutes. +- **Self-signed backend is expected.** The admin UI's internal cert is self-signed, so the sidecar uses `https+insecure://admin-ui:8080`. Your browser still sees a valid Let's Encrypt cert because Tailscale terminates TLS at the edge with the `*.ts.net` cert. +- **The data plane is still public.** `caddy-waf` keeps listening on host `80/443`. Point DNS for the sites you protect at this host as you normally would; Caddy's automatic per-site TLS and GeoIP/client-IP features are unaffected. Tailscale is only in front of the admin UI. +- **Updating:** `docker compose pull && docker compose up -d`. Data lives in named volumes and survives updates. The Tailscale node identity persists in the `tailscale-state` volume. + +## Files + +- `.env` โ€” all WAF variables + the Tailscale section (`TS_AUTHKEY` is critical; keep private). +- `compose.yml` โ€” orchestrator; `include`s the upstream stack and the sidecar. Auto-selected by `docker compose`. +- `compose.tailscale.yml` โ€” the Tailscale sidecar service + inline serve config. +- `docker-compose.yml` โ€” upstream, unchanged (provided by the cloned repo; not in this bundle). + +## Upstream Documentation + +- [SOCFortress WAF repo](https://github.com/socfortress/waf-platform-public) +- [Tailscale Serve](https://tailscale.com/kb/1242/tailscale-serve) ยท [Tailscale + Docker guide](https://tailscale.com/blog/docker-tailscale-guide) +- [Enabling HTTPS in your tailnet](https://tailscale.com/kb/1153/enabling-https) + +## Troubleshooting + +- **`serve proxy: no serve config ... skipping`** โ€” HTTPS isn't enabled in your tailnet yet, or the config file path is wrong. Enable HTTPS, then `docker compose up -d tailscale`. If it persists, switch from the inline `configs:` block to a bind-mounted file: create `./config/serve.json` with the same JSON (use literal `${TS_CERT_DOMAIN}`), and mount `./config:/config` on the `tailscale` service. +- **Browser cert warning / no page** โ€” the `*.ts.net` cert may still be provisioning (wait a few minutes), or HTTPS isn't enabled in the tailnet. +- **502 / connection refused at the `.ts.net` URL** โ€” `admin-ui` not healthy yet (`docker compose ps`), or its internal port isn't 8080; check `docker compose logs admin-ui`. +- **Login/API fails after the page loads** โ€” `ALLOWED_ORIGINS` doesn't match the Tailnet URL. Fix `.env` and `docker compose up -d admin-api`. +- **No Tailnet IP** โ€” auth key expired/invalid, or ACL/tag restrictions; check `docker compose logs tailscale`. + +--- diff --git a/services/socfortress-waf/compose.yml b/services/socfortress-waf/compose.yml new file mode 100644 index 0000000..675b0e8 --- /dev/null +++ b/services/socfortress-waf/compose.yml @@ -0,0 +1,252 @@ +--- +# ============================================================================= +# SOCFortress WAF Management Platform + Tailscale sidecar +# ============================================================================= +# Serve config (inline). $$ escapes the $ so ${TS_CERT_DOMAIN} survives Compose +# interpolation and is expanded to this node's FQDN by the Tailscale container. +# ============================================================================= +configs: + ts_serve: + content: | + { + "TCP": { "443": { "HTTPS": true } }, + "Web": { + "$${TS_CERT_DOMAIN}:443": { + "Handlers": { "/": { "Proxy": "https+insecure://admin-ui:8080" } } + } + }, + "AllowFunnel": { "$${TS_CERT_DOMAIN}:443": false } + } +# ============================================================================= +# Networks +# ============================================================================= +networks: + waf-internal: + driver: bridge +services: + # --------------------------------------------------------------------------- + # Tailscale sidecar โ€” serves the admin UI over your Tailnet (private). + # + # Joins waf-internal as a normal peer and reverse-proxies to admin-ui:8080 + # by Docker DNS name. The UI's internal cert is self-signed, hence the + # https+insecure backend; the public-facing *.ts.net cert is valid. + # Tailnet-only โ€” Funnel is disabled (AllowFunnel: false). + # --------------------------------------------------------------------------- + tailscale: + image: tailscale/tailscale:latest + container_name: ts-${TS_HOSTNAME} + hostname: ${TS_HOSTNAME} # MagicDNS -> ${TS_HOSTNAME}..ts.net + environment: + - TS_AUTHKEY=${TS_AUTHKEY:?Set TS_AUTHKEY in .env} + - TS_HOSTNAME=${TS_HOSTNAME} + - TS_STATE_DIR=/var/lib/tailscale + - TS_USERSPACE=true # no NET_ADMIN / tun device required + - TS_SERVE_CONFIG=/config/serve.json + - TS_EXTRA_ARGS=${TS_EXTRA_ARGS:-} + configs: + - source: ts_serve + target: /config/serve.json + volumes: + - tailscale-state:/var/lib/tailscale + networks: + - waf-internal + depends_on: + admin-ui: + condition: service_started + healthcheck: + test: ["CMD", "tailscale", "status"] + interval: 1m + timeout: 10s + retries: 3 + start_period: 30s + restart: unless-stopped + + # --------------------------------------------------------------------------- + # Caddy + Coraza: WAF engine and reverse proxy (public data plane) + # --------------------------------------------------------------------------- + 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:. 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 + 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). + - ${GEOIP_DB_PATH:-./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 (Nginx, HTTPS on internal 8080) + # + # Tailnet-only by default: the Tailscale sidecar below serves this UI over + # your Tailnet. Uncomment the ports block to ALSO expose it on the host LAN + # at https://: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 + +# ============================================================================= +# Named volumes +# ============================================================================= +volumes: + coraza-rules: + coraza-custom: + coraza-logs: + caddy-config: + crs-data: + geoip-data: + postgres-data: + redis-data: + tls-certs: + tailscale-state: