diff --git a/arty.yml b/arty.yml index 0dee40d..c0f1f25 100644 --- a/arty.yml +++ b/arty.yml @@ -125,6 +125,9 @@ envs: # Mailpit SMTP Relay NET_MAILPIT_IMAGE: axllent/mailpit:latest NET_MAILPIT_TRAEFIK_HOST: mailpit.pivoine.art + # Authelia SSO + NET_AUTHELIA_IMAGE: authelia/authelia:latest + NET_AUTHELIA_TRAEFIK_HOST: auth.pivoine.art # AI Stack AI_TRAEFIK_ENABLED: true AI_COMPOSE_PROJECT_NAME: ai diff --git a/core/postgres/init/01-init-databases.sh b/core/postgres/init/01-init-databases.sh index 6bcb9f3..40f6be5 100644 --- a/core/postgres/init/01-init-databases.sh +++ b/core/postgres/init/01-init-databases.sh @@ -49,6 +49,10 @@ psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-E SELECT 'CREATE DATABASE coolify' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'coolify')\\gexec + -- Authelia SSO authentication database + SELECT 'CREATE DATABASE authelia' + WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'authelia')\\gexec + -- Grant privileges to all databases GRANT ALL PRIVILEGES ON DATABASE directus TO $POSTGRES_USER; GRANT ALL PRIVILEGES ON DATABASE umami TO $POSTGRES_USER; @@ -60,11 +64,12 @@ psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-E GRANT ALL PRIVILEGES ON DATABASE asciinema TO $POSTGRES_USER; GRANT ALL PRIVILEGES ON DATABASE gitea TO $POSTGRES_USER; GRANT ALL PRIVILEGES ON DATABASE coolify TO $POSTGRES_USER; + GRANT ALL PRIVILEGES ON DATABASE authelia TO $POSTGRES_USER; -- Log success SELECT 'Compose databases initialized:' AS status; SELECT datname FROM pg_database - WHERE datname IN ('directus', 'umami', 'n8n', 'linkwarden', 'joplin', 'mattermost', 'tandoor', 'asciinema', 'gitea', 'coolify') + WHERE datname IN ('directus', 'umami', 'n8n', 'linkwarden', 'joplin', 'mattermost', 'tandoor', 'asciinema', 'gitea', 'coolify', 'authelia') ORDER BY datname; EOSQL @@ -83,4 +88,5 @@ echo " • tandoor - Recipe manager database" echo " • asciinema - Terminal recording server database" echo " • gitea - Self-hosted Git service database" echo " • coolify - Deployment platform database" +echo " • authelia - SSO authentication database" echo "" diff --git a/net/authelia/configuration.yml b/net/authelia/configuration.yml new file mode 100644 index 0000000..13b5eeb --- /dev/null +++ b/net/authelia/configuration.yml @@ -0,0 +1,116 @@ +--- +############################################################### +# Authelia Configuration # +############################################################### + +theme: auto + +server: + host: 0.0.0.0 + port: 9091 + path: "" + asset_path: /config/assets/ + headers: + csp_template: "" + +log: + level: info + format: text + +totp: + issuer: pivoine.art + period: 30 + skew: 1 + +webauthn: + disable: false + display_name: Pivoine Auth + attestation_conveyance_preference: indirect + user_verification: preferred + timeout: 60s + +ntp: + address: "time.cloudflare.com:123" + version: 4 + max_desync: 3s + disable_startup_check: false + disable_failure: false + +authentication_backend: + password_reset: + disable: false + refresh_interval: 5m + file: + path: /etc/authelia/users_database.yml + password: + algorithm: argon2 + argon2: + variant: argon2id + iterations: 3 + memory: 65536 + parallelism: 4 + key_length: 32 + salt_length: 16 + +access_control: + default_policy: deny + rules: + # Authelia portal itself + - domain: auth.pivoine.art + policy: bypass + + # Services that should be publicly accessible + - domain: + - "pivoine.art" + - "www.pivoine.art" + policy: bypass + + # Protected services - require authentication + - domain: + - "netdata.pivoine.art" + - "mailpit.pivoine.art" + - "scrapy.pivoine.art" + - "restic.pivoine.art" + - "traefik.pivoine.art" + policy: two_factor + + # Development services + - domain: + - "dev.pivoine.art" + - "n8n.pivoine.art" + - "asciinema.pivoine.art" + - "coolify.pivoine.art" + policy: two_factor + +session: + name: authelia_session + domain: pivoine.art + same_site: lax + expiration: 1h + inactivity: 5m + remember_me_duration: 1M + +regulation: + max_retries: 3 + find_time: 2m + ban_time: 5m + +storage: + encryption_key: ${AUTHELIA_STORAGE_ENCRYPTION_KEY} + postgres: + host: postgres + port: 5432 + database: authelia + username: valknar + password: ${DB_PASSWORD} + schema: public + +notifier: + disable_startup_check: false + smtp: + host: net_mailpit + port: 1025 + sender: auth@pivoine.art + identifier: auth.pivoine.art + disable_require_tls: true + disable_html_emails: false diff --git a/net/authelia/users_database.yml b/net/authelia/users_database.yml new file mode 100644 index 0000000..e85deac --- /dev/null +++ b/net/authelia/users_database.yml @@ -0,0 +1,16 @@ +--- +############################################################### +# Users Database # +############################################################### + +# This file can be used if you do not have an LDAP set up. + +# List of users +users: + valknar: + displayname: "Valknar" + password: "$argon2id$v=19$m=65536,t=3,p=4$c2FsdHNhbHRzYWx0$4oCb4oCh4oCd4oCi4oCl4oCm" # CHANGE THIS - use: docker run --rm authelia/authelia:latest authelia crypto hash generate argon2 --password 'yourpassword' + email: valknar@pivoine.art + groups: + - admins + - dev diff --git a/net/compose.yaml b/net/compose.yaml index c234bb5..967bee3 100644 --- a/net/compose.yaml +++ b/net/compose.yaml @@ -265,6 +265,43 @@ services: # Watchtower - 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}' + # Authelia - SSO and authentication portal + authelia: + image: ${NET_AUTHELIA_IMAGE:-authelia/authelia:latest} + container_name: ${NET_COMPOSE_PROJECT_NAME}_authelia + restart: unless-stopped + environment: + TZ: ${TIMEZONE:-Europe/Berlin} + AUTHELIA_JWT_SECRET: ${AUTHELIA_JWT_SECRET} + AUTHELIA_SESSION_SECRET: ${AUTHELIA_SESSION_SECRET} + AUTHELIA_STORAGE_ENCRYPTION_KEY: ${AUTHELIA_STORAGE_ENCRYPTION_KEY} + volumes: + - authelia_config:/config + - ./authelia:/etc/authelia:ro + networks: + - compose_network + labels: + - 'traefik.enable=${NET_TRAEFIK_ENABLED}' + # HTTP to HTTPS redirect + - 'traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-authelia-redirect-web-secure.redirectscheme.scheme=https' + - 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-authelia-web.middlewares=${NET_COMPOSE_PROJECT_NAME}-authelia-redirect-web-secure' + - 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-authelia-web.rule=Host(`${NET_AUTHELIA_TRAEFIK_HOST}`)' + - 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-authelia-web.entrypoints=web' + # HTTPS router + - 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-authelia-web-secure.rule=Host(`${NET_AUTHELIA_TRAEFIK_HOST}`)' + - 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-authelia-web-secure.tls.certresolver=resolver' + - 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-authelia-web-secure.entrypoints=web-secure' + - 'traefik.http.routers.${NET_COMPOSE_PROJECT_NAME}-authelia-web-secure.middlewares=security-headers@file' + # Service + - 'traefik.http.services.${NET_COMPOSE_PROJECT_NAME}-authelia-web-secure.loadbalancer.server.port=9091' + - 'traefik.docker.network=${NETWORK_NAME}' + # ForwardAuth middleware for other services + - 'traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-authelia.forwardAuth.address=http://authelia:9091/api/verify?rd=https://${NET_AUTHELIA_TRAEFIK_HOST}' + - 'traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-authelia.forwardAuth.trustForwardHeader=true' + - 'traefik.http.middlewares.${NET_COMPOSE_PROJECT_NAME}-authelia.forwardAuth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Name,Remote-Email' + # Watchtower + - 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}' + volumes: letsencrypt_data: name: ${NET_COMPOSE_PROJECT_NAME}_letsencrypt_data @@ -276,6 +313,8 @@ volumes: name: ${NET_COMPOSE_PROJECT_NAME}_netdata_cache mailpit_data: name: ${NET_COMPOSE_PROJECT_NAME}_mailpit_data + authelia_config: + name: ${NET_COMPOSE_PROJECT_NAME}_authelia_config networks: compose_network: