b8e8870b29
Phase 2-4 of multi-provider authentication. All three providers slot into the AuthOrchestrator from phase 1; no further changes to /auth/login are needed. LDAPS (app/auth/strategies/ldap_strategy.py): - ldap3 with strict TLS cert validation (CERT_REQUIRED + CA bundle) - Service-account search-then-bind flow (DN never exposed to caller) - Filter chars escaped via ldap3.utils.conv.escape_filter_chars - AD-flavored defaults: sAMAccountName / memberOf / objectGUID - Refuses if filter returns >1 entry (anti-impersonation safety) - Bind password encrypted at rest (Fernet), bootstrapped from env on first start then stored in settings table OIDC (app/auth/strategies/oidc_strategy.py + app/routers/auth_oidc.py): - Authorization Code + PKCE (S256) - Strict ID-Token validation via Authlib: signature (JWKS w/ auto-refresh on rotation), iss (essential), aud (essential, must == client_id), exp (essential), nonce (replay protection) - State/PKCE-verifier/nonce stored in signed itsdangerous cookie (no server-side session store needed) - Discovery + JWKS cached in-process; JWKS auto-refetched on key miss - Groups merged from both id_token claims and userinfo endpoint - Hardened: prompt=select_account to defeat silent IdP reuse SAML 2.0 (app/auth/strategies/saml_strategy.py + app/routers/auth_saml.py): - python3-saml (OneLogin) with strict=true; xmlsec1 handles signature validation. XSW attacks mitigated via strict assertion/response signature position checks plus wantAssertionsSigned=true - SP-initiated (/auth/saml/login) + IdP-initiated (POST /auth/saml/acs) - /auth/saml/metadata serves signed SP descriptor - RelayState same-origin check to prevent open redirect - IdP metadata loaded from URL or file at startup Wiring: - app/main.py imports auth_oidc/auth_saml routers behind try/except so the app still starts when authlib or python3-saml aren't installed - Frontend login page fetches /auth/providers and renders matching redirect buttons (Sign in with Entra ID / SAML SSO) plus the local credentials form. Adds MFA second-step screen with 6-digit OTP input when /auth/login returns mfa_required=true. .env.example: full provider config blocks with worked examples for Entra ID, Okta, Keycloak, Google. Each block is commented with the exact format the corresponding admin needs.
174 lines
6.8 KiB
Bash
174 lines
6.8 KiB
Bash
# =============================================================================
|
|
# VulnCheck Environment Configuration
|
|
# =============================================================================
|
|
# Copy this file to .env and adjust values before starting:
|
|
# cp .env.example .env
|
|
#
|
|
# Designed to run behind a reverse proxy (Nginx Proxy Manager, Traefik, etc.)
|
|
# with HTTPS termination at the proxy level.
|
|
# =============================================================================
|
|
|
|
# --- Database ---
|
|
POSTGRES_USER=vulnmanager
|
|
POSTGRES_PASSWORD=changeme # Change this!
|
|
POSTGRES_DB=vulnmanager
|
|
POSTGRES_PORT=5432
|
|
|
|
# --- Application Ports ---
|
|
# These are the internal Docker ports. Map them in your reverse proxy.
|
|
BACKEND_PORT=8022
|
|
FRONTEND_PORT=3003
|
|
|
|
# --- Security (REQUIRED) ---
|
|
# Generate with: openssl rand -hex 32
|
|
JWT_SECRET_KEY=CHANGE-ME-GENERATE-WITH-openssl-rand-hex-32
|
|
|
|
# --- Environment ---
|
|
# 'production' disables Swagger docs and enables security headers.
|
|
# 'development' enables Swagger UI at /docs and relaxes some checks.
|
|
ENV=production
|
|
|
|
# --- Cookies ---
|
|
# Set to true when using HTTPS (recommended, required behind HTTPS proxy)
|
|
AUTH_COOKIE_SECURE=true
|
|
AUTH_COOKIE_SAMESITE=lax
|
|
|
|
# --- Reverse Proxy ---
|
|
# Enable if running behind a reverse proxy to trust X-Forwarded-For headers
|
|
# for correct client IP in rate limiting and audit logs.
|
|
TRUST_PROXY_HEADERS=true
|
|
|
|
# --- Default Admin Account ---
|
|
# Created on first start only (if no admin exists in the database).
|
|
# The password MUST satisfy strength rules (>=8 chars, upper, lower, digit, special),
|
|
# otherwise the admin will NOT be created and login will be impossible.
|
|
# Change the password immediately after first login.
|
|
DEFAULT_ADMIN_USERNAME=admin
|
|
DEFAULT_ADMIN_PASSWORD=ChangeMe123!
|
|
DEFAULT_ADMIN_EMAIL=admin@vulnmanager.local
|
|
|
|
# Set to true to skip default admin creation entirely
|
|
# DISABLE_DEFAULT_ADMIN=false
|
|
|
|
# --- Emergency Admin Reset (optional) ---
|
|
# Uncomment and set a token to enable the /auth/setup-admin endpoint.
|
|
# SETUP_ADMIN_TOKEN=your-setup-token
|
|
# ALLOW_ADMIN_RESET=false
|
|
|
|
# --- Timezone ---
|
|
TIMEZONE=Europe/Zurich
|
|
|
|
# --- Dashboard URL ---
|
|
# Used in email notifications for links back to the dashboard.
|
|
# Set this to your external URL (behind reverse proxy).
|
|
# DASHBOARD_URL=https://vuln.example.com
|
|
|
|
|
|
# =============================================================================
|
|
# Multi-Provider Authentication (LDAP / OIDC / SAML / TOTP-MFA)
|
|
# =============================================================================
|
|
# Default config = local only (backwards compatible). Enable providers
|
|
# below by listing them in AUTH_PROVIDERS and filling in the matching block.
|
|
# =============================================================================
|
|
|
|
# Comma-separated list of enabled providers. Local is always recommended
|
|
# as fallback for emergency admin access.
|
|
AUTH_PROVIDERS=local
|
|
# AUTH_PROVIDERS=local,ldap,oidc,saml
|
|
|
|
# Order in which credential-based providers are tried for username/password
|
|
# login. SSO providers (saml/oidc) are not in this chain — they have their
|
|
# own redirect endpoints.
|
|
AUTH_LOOKUP_ORDER=local,ldap
|
|
|
|
# Auto-create local user stub on first SSO/LDAP login. Re-evaluates role
|
|
# from external groups on every subsequent login.
|
|
AUTH_JIT_PROVISIONING=true
|
|
|
|
# Fallback role when no group-mapping rule matches.
|
|
# Values: admin, editor, readonly
|
|
AUTH_JIT_DEFAULT_ROLE=readonly
|
|
|
|
# Fernet key for encrypting LDAP bind-pw and TOTP secrets at rest.
|
|
# Generate with:
|
|
# python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
|
|
# CHANGE THIS — losing the key invalidates all stored TOTP secrets.
|
|
AUTH_PROVIDER_CRYPTO_KEY=CHANGE-ME-FERNET-KEY
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# LDAPS (Active Directory by default)
|
|
# -----------------------------------------------------------------------------
|
|
LDAP_HOST=ldaps.company.internal
|
|
LDAP_PORT=636
|
|
LDAP_USE_SSL=true
|
|
LDAP_USE_STARTTLS=false # mutually exclusive with USE_SSL
|
|
# CA bundle that signed the LDAPS server cert (required for strict validation).
|
|
LDAP_CA_CERT_PATH=/etc/ssl/certs/company-ca.pem
|
|
LDAP_VALIDATE_CERT=true # NEVER set to false in production
|
|
|
|
# Service account for the search-then-bind flow.
|
|
LDAP_BIND_DN=cn=svc-vulncheck,ou=ServiceAccounts,dc=company,dc=local
|
|
# Bootstrap password — loaded once, encrypted, stored in DB.
|
|
# Remove from env AFTER the first successful start.
|
|
LDAP_BIND_PASSWORD_BOOTSTRAP=
|
|
|
|
# User search
|
|
LDAP_USER_SEARCH_BASE=ou=Users,dc=company,dc=local
|
|
# {username} is replaced by the login form input (filter-escaped).
|
|
LDAP_USER_SEARCH_FILTER=(&(objectClass=user)(sAMAccountName={username}))
|
|
LDAP_USER_ATTR_USERNAME=sAMAccountName
|
|
LDAP_USER_ATTR_EMAIL=mail
|
|
LDAP_USER_ATTR_GROUPS=memberOf
|
|
LDAP_USER_ATTR_GUID=objectGUID # AD stable identifier
|
|
LDAP_USER_ATTR_DISPLAY=displayName
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# OIDC (OpenID Connect) — Entra ID / Okta / Keycloak / Google
|
|
# -----------------------------------------------------------------------------
|
|
OIDC_PROVIDER_NAME=Entra ID
|
|
# Examples:
|
|
# Entra ID: https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration
|
|
# Okta: https://<your-okta-domain>/.well-known/openid-configuration
|
|
# Keycloak: https://kc.company.com/realms/<realm>/.well-known/openid-configuration
|
|
# Google: https://accounts.google.com/.well-known/openid-configuration
|
|
OIDC_DISCOVERY_URL=
|
|
OIDC_CLIENT_ID=
|
|
OIDC_CLIENT_SECRET=
|
|
OIDC_REDIRECT_URI=https://vulncheck.company.com/auth/oidc/callback
|
|
OIDC_SCOPES=openid profile email groups
|
|
|
|
# Claim names — defaults work for Entra ID / Keycloak. Adjust per IdP.
|
|
OIDC_CLAIM_USERNAME=preferred_username
|
|
OIDC_CLAIM_EMAIL=email
|
|
OIDC_CLAIM_GROUPS=groups
|
|
OIDC_CLAIM_DISPLAY=name
|
|
OIDC_CLAIM_SUBJECT=sub
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# SAML 2.0
|
|
# -----------------------------------------------------------------------------
|
|
SAML_PROVIDER_NAME=Single Sign-On
|
|
SAML_SP_ENTITY_ID=https://vulncheck.company.com/auth/saml/metadata
|
|
SAML_SP_ACS_URL=https://vulncheck.company.com/auth/saml/acs
|
|
SAML_SP_SLO_URL=https://vulncheck.company.com/auth/saml/slo
|
|
|
|
# SP cert + key — generate a keypair specifically for this SP:
|
|
# openssl req -x509 -newkey rsa:2048 -nodes \
|
|
# -keyout sp.key -out sp.crt -days 730 \
|
|
# -subj "/CN=vulncheck.company.com"
|
|
SAML_SP_CERT_PATH=/etc/vulncheck/saml/sp.crt
|
|
SAML_SP_PRIVATE_KEY_PATH=/etc/vulncheck/saml/sp.key
|
|
|
|
# IdP metadata source — exactly one of:
|
|
SAML_IDP_METADATA_URL=
|
|
# SAML_IDP_METADATA_PATH=/etc/vulncheck/saml/idp-metadata.xml
|
|
|
|
# Attribute mapping (defaults work for most IdPs)
|
|
SAML_ATTR_USERNAME=urn:oid:0.9.2342.19200300.100.1.1
|
|
SAML_ATTR_EMAIL=urn:oid:1.2.840.113549.1.9.1
|
|
SAML_ATTR_GROUPS=http://schemas.xmlsoap.org/claims/Group
|
|
SAML_ATTR_DISPLAY=urn:oid:2.16.840.1.113730.3.1.241
|