SSO Details
The SSO is handled by Authelia, and a lot of my configs are based on this page. It was pretty difficult to get this going, but now that it is I will give an overview of how it works. It is integrated with the proxy server so that when SSO protected domains are requested my proxy server. When a protected domain is loaded up, the proxy calls on Authelia to validate, and Authelia takes over. Authelia sets a cookie when you log in, so if you've already logged in somewhere, it will just pass you through to the website. I also was able to cobble together OIDC integration with Authelia, and that is integrated with my audiobook site as well as this book stack site.
Here's a diagram of how the proxy, SSO, and containers interact. The site items with ovals are the docker containers with open ports that the proxy server would normally forward unfettered. On sites that need SSO, I add a big pile of code to the custom part of the NPM site config that tells it to finish up with Authelia. Then, Authelia does it's stuff based on the config file. The nope in the oval is the forbidden page that you see when you are logged into SSO and try to access a site that Authelia says nope to.
For the terribly curious, here is a stripped out docker-compose.yaml and configuration.yml for authelia and the nginx configs.
docker-compose.yaml
services:
authelia:
container_name: authelia
image: 'authelia/authelia'
restart: always
network_mode: bridge
ports:
- 9110:9091
volumes:
- ./authelia-config:/config
- ./authelia-secrets:/secrets
authelia-redis:
container_name: authelia-redis
image: bitnami/redis:latest
volumes:
- ./authelia-redis:/bitnami/
environment:
REDIS_PASSWORD: "lolwtfbbqlolwtfbbq"
restart: always
network_mode: bridge
ports:
- 9112:6379
authelia-db:
image: postgres
container_name: authelia-db
restart: always
network_mode: bridge
volumes:
- ./authelia-db:/var/lib/postgresql/data
environment:
POSTGRES_DB: authelia
POSTGRES_USER: authelia
POSTGRES_PASSWORD: lolwtfbbqlolwtfbbq
ports:
- 9111:5432
configuration.yml
---
###############################################################################
# Authelia Configuration #
###############################################################################
theme: dark
jwt_secret: "lolwtfbbqlolwtfbbq"
default_redirection_url: https://auth.domain.com/
server:
host: 0.0.0.0
port: 9091
disable_healthcheck: false
log:
level: info
totp:
disable: false
issuer: 'auth.domain.com'
algorithm: 'sha1'
digits: 6
period: 30
skew: 1
secret_size: 32
allowed_algorithms:
- 'SHA1'
allowed_digits:
- 6
allowed_periods:
- 30
disable_reuse_security_policy: false
authentication_backend:
ldap:
address: 'ldap://pdc.domain.local:389'
implementation: 'activedirectory'
base_dn: 'OU=users,DC=domain,DC=local'
users_filter: (&(|({username_attribute}={input})({mail_attribute}={input}))(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2)(!pwdLastSet=0))
groups_filter: (&(member:1.2.840.113556.1.4.1941:={dn})(objectClass=group)(objectCategory=group))
group_name_attribute: cn
mail_attribute: mail
display_name_attribute: displayname
user: 'CN=LDAP Service,OU=Service,OU=users,DC=domain,DC=local'
password: 'lolwtfbbq'
disable_reset_password: true
access_control:
## just some simple example rules
default_policy: deny
rules:
## bypass rule
- domain:
- "auth.domain.com"
policy: bypass
- domain:
- "*.domain.com"
resources:
- "^/api([/?].*)?$"
policy: bypass
## 2fa domain
- domain:
- "2fa.domain.com"
policy: two_factor
subject:
- "group:2FA-Users"
# Normal protection
- domain:
- "secure.domain.com"
policy: one_factor
subject: "group:secure-users"
session:
name: authelia_session
domain: domain.com
same_site: lax
secret: "lolwtfbbqlolwtfbbq"
expiration: 30d
inactivity: 1d
remember_me_duration: 6M
redis:
host: 0.0.0.0
port: 9112
password: "lolwtfbbqlolwtfbbq"
database_index: 0
maximum_active_connections: 10
minimum_idle_connections: 0
regulation:
max_retries: 3
find_time: 10m
ban_time: 12h
storage:
encryption_key: 'lolwtfbbqlolwtfbbq'
postgres:
address: 'tcp://0.0.0.0:9111'
database: 'authelia'
schema: 'public'
username: 'authelia'
password: 'lolwtfbbqlolwtfbbq'
notifier:
disable_startup_check: false
smtp:
username: authelia@domain.com
password: "lolwtfbbqlolwtfbbq"
host: mail.domain.com
port: 587
sender: authelia@domain.com
identifier: authelia.home.cosmos
subject: "[Authelia] {title}"
startup_check_address: user@domain.com
disable_require_tls: false
disable_html_emails: true
tls:
skip_verify: false
minimum_version: TLS1.2
identity_providers:
oidc:
hmac_secret: lolwtfbbqlolwtfbbq # provide secure secret
issuer_certificate_chain: |
-----BEGIN CERTIFICATE-----
lolwtfbbqlolwtfbbq
lolwtfbbqlolwtfbbq
lolwtfbbqlolwtfbbq
lolwtfbbqlolwtfbbq
lolwtfbbqlolwtfbbq
lolwtfbbqlolwtfbbq
lolwtfbbqlolwtfbbq
lolwtfbbqlolwtfbbq
lolwtfbbqlolwtfbbq
lolwtfbbqlolwtfbbq
-----END CERTIFICATE-----
issuer_private_key: |
-----BEGIN PRIVATE KEY-----
lolwtfbbqlolwtfbbq
lolwtfbbqlolwtfbbq
lolwtfbbqlolwtfbbq
lolwtfbbqlolwtfbbq
lolwtfbbqlolwtfbbq
lolwtfbbqlolwtfbbq
lolwtfbbqlolwtfbbq
lolwtfbbqlolwtfbbq
lolwtfbbqlolwtfbbq
lolwtfbbqlolwtfbbq
-----END PRIVATE KEY-----
access_token_lifespan: 1h
authorize_code_lifespan: 1m
id_token_lifespan: 1h
refresh_token_lifespan: 90m
enable_client_debug_messages: false
enforce_pkce: public_clients_only
cors:
endpoints:
- authorization
- token
- revocation
- introspection
allowed_origins:
- https://*.domain.com # adjust to your url
allowed_origins_from_client_redirect_uris: false
clients:
- id: domain-oidc
description: domain
secret: 'lolwtfbbqlolwtfbbq' # provide secure secret
sector_identifier: 'auth.domain.com'
public: false
authorization_policy: one_factor # may use two_factor to enforce 2FA
consent_mode: implicit
pre_configured_consent_duration: 6m
audience: []
scopes:
- openid
- groups
- email
- profile
redirect_uris: # adjust to your domains
- https://auth.domain.com/
- https://auth.domain.com/oauth2/callback
- https://audiobooks.domain.com/oauth2/callback
- https://audiobooks.domain.com/auth/login
- https://audiobooks.domain.com/user-settings
- https://audiobooks.domain.com
- https://audiobooks.domain.com/auth/openid/callback
grant_types:
- refresh_token
- authorization_code
- implicit
response_types:
- code
- id_token
response_modes:
- form_post
- query
- fragment
userinfo_signing_algorithm: none
nginx-server
location / {
set $upstream_authelia http://0.0.0.0:9110; # This example assumes a Docker deployment
proxy_pass $upstream_authelia;
client_body_buffer_size 128k;
#Timeout if the real server is dead
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
# Advanced Proxy Config
send_timeout 5m;
proxy_read_timeout 360;
proxy_send_timeout 360;
proxy_connect_timeout 360;
# Basic Proxy Config
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
proxy_set_header X-Forwarded-Ssl on;
proxy_redirect http:// $scheme://;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_cache_bypass $cookie_session;
proxy_no_cache $cookie_session;
proxy_buffers 64 256k;
# If behind a reverse proxy, forwards the correct IP, assumes you're using Cloudflare. Adjust IP for your Docker network.
set_real_ip_from 172.17.0.0/16;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
}
nginx-client
location /authelia {
internal;
set $upstream_authelia http://0.0.0.0:9110/api/verify;
proxy_pass_request_body off;
proxy_pass $upstream_authelia;
proxy_set_header Content-Length "";
# Timeout if the real server is dead
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
client_body_buffer_size 128k;
proxy_set_header Host $host;
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
proxy_set_header X-Forwarded-Ssl on;
proxy_redirect http:// $scheme://;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_cache_bypass $cookie_session;
proxy_no_cache $cookie_session;
proxy_buffers 4 32k;
send_timeout 5m;
proxy_read_timeout 240;
proxy_send_timeout 240;
proxy_connect_timeout 240;
}
location / {
set $upstream_app $forward_scheme://$server:$port;
proxy_pass $upstream_app;
auth_request /authelia;
auth_request_set $target_url https://$http_host$request_uri;
auth_request_set $user $upstream_http_remote_user;
auth_request_set $email $upstream_http_remote_email;
auth_request_set $groups $upstream_http_remote_groups;
proxy_set_header Remote-User $user;
proxy_set_header Remote-Email $email;
proxy_set_header Remote-Groups $groups;
error_page 401 =302 https://auth.matt-cloud.com/?rd=$target_url;
client_body_buffer_size 128k;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
send_timeout 5m;
proxy_read_timeout 360;
proxy_send_timeout 360;
proxy_connect_timeout 360;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection upgrade;
proxy_set_header Accept-Encoding gzip;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
proxy_set_header X-Forwarded-Ssl on;
proxy_redirect http:// $scheme://;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_cache_bypass $cookie_session;
proxy_no_cache $cookie_session;
proxy_buffers 64 256k;
set_real_ip_from 172.17.0.0/16;
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
}

