# ============================================================================= # IRDB — IP Reputation Database — environment configuration # ============================================================================= # Copy this file to `.env` and fill in the blanks. # Generate 32-byte hex secrets with: openssl rand -hex 32 # ============================================================================= # ----------------------------------------------------------------------------- # Docker host ports (consumed by docker-compose.yml only) # ----------------------------------------------------------------------------- # Host-side ports for the published service mappings. Container-internal # ports are fixed at 8081 (api) and 8080 (ui) — only the host side here is # remappable, so port collisions on the host can be resolved without # rebuilding the images. If you change UI_PORT, also update PUBLIC_URL # below so the browser-facing URL matches. API_PORT=8081 UI_PORT=8080 # ----------------------------------------------------------------------------- # Shared (consumed by both api and ui containers) # ----------------------------------------------------------------------------- # IRDB-format service token. The api uses this to authenticate the ui's # calls; the ui presents it on every API request together with # X-Acting-User-Id. Format: irdb_svc_<32 base32 chars>. Generate one with # (note `--entrypoint php` — the api image's default entrypoint is a # dispatcher with `api` / `migrate` modes, so ad-hoc PHP bypasses it): # docker compose run --rm -T --entrypoint php api -r 'require "/app/vendor/autoload.php"; # echo (new App\Domain\Auth\TokenIssuer())->issue(App\Domain\Auth\TokenKind::Service), PHP_EOL;' UI_SERVICE_TOKEN= # ----------------------------------------------------------------------------- # api container # ----------------------------------------------------------------------------- APP_ENV=production # development | production LOG_LEVEL=info # Database DB_DRIVER=sqlite # sqlite | mysql DB_SQLITE_PATH=/data/irdb.sqlite DB_MYSQL_HOST= DB_MYSQL_PORT=3306 DB_MYSQL_DATABASE= DB_MYSQL_USERNAME= DB_MYSQL_PASSWORD= # OIDC role mapping (defaults applied if no group mapping matches) OIDC_DEFAULT_ROLE=viewer # viewer | none # Reputation engine SCORE_RECOMPUTE_INTERVAL_SECONDS=300 SCORE_REPORT_HARD_CUTOFF_DAYS=365 # Internal jobs INTERNAL_JOB_TOKEN= # 32-byte hex (api refuses to boot if shorter than 32 hex chars) # Comma- or whitespace-separated CIDR list of sources allowed to reach # /internal/*. Empty (the default) means loopback-only (127.0.0.1/32 + # ::1/128). The bundled `compose.scheduler.yml` shares the api's network # namespace, so its calls hit loopback and need no extra entries. # Production topologies that genuinely need extra sources (host cron on # a private bridge, etc.) list them here AND mirror them into the api # Caddyfile's @internal matcher. INTERNAL_CIDR_ALLOWLIST= # Comma- or whitespace-separated CIDR list of trusted reverse-proxy IPs # whose `X-Forwarded-For` header Caddy should honour for REMOTE_ADDR # rewriting. Default (loopback-only) means no XFF rewriting from any # real client. Set to your reverse proxy's CIDR (e.g. "10.0.0.5/32") if # the api sits behind one — required for accurate audit-log source IPs. # SEC_REVIEW F25: never include broad RFC1918 ranges in deployments # where untrusted neighbours can reach the api on the same docker bridge. TRUSTED_PROXIES= # HSTS header value — applied prod-only by both Caddyfiles. Default is # 1 year + subdomains, NO preload (preload-listing is a one-way # commitment; browser preload removals take months). Operators who # want to apply for the HSTS preload list at https://hstspreload.org/ # set: # HSTS_HEADER="max-age=31536000; includeSubDomains; preload" # SEC_REVIEW F60. HSTS_HEADER= JOB_RECOMPUTE_MAX_RUNTIME_SECONDS=240 JOB_RECOMPUTE_MAX_ROWS_PER_TICK=5000 JOB_AUDIT_RETENTION_DAYS=180 JOB_GEOIP_REFRESH_INTERVAL_DAYS=7 # Manual blocks / allowlist evaluator # In-process cache TTL for the CidrEvaluator. Mutations invalidate explicitly, # so this only matters for cross-replica visibility (per replica is fine). CIDR_EVALUATOR_TTL_SECONDS=60 # Distribution endpoint # Per-policy blocklist cache TTL. Mutations to policies / manual_blocks / # allowlist invalidate explicitly; this is the cross-replica window. BLOCKLIST_CACHE_TTL_SECONDS=30 # GeoIP / ASN enrichment # Three pluggable MMDB providers — pick one. The on-disk paths below are # provider-agnostic; the refresh-geoip job atomic-replaces them with the # selected provider's files. # - dbip (default, no auth required, CC BY 4.0 — UI shows attribution) # - maxmind (opt-in, requires MAXMIND_LICENSE_KEY) # - ipinfo (opt-in, requires IPINFO_TOKEN — UI shows attribution) GEOIP_ENABLED=true GEOIP_PROVIDER=dbip GEOIP_COUNTRY_DB=/data/geoip/country.mmdb GEOIP_ASN_DB=/data/geoip/asn.mmdb MAXMIND_LICENSE_KEY= IPINFO_TOKEN= # CORS — origin of the ui container (or future SPA frontend) UI_ORIGIN=http://localhost:8080 # Rate limiting (public API) API_RATE_LIMIT_PER_SECOND=60 # Public API docs viewer. # `false` (default): /api/docs and /api/v1/openapi.yaml are not # registered — Slim returns 404 — so the OpenAPI spec doesn't leak # the full admin/internal route surface to unauthenticated callers. # Set `true` for open APIs / dev environments where the menu is # meant to be public. SEC_REVIEW F68. API_DOCS_PUBLIC=false # ----------------------------------------------------------------------------- # ui container # ----------------------------------------------------------------------------- # (APP_ENV / LOG_LEVEL above are reused; the ui reads its own copies of those.) PUBLIC_URL=http://localhost:8080 # Where the ui finds the api (internal docker network DNS) API_BASE_URL=http://api:8081 # OIDC (Entra ID) — lives in ui only OIDC_ENABLED=true OIDC_ISSUER=https://login.microsoftonline.com//v2.0 OIDC_CLIENT_ID= OIDC_CLIENT_SECRET= OIDC_REDIRECT_URI=https://reputation.example.com/oidc/callback # Local admin — lives in ui only LOCAL_ADMIN_ENABLED=true LOCAL_ADMIN_USERNAME=admin # Generate with: php -r "echo password_hash('s3cret', PASSWORD_ARGON2ID);" LOCAL_ADMIN_PASSWORD_HASH= # Optional BCP 47 locale fallback for date/time formatting (e.g. "de-CH", # "en-GB"). The browser's locale wins; this is appended as a fallback so # JavaScript's Intl.DateTimeFormat picks something sensible if the # browser's preference isn't supported. Empty = browser-only. UI_LOCALE= # How often (seconds) the UI re-validates the cached user role/identity # against `GET /api/v1/admin/me`. Lower = faster propagation of Entra # group changes and explicit user-disable actions; higher = fewer api # calls per active user. Default 300 (5 min). UI_SESSION_REVALIDATE_SECONDS=300