diff --git a/app_gateway/caddy_config_export.py b/app_gateway/caddy_config_export.py index 3451df3..ff1dd17 100644 --- a/app_gateway/caddy_config_export.py +++ b/app_gateway/caddy_config_export.py @@ -34,10 +34,14 @@ def build_applications_data(): def _build_auth_method_entry(method): entry = {'type': method.auth_type} - if method.auth_type == 'totp': + if method.auth_type == 'local_password': + entry['session_expiration_minutes'] = method.session_expiration_minutes + + elif method.auth_type == 'totp': entry['totp_secret'] = method.totp_secret elif method.auth_type == 'oidc': + entry['session_expiration_minutes'] = method.session_expiration_minutes entry['provider'] = method.oidc_provider entry['client_id'] = method.oidc_client_id entry['client_secret'] = method.oidc_client_secret @@ -77,7 +81,7 @@ def build_auth_policies_data(): for user in GatekeeperUser.objects.all(): users[user.username] = { 'email': user.email, - 'password_hash': user.password_hash or '', + 'password_hash': user.password or '', 'totp_secret': user.totp_secret, } diff --git a/containers/authelia/Dockerfile-authelia b/containers/authelia/Dockerfile-authelia deleted file mode 100644 index a276c04..0000000 --- a/containers/authelia/Dockerfile-authelia +++ /dev/null @@ -1,6 +0,0 @@ -FROM authelia/authelia:latest - -COPY entrypoint.sh /usr/local/bin/authelia-entrypoint.sh -RUN chmod +x /usr/local/bin/authelia-entrypoint.sh - -ENTRYPOINT ["/usr/local/bin/authelia-entrypoint.sh"] diff --git a/containers/authelia/entrypoint.sh b/containers/authelia/entrypoint.sh deleted file mode 100644 index 5b590d9..0000000 --- a/containers/authelia/entrypoint.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/sh - -set -eu - -CONFIG_PATH="/config/configuration.yml" -WAIT_INTERVAL=5 - -echo "==> Waiting for Authelia configuration file..." -while [ ! -f "$CONFIG_PATH" ]; do - sleep "$WAIT_INTERVAL" -done -echo "==> Configuration file found: ${CONFIG_PATH}" - -echo "==> Starting Authelia..." -authelia --config "$CONFIG_PATH" & -AUTHELIA_PID=$! - -sleep 3 - -echo "==> Watching ${CONFIG_PATH} for changes..." - -# Function to safely get hash in minimal environments -get_hash() { - md5sum "$CONFIG_PATH" 2>/dev/null | awk '{print $1}' || echo "error" -} - -LAST_HASH=$(get_hash) - -while true; do - sleep 3 - CURRENT_HASH=$(get_hash) - - if [ "$LAST_HASH" != "$CURRENT_HASH" ]; then - echo "==> Configuration change detected, restarting Authelia..." - LAST_HASH="$CURRENT_HASH" - - kill "$AUTHELIA_PID" 2>/dev/null || true - wait "$AUTHELIA_PID" 2>/dev/null || true - - authelia --config "$CONFIG_PATH" & - AUTHELIA_PID=$! - echo "==> Authelia restarted with PID ${AUTHELIA_PID}" - fi -done diff --git a/containers/caddy/process_config.py b/containers/caddy/process_config.py index 52d59af..4c019fa 100644 --- a/containers/caddy/process_config.py +++ b/containers/caddy/process_config.py @@ -1,9 +1,6 @@ #!/usr/bin/env python3 """ -Reads JSON config files exported by Django and generates: - - /etc/caddy/Caddyfile - - /authelia_config/configuration.yml - - /authelia_config/users_database.yml +Reads JSON config files exported by Django and generates /etc/caddy/Caddyfile. Expected input files in /caddy_json_export/: - wireguard_webadmin.json (required, generated on container startup) @@ -14,20 +11,13 @@ Expected input files in /caddy_json_export/: import json import os -import secrets -import string - -import yaml +from urllib.parse import urlparse JSON_DIR = os.environ.get("JSON_DIR", "/caddy_json_export") CADDYFILE_PATH = os.environ.get("CADDYFILE_PATH", "/etc/caddy/Caddyfile") -AUTHELIA_CONFIG_DIR = os.environ.get("AUTHELIA_CONFIG_DIR", "/authelia_config") -AUTHELIA_CONFIG_PATH = os.path.join(AUTHELIA_CONFIG_DIR, "configuration.yml") -AUTHELIA_USERS_PATH = os.path.join(AUTHELIA_CONFIG_DIR, "users_database.yml") -AUTHELIA_SECRETS_DIR = os.path.join(AUTHELIA_CONFIG_DIR, "secrets") - -AUTHELIA_INTERNAL_URL = "http://wireguard-webadmin-authelia:9091" -AUTHELIA_PORTAL_PATH = "/authelia" +AUTH_GATEWAY_INTERNAL_URL = "http://wireguard-webadmin-auth-gateway:9091" +AUTH_GATEWAY_PORTAL_PATH = "/auth-gateway" +AUTH_GATEWAY_CHECK_URI = "/auth/check" def load_json(filename): @@ -38,25 +28,7 @@ def load_json(filename): return json.load(json_file) -def generate_secret(length=64): - alphabet = string.ascii_letters + string.digits - return "".join(secrets.choice(alphabet) for char_index in range(length)) - - -def get_or_create_secret(name): - os.makedirs(AUTHELIA_SECRETS_DIR, exist_ok=True) - secret_path = os.path.join(AUTHELIA_SECRETS_DIR, name) - if os.path.exists(secret_path): - with open(secret_path, "r", encoding="utf-8") as secret_file: - return secret_file.read().strip() - secret_value = generate_secret() - with open(secret_path, "w", encoding="utf-8") as secret_file: - secret_file.write(secret_value) - return secret_value - - def collect_all_applications(): - """Merge entries from wireguard_webadmin.json and applications.json.""" apps = [] webadmin_data = load_json("wireguard_webadmin.json") @@ -70,440 +42,117 @@ def collect_all_applications(): return apps +def split_upstream(upstream): + """Return (base_url, upstream_path) where base_url has no path component. + Upstreams without an explicit http(s):// scheme are returned as-is with no path.""" + if not upstream.startswith("http://") and not upstream.startswith("https://"): + return upstream, "" + parsed = urlparse(upstream) + base = f"{parsed.scheme}://{parsed.netloc}" + path = parsed.path.rstrip("/") + return base, path + + def build_caddyfile(apps, auth_policies, routes): + policies = auth_policies.get("policies", {}) if auth_policies else {} + route_entries = routes.get("entries", {}) if routes else {} lines = [] - has_authelia = auth_policies is not None - policies = auth_policies.get("policies", {}) if has_authelia else {} def get_policy_type(policy_name): if policy_name and policy_name in policies: return policies[policy_name].get("policy_type", "bypass") - return "bypass" + return "deny" - def emit_authelia_portal(): - lines.append(f" handle_path {AUTHELIA_PORTAL_PATH}/* {{") - lines.append(f" reverse_proxy {AUTHELIA_INTERNAL_URL}") - lines.append(f" }}") + def emit_auth_portal(): + lines.append(f" handle_path {AUTH_GATEWAY_PORTAL_PATH}/* {{") + lines.append(f" reverse_proxy {AUTH_GATEWAY_INTERNAL_URL}") + lines.append(" }") + lines.append("") + + def handle_open(matcher): + if matcher == "*": + return " handle {" + return f" handle {matcher} {{" + + def emit_reverse_proxy(base, upstream_path, indent=" "): + if upstream_path: + lines.append(f"{indent}rewrite * {upstream_path}{{uri}}") + lines.append(f"{indent}reverse_proxy {base}") + + def emit_protected_handle(path_matcher, base, upstream_path): + lines.append(handle_open(path_matcher)) + lines.append(f" forward_auth {AUTH_GATEWAY_INTERNAL_URL} {{") + lines.append(f" uri {AUTH_GATEWAY_CHECK_URI}") + lines.append(" copy_headers X-Auth-User X-Auth-Email X-Auth-Groups X-Auth-Factors X-Auth-Policy") + lines.append(" }") + emit_reverse_proxy(base, upstream_path) + lines.append(" }") lines.append("") for app in apps: - app_id = app.get("id", "unknown") hosts = app.get("hosts", []) upstream = app.get("upstream", "") static_routes = app.get("static_routes", []) + app_id = app.get("id", "") if not hosts or not upstream: continue - host_list = ", ".join(hosts) - lines.append(f"{host_list} {{") + base, upstream_path = split_upstream(upstream) - if has_authelia and app_id == "wireguard_webadmin": - emit_authelia_portal() + lines.append(f"{', '.join(hosts)} {{") + emit_auth_portal() for static_route in static_routes: path_prefix = static_route.get("path_prefix", "") root_dir = static_route.get("root", "") - strip_prefix = static_route.get("strip_prefix", "") cache_control = static_route.get("cache_control", "") - lines.append(f" handle_path {path_prefix}/* {{") lines.append(f" root * {root_dir}") - lines.append(f" file_server") + lines.append(" file_server") if cache_control: lines.append(f" header Cache-Control \"{cache_control}\"") - lines.append(f" }}") + lines.append(" }") lines.append("") - app_routes = {} - app_default_policy = None - if routes: - route_entries = routes.get("entries", {}) - if app_id in route_entries: - app_route_data = route_entries[app_id] - app_default_policy = app_route_data.get("default_policy") - for route in app_route_data.get("routes", []): - app_routes[route.get("path_prefix", "")] = route.get("policy", "") - - default_policy_type = get_policy_type(app_default_policy) - - # When the default policy is deny, use handle blocks for specific - # non-deny routes and a catch-all respond 403 at Caddy level, - # avoiding an unnecessary Authelia round-trip. - if has_authelia and default_policy_type == "deny": - has_protected_routes = any( - get_policy_type(pn) not in ("bypass", "deny") - for pn in app_routes.values() - ) - if has_protected_routes: - emit_authelia_portal() - - for path_prefix, policy_name in app_routes.items(): - ptype = get_policy_type(policy_name) - if ptype == "bypass": - lines.append(f" handle {path_prefix}/* {{") - lines.append(f" reverse_proxy {upstream}") - lines.append(f" }}") - lines.append("") - elif ptype == "deny": - lines.append(f" handle {path_prefix}/* {{") - lines.append(f" respond 403") - lines.append(f" }}") - lines.append("") - else: - lines.append(f" handle {path_prefix}/* {{") - lines.append(f" forward_auth {AUTHELIA_INTERNAL_URL} {{") - lines.append(f" uri {AUTHELIA_PORTAL_PATH}/api/authz/forward-auth") - lines.append(f" copy_headers Remote-User Remote-Groups Remote-Name Remote-Email") - lines.append(f" }}") - lines.append(f" reverse_proxy {upstream}") - lines.append(f" }}") - lines.append("") - lines.append(f" respond 403") - lines.append(f"}}") + app_route_data = route_entries.get(app_id) + if app_route_data is None: + emit_reverse_proxy(base, upstream_path, indent=" ") + lines.append("}") lines.append("") continue - # For bypass/protected default policy: emit explicit deny blocks for - # any per-route deny entries before the forward_auth check. - for path_prefix, policy_name in app_routes.items(): - if get_policy_type(policy_name) == "deny": - lines.append(f" handle {path_prefix}/* {{") - lines.append(f" respond 403") - lines.append(f" }}") + route_list = sorted(app_route_data.get("routes", []), key=lambda route: len(route.get("path_prefix", "")), reverse=True) + for route in route_list: + path_prefix = route.get("path_prefix", "/") + policy_type = get_policy_type(route.get("policy")) + matcher = f"{path_prefix}*" + if policy_type == "bypass": + lines.append(handle_open(matcher)) + emit_reverse_proxy(base, upstream_path) + lines.append(" }") lines.append("") + elif policy_type == "deny": + lines.append(handle_open(matcher)) + lines.append(" respond 403") + lines.append(" }") + lines.append("") + else: + emit_protected_handle(matcher, base, upstream_path) - needs_auth = False - if has_authelia and auth_policies: - if default_policy_type not in ("bypass", "deny"): - needs_auth = True - for path_prefix, policy_name in app_routes.items(): - if get_policy_type(policy_name) not in ("bypass", "deny"): - needs_auth = True - - if needs_auth: - # Expose the Authelia portal on this domain so session cookies can - # be set with the correct scope (authelia_url = this domain). - if app_id != "wireguard_webadmin": - emit_authelia_portal() - - for path_prefix, policy_name in app_routes.items(): - if get_policy_type(policy_name) == "bypass": - lines.append(f" @bypass_{_sanitize_id(path_prefix)} path {path_prefix}*") - lines.append(f" skip_log @bypass_{_sanitize_id(path_prefix)}") - lines.append("") - - lines.append(f" forward_auth {AUTHELIA_INTERNAL_URL} {{") - lines.append(f" uri {AUTHELIA_PORTAL_PATH}/api/authz/forward-auth") - lines.append(f" copy_headers Remote-User Remote-Groups Remote-Name Remote-Email") - lines.append(f" }}") - lines.append("") - - lines.append(f" reverse_proxy {upstream}") - lines.append(f"}}") + default_policy_type = get_policy_type(app_route_data.get("default_policy")) + if default_policy_type == "bypass": + emit_reverse_proxy(base, upstream_path, indent=" ") + elif default_policy_type == "deny": + lines.append(" respond 403") + else: + emit_protected_handle("*", base, upstream_path) + lines.append("}") lines.append("") return "\n".join(lines) -def _sanitize_id(value): - return value.strip("/").replace("/", "_").replace("-", "_") - - -def _collect_session_cookies(apps, routes, policies, server_address): - """Build session.cookies entries for Authelia. - - Authelia v4.37+ requires a session.cookies entry for every domain managed - via forward_auth, and authelia_url must share the cookie scope with its - domain. For multi-domain gateways this means each domain must expose the - Authelia portal under its own hostname — Caddy handles this by routing - /authelia/* to the Authelia backend for each protected app. - - Only apps with at least one protected (non-bypass, non-deny) route are - included. Deny is handled at the Caddy level; bypass needs no session. - """ - def get_policy_type(policy_name): - if policy_name and policy_name in policies: - return policies[policy_name].get("policy_type", "bypass") - return "bypass" - - route_entries = routes.get("entries", {}) if routes else {} - seen = {server_address} - cookies = [ - { - "domain": server_address, - "authelia_url": f"https://{server_address}{AUTHELIA_PORTAL_PATH}", - "default_redirection_url": f"https://{server_address}", - } - ] - - for app in apps: - app_id = app.get("id", "") - hosts = app.get("hosts", []) - - if app_id == "wireguard_webadmin": - # Extra hostnames for the main app already have /authelia/* routed - # in Caddy — add their session cookies pointing to themselves. - for host in hosts: - if host not in seen: - seen.add(host) - cookies.append({ - "domain": host, - "authelia_url": f"https://{host}{AUTHELIA_PORTAL_PATH}", - "default_redirection_url": f"https://{host}", - }) - continue - - app_route_data = route_entries.get(app_id, {}) - default_ptype = get_policy_type(app_route_data.get("default_policy")) - needs_auth = default_ptype not in ("bypass", "deny") - if not needs_auth: - for route in app_route_data.get("routes", []): - if get_policy_type(route.get("policy", "")) not in ("bypass", "deny"): - needs_auth = True - break - - if not needs_auth: - continue - - for host in hosts: - if host not in seen: - seen.add(host) - cookies.append({ - "domain": host, - "authelia_url": f"https://{host}{AUTHELIA_PORTAL_PATH}", - "default_redirection_url": f"https://{host}", - }) - - return cookies - - -def build_authelia_config(auth_policies, routes, apps): - server_address = os.environ.get("SERVER_ADDRESS", "localhost") - - jwt_secret = get_or_create_secret("jwt_secret") - session_secret = get_or_create_secret("session_secret") - storage_encryption_key = get_or_create_secret("storage_encryption_key") - - session_cookies = _collect_session_cookies( - apps, routes, auth_policies.get("policies", {}), server_address - ) - - config = { - "server": { - "address": "tcp://0.0.0.0:9091", - }, - "log": { - "level": "info", - }, - "identity_validation": { - "reset_password": { - "jwt_secret": jwt_secret, - }, - }, - "authentication_backend": { - "file": { - "path": "/config/users_database.yml", - }, - }, - "session": { - "secret": session_secret, - "cookies": session_cookies, - }, - "storage": { - "encryption_key": storage_encryption_key, - "local": { - "path": "/data/db.sqlite3", - }, - }, - "notifier": { - "filesystem": { - "filename": "/data/notification.txt", - }, - }, - "access_control": build_access_control_rules(auth_policies, routes, apps), - } - - identity_providers = build_identity_providers(auth_policies, server_address) - if identity_providers: - config["identity_providers"] = identity_providers - - return config - - -def build_access_control_rules(auth_policies, routes, apps): - if not auth_policies or not routes: - return {"default_policy": "bypass", "rules": []} - - policies = auth_policies.get("policies", {}) - route_entries = routes.get("entries", {}) - - host_map = {} - for app in apps: - app_id = app.get("id", "unknown") - host_map[app_id] = app.get("hosts", []) - - rules = [] - - for app_id, route_data in route_entries.items(): - app_hosts = host_map.get(app_id, []) - if not app_hosts: - continue - - domain_list = list(app_hosts) - - for route in route_data.get("routes", []): - policy_name = route.get("policy", "") - path_prefix = route.get("path_prefix", "") - - if policy_name not in policies: - continue - - policy_data = policies[policy_name] - policy_type = policy_data.get("policy_type", "bypass") - - if policy_type == "bypass": - authelia_policy = "bypass" - elif policy_type == "deny": - authelia_policy = "deny" - else: - authelia_policy = "two_factor" - - rule = { - "domain": domain_list, - "policy": authelia_policy, - "resources": [f"^{path_prefix}.*$"], - } - - groups = policy_data.get("groups", []) - if groups and authelia_policy not in ("bypass", "deny"): - rule["subject"] = [f"group:{g}" for g in groups] - - rules.append(rule) - - default_policy_name = route_data.get("default_policy") - if default_policy_name and default_policy_name in policies: - default_policy_data = policies[default_policy_name] - default_type = default_policy_data.get("policy_type", "bypass") - - if default_type == "bypass": - authelia_default = "bypass" - elif default_type == "deny": - authelia_default = "deny" - else: - authelia_default = "two_factor" - - default_rule = { - "domain": domain_list, - "policy": authelia_default, - } - - groups = default_policy_data.get("groups", []) - if groups and authelia_default not in ("bypass", "deny"): - default_rule["subject"] = [f"group:{g}" for g in groups] - - rules.append(default_rule) - - return { - "default_policy": "two_factor" if not rules else "deny", - "rules": rules, - } - - -def build_identity_providers(auth_policies, server_address): - if not auth_policies: - return None - - auth_methods = auth_policies.get("auth_methods", {}) - oidc_clients = [] - - for method_name, method in auth_methods.items(): - if method.get("type") != "oidc": - continue - - client_id = method.get("client_id", "") - client_secret = method.get("client_secret", "") - if not client_id: - continue - - client = { - "client_id": client_id, - "client_secret": client_secret, - "authorization_policy": "two_factor", - "redirect_uris": [ - f"https://{server_address}{AUTHELIA_PORTAL_PATH}/oidc/callback", - ], - "scopes": ["openid", "profile", "email", "groups"], - } - oidc_clients.append(client) - - if not oidc_clients: - return None - - hmac_secret = get_or_create_secret("oidc_hmac_secret") - - return { - "oidc": { - "hmac_secret": hmac_secret, - "clients": oidc_clients, - }, - } - - -DUMMY_USER = { - "_dummy_setup_user": { - "disabled": True, - "displayname": "Dummy Setup User", - "password": "$argon2id$v=19$m=65536,t=3,p=4$Nklqa1J5a3ZweDhlZnNlUw$5D8WJ+sT20eXj1U10qNnS2Ew/M40B8v1/37X2b1lG0I", - "email": "dummy@localhost", - } -} - -def build_users_database(auth_policies): - if not auth_policies: - return {"users": DUMMY_USER} - - users_data = auth_policies.get("users", {}) - groups_data = auth_policies.get("groups", {}) - - user_groups = {} - for group_name, group_info in groups_data.items(): - for username in group_info.get("users", []): - if username not in user_groups: - user_groups[username] = [] - user_groups[username].append(group_name) - - users = {} - for username, user_info in users_data.items(): - user_entry = { - "displayname": username, - "email": user_info.get("email", f"{username}@localhost"), - "groups": user_groups.get(username, []), - } - password_hash = user_info.get("password_hash", "") - if password_hash: - user_entry["password"] = password_hash - - users[username] = user_entry - - if not users: - users = DUMMY_USER.copy() - - return {"users": users} - - -class _NoAliasDumper(yaml.SafeDumper): - """YAML dumper that never emits anchors/aliases.""" - def ignore_aliases(self, data): - return True - - -def write_yaml(filepath, data): - os.makedirs(os.path.dirname(filepath), exist_ok=True) - with open(filepath, "w", encoding="utf-8") as yaml_file: - yaml.dump(data, yaml_file, Dumper=_NoAliasDumper, default_flow_style=False, allow_unicode=True, sort_keys=False) - - def main(): apps = collect_all_applications() auth_policies = load_json("auth_policies.json") @@ -515,17 +164,6 @@ def main(): caddyfile.write(caddyfile_content) print(f"Caddyfile written to {CADDYFILE_PATH}") - if auth_policies: - authelia_config = build_authelia_config(auth_policies, routes, apps) - write_yaml(AUTHELIA_CONFIG_PATH, authelia_config) - print(f"Authelia configuration written to {AUTHELIA_CONFIG_PATH}") - - users_db = build_users_database(auth_policies) - write_yaml(AUTHELIA_USERS_PATH, users_db) - print(f"Authelia users database written to {AUTHELIA_USERS_PATH}") - else: - print("No auth_policies.json found, skipping Authelia config generation.") - if __name__ == "__main__": main() diff --git a/docker-compose-caddy.yml b/docker-compose-caddy.yml index 4e88a51..2bfeda8 100644 --- a/docker-compose-caddy.yml +++ b/docker-compose-caddy.yml @@ -84,7 +84,6 @@ services: volumes: - static_volume:/static - caddy_json_export:/caddy_json_export - - authelia_config:/authelia_config - caddy_data:/data - caddy_config:/config ports: @@ -92,20 +91,23 @@ services: - "443:443" depends_on: - wireguard-webadmin + - wireguard-webadmin-auth-gateway - wireguard-webadmin-authelia: - container_name: wireguard-webadmin-authelia + wireguard-webadmin-auth-gateway: + container_name: wireguard-webadmin-auth-gateway restart: unless-stopped build: - context: ./containers/authelia - dockerfile: Dockerfile-authelia + context: ./containers/auth-gateway + dockerfile: Dockerfile-auth-gateway environment: - TZ=${TIMEZONE} + - AUTH_GATEWAY_CONFIG_DIR=/caddy_json_export + - AUTH_GATEWAY_DATABASE_PATH=/data/auth-gateway.sqlite3 volumes: - - authelia_config:/config - - authelia_data:/data + - caddy_json_export:/caddy_json_export + - auth_gateway_data:/data depends_on: - - wireguard-webadmin-caddy + - wireguard-webadmin volumes: static_volume: @@ -114,7 +116,6 @@ volumes: app_secrets: rrd_data: caddy_json_export: - authelia_config: - authelia_data: + auth_gateway_data: caddy_data: caddy_config: diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 08dc516..2d66795 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -94,7 +94,6 @@ services: volumes: - static_volume:/static - caddy_json_export:/caddy_json_export - - authelia_config:/authelia_config - caddy_data:/data - caddy_config:/config ports: @@ -102,20 +101,24 @@ services: - "443:443" depends_on: - wireguard-webadmin + - wireguard-webadmin-auth-gateway - wireguard-webadmin-authelia: - container_name: wireguard-webadmin-authelia + wireguard-webadmin-auth-gateway: + container_name: wireguard-webadmin-auth-gateway restart: unless-stopped build: - context: ./containers/authelia - dockerfile: Dockerfile-authelia + context: ./containers/auth-gateway + dockerfile: Dockerfile-auth-gateway environment: - TZ=${TIMEZONE} + - AUTH_GATEWAY_CONFIG_DIR=/caddy_json_export + - AUTH_GATEWAY_DATABASE_PATH=/data/auth-gateway.sqlite3 + - AUTH_GATEWAY_SECURE_COOKIES=false volumes: - - authelia_config:/config - - authelia_data:/data + - caddy_json_export:/caddy_json_export + - auth_gateway_data:/data depends_on: - - wireguard-webadmin-caddy + - wireguard-webadmin volumes: static_volume: @@ -124,7 +127,6 @@ volumes: app_secrets: rrd_data: caddy_json_export: - authelia_config: - authelia_data: + auth_gateway_data: caddy_data: caddy_config: