Files
portainer-core/docs/AUTHENTIK_FORWARD_AUTH_DEPLOYMENT.md
T

11 KiB

Authentik Forward Authentication Deployment Summary

Overview

Successfully deployed Authentik forward authentication to all NPM-managed domains. All protected sites now require SSO authentication via Authentik before access is granted.

Deployment Date

2025-11-16

Architecture

Components

  1. Authentik Server (authentik-server)

    • Port: 9000
    • Container: authentik-server
    • Purpose: Main authentication server
  2. Authentik Proxy Outpost (authentik-proxy-outpost)

    • Port: 9001 (HTTP), 9300 (Metrics)
    • Container: authentik-proxy-outpost
    • Purpose: Forward authentication endpoint for NPM
    • Network: host mode (to match NPM)
    • Image: ghcr.io/goauthentik/proxy:latest
  3. Nginx Proxy Manager (nginx-proxy-manager)

    • Ports: 80, 81, 443
    • Purpose: Reverse proxy with SSL termination
    • Forward auth endpoint: http://192.168.86.149:9001/outpost.goauthentik.io/auth/nginx

Authentication Flow

1. User → https://home.schweitz.net
2. NPM → auth_request to http://192.168.86.149:9001/outpost.goauthentik.io/auth/nginx
3. Outpost → validates session with Authentik Server (port 9000)
4. If not authenticated:
   a. NPM → redirects to /outpost.goauthentik.io/start?rd=https://home.schweitz.net/
   b. Outpost → redirects to https://auth.schweitz.net/if/flow/...
   c. User → logs in via Google OAuth
   d. Authentik → sets session cookie
   e. Redirect → back to https://home.schweitz.net/
5. If authenticated:
   a. Request passes through with user headers

Protected Domains

All NPM proxy hosts now require authentication:

  1. 192.168.86.149 - Direct IP access
  2. amp.schweitz.net - AMP Server
  3. cloud.schweitz.net - Nextcloud
  4. code.schweitz.net - VS Code Server
  5. git.schweitz.net - Gitea
  6. home.schweitz.net - Home Assistant/Dashboard
  7. media.schweitz.net - Media Server
  8. tatlock.schweitz.net - Tatlock Services
  9. tower-of-joy - Tower of Joy Services

Excluded Domains

  • auth.schweitz.net - Authentik itself (cannot protect the auth provider)

Configuration Details

Authentik Proxy Provider

  • Name: npm-forward-auth-provider
  • ID: 2
  • Mode: forward_single
  • External Host: https://auth.schweitz.net
  • Token Validity: 480 minutes (8 hours)
  • Session Duration: 480 minutes (8 hours)
  • Rolling Expiration: Active (sessions extend on use)

Authentik Outpost

  • Name: npm-forward-auth-outpost
  • ID: 5fbba1f4-e50f-4d3f-94e8-8d9f30bf7a51
  • Type: proxy
  • Provider: npm-forward-auth-provider (ID: 2)
  • Token: aVJXnn3I44NhTYRYxGtbP3kJLEKQrTEMwu9IPVrhzN3NZV4vakgztcEf2L44
  • Container: authentik-proxy-outpost
  • Listen: 0.0.0.0:9001 (HTTP), 0.0.0.0:9300 (metrics)

NPM Configuration

Each protected proxy host has this advanced nginx configuration:

# Authentik Forward Authentication
# Send authentication requests to Authentik
auth_request /outpost.goauthentik.io/auth/nginx;

# Preserve authentication cookies
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;

# Get user information from Authentik
auth_request_set $authentik_username $upstream_http_x_authentik_username;
auth_request_set $authentik_groups $upstream_http_x_authentik_groups;
auth_request_set $authentik_email $upstream_http_x_authentik_email;
auth_request_set $authentik_name $upstream_http_x_authentik_name;
auth_request_set $authentik_uid $upstream_http_x_authentik_uid;

# Pass user info to backend
proxy_set_header X-authentik-username $authentik_username;
proxy_set_header X-authentik-groups $authentik_groups;
proxy_set_header X-authentik-email $authentik_email;
proxy_set_header X-authentik-name $authentik_name;
proxy_set_header X-authentik-uid $authentik_uid;

# On authentication failure, redirect to Authentik login
error_page 401 = @authentik_proxy_signin;

location @authentik_proxy_signin {
    internal;
    add_header Set-Cookie $auth_cookie;
    return 302 /outpost.goauthentik.io/start?rd=$scheme://$http_host$request_uri;
}

# Authentik authentication endpoint
location /outpost.goauthentik.io {
    proxy_pass http://192.168.86.149:9001/outpost.goauthentik.io;
    proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header Host $host;
}

Deployment Steps Taken

1. Created Proxy Provider

docker exec core-api /venv/bin/python /app/setup_authentik_forward_auth.py
  • Created npm-forward-auth-provider proxy provider (ID: 2)
  • Created NPM Forward Auth application (slug: npm-forward-auth)
  • Updated embedded outpost to include provider

2. Enabled Forward Auth on NPM Hosts

docker exec core-api /venv/bin/python /app/enable_npm_forward_auth.py
  • Added forward auth configuration to all 9 NPM proxy hosts
  • Initially pointed to http://192.168.86.149:9000 (incorrect)

3. Created Proxy Outpost

python3 -c "..." # Created outpost via Authentik API
  • Created npm-forward-auth-outpost (ID: 5fbba1f4-e50f-4d3f-94e8-8d9f30bf7a51)
  • Authentik auto-created container via Docker service connection
  • Retrieved token from auto-created container environment variables

4. Deployed Proxy Outpost Container

docker run -d \
  --name authentik-proxy-outpost \
  --restart unless-stopped \
  --network host \
  -e AUTHENTIK_HOST=http://192.168.86.149:9000 \
  -e AUTHENTIK_TOKEN=aVJXnn3I44NhTYRYxGtbP3kJLEKQrTEMwu9IPVrhzN3NZV4vakgztcEf2L44 \
  -e AUTHENTIK_LISTEN__HTTP=0.0.0.0:9001 \
  -e AUTHENTIK_LISTEN__METRICS=0.0.0.0:9300 \
  ghcr.io/goauthentik/proxy:latest

5. Updated NPM Hosts to Port 9001

docker exec core-api /venv/bin/python /app/fix_port_to_9001.py
  • Updated all 9 hosts to point to http://192.168.86.149:9001 instead of port 9000

Verification

Testing Forward Auth

# Test ping endpoint
curl http://192.168.86.149:9001/outpost.goauthentik.io/ping
# Expected: 204 No Content

# Test unauthenticated request
curl -I http://home.schweitz.net
# Expected: 302 redirect to /outpost.goauthentik.io/start?rd=...

Logs

# Outpost logs
docker logs authentik-proxy-outpost

# Authentik server logs
docker logs authentik-server

# NPM logs
docker logs nginx-proxy-manager

Token & Session Management

Token Validity

  • Initial validity: 480 minutes (8 hours)
  • Rolling expiration: Token stays active on use
  • Idle timeout: 8 hours after last activity
  • Explicit logout: Token invalidated immediately at https://auth.schweitz.net/if/user/#/settings

Session Behavior

  • Single login protects all domains under *.schweitz.net
  • Cookie domain: Shared across all sites (set by individual domain)
  • Persistent: Survives browser restarts until expiration/logout
  • Secure: HTTPS-only, HttpOnly flag set

User Management

Adding Users

  1. Go to https://auth.schweitz.net/if/admin/
  2. Navigate to Directory → Users
  3. Click Create → Create and enroll user
  4. Enter user details
  5. Send enrollment invite

Current Access Control

All authenticated users can access all protected sites. To restrict:

  1. Go to https://auth.schweitz.net/if/admin/#/core/applications
  2. Select NPM Forward Auth application
  3. Go to Policy / Group / User Bindings
  4. Add specific users or groups

Troubleshooting

Check Outpost Status

docker ps | grep authentik-proxy-outpost
docker logs authentik-proxy-outpost --tail 50

Check Authentik Server

docker logs authentik-server --tail 50 | grep -i outpost

Test Endpoints

# Ping (should return 204)
curl -I http://192.168.86.149:9001/outpost.goauthentik.io/ping

# Auth endpoint (needs proper headers from nginx)
curl -I http://home.schweitz.net

Common Issues

  1. 502 Bad Gateway: Outpost not running or not reachable

    • Check: docker ps | grep authentik-proxy-outpost
    • Fix: Restart outpost container
  2. Redirect Loop: Misconfigured nginx or outpost

    • Check: NPM advanced config points to port 9001
    • Check: Outpost logs for errors
  3. Session Expires Too Quickly: Token validity too short

    • Check: Provider token_validity setting (should be 480 minutes)

Automation Scripts

Enable Forward Auth on New Sites

from src.clients.npm_client import get_npm_client

npm = get_npm_client()
proxy = await npm.create_proxy_host(
    domain_names=["newsite.schweitz.net"],
    forward_host="backend-container",
    forward_port=8080,
    ssl_enabled=True
)
await npm.enable_authentik_forward_auth(proxy["id"])

Bulk Operations

# Enable on all hosts
docker exec core-api /venv/bin/python /app/enable_npm_forward_auth.py

# Update to port 9001
docker exec core-api /venv/bin/python /app/fix_port_to_9001.py

Security Considerations

  1. ✓ HTTPS Required: All sites use SSL termination at NPM
  2. ✓ Secure Cookies: HttpOnly and Secure flags prevent XSS/MITM
  3. ✓ Token Rotation: Tokens extend on activity for security
  4. ✓ Audit Logging: Authentik logs all authentication events
  5. ⚠ MFA: Not yet enabled (future enhancement)

Monitoring

Health Checks

# Authentik server
curl http://192.168.86.149:9000/-/health/live/

# Proxy outpost
curl http://192.168.86.149:9001/outpost.goauthentik.io/ping

# Metrics
curl http://192.168.86.149:9300/metrics

Authentik Admin Dashboard

Future Enhancements

  1. Multi-Factor Authentication: Enable TOTP/SMS for high-security sites
  2. Per-Site Access Control: Different user groups for different domains
  3. Rate Limiting: Prevent brute force attacks
  4. IP Whitelisting: Allow certain IPs without auth
  5. API Key Support: Service-to-service authentication
  6. Integration with Core API: Expose auth status via REST API

Notes

  • The embedded Authentik outpost (ID: fbbefe20-b2e1-4706-8533-7bfece51989c) is for Authentik's own web UI, not for external forward auth
  • Forward auth requires a separate proxy outpost container listening on its own port (9001)
  • The outpost automatically created by Authentik via Docker service connection had the wrong port configuration
  • We manually deployed the outpost with correct settings using docker run
  • NPM uses host network mode, so the outpost must also use host mode or be accessible via host IP:port
  • Token keys are only visible once during creation in the Authentik UI; we retrieved it from the auto-created container's environment variables

Completion Status

✅ COMPLETE - All protected sites now require Authentik authentication

Deployment successful as of 2025-11-16 15:41 UTC