Replace Authelia & LLDAP by Keycloak

This commit is contained in:
Viyurz 2024-07-06 10:29:57 +02:00
parent ec47ab7a9e
commit 8cd14ac9e6
Signed by: Viyurz
SSH key fingerprint: SHA256:IskOHTmhHSJIvAt04N6aaxd5SZCVWW1Guf9tEcxIMj8
26 changed files with 97 additions and 387 deletions

16
env.yml
View file

@ -1,5 +1,4 @@
domain: viyurz.fr
ldap_base_dn: dc=viyurz,dc=fr
timezone: "Europe/Paris"
host_uid: 1000
project_dir: "{{ ansible_env['HOME'] }}/docker-projects/{{ role_name }}"
@ -39,13 +38,12 @@ cifs_mounts:
projects:
- authelia
- coturn
- element
- etebase
- hedgedoc
- homepage
- lldap
- keycloak
- mailserver
- postgres
- searxng
@ -57,10 +55,9 @@ projects:
projects_to_backup:
- authelia
- etebase
- hedgedoc
- lldap
- keycloak
- mailserver
- postgres
- stump
@ -81,7 +78,6 @@ borg_prune_options: |
# Ports exposed to host
ports:
authelia: 9091
coturn_listening: 3478
coturn_tls_listening: 5349
coturn_relay_min: 49152
@ -90,7 +86,7 @@ ports:
etebase: 3735
hedgedoc: 8086
homepage: 8686
lldap: 17170
keycloak: 8444
mailserver_smtp: 1025
mailserver_smtps: 1465
mailserver_imaps: 1993
@ -112,12 +108,11 @@ ports:
# UID in containers
users:
authelia: 1008
coturn: 666
etebase: 373
hedgedoc: 1004
homepage: 8686
lldap: 1007
keycloak: 1000
mailserver: 8
postgres: 70
searxng: 977
@ -128,7 +123,7 @@ users:
syncthing_discosrv: 1002
syncthing_relaysrv: 1003
uptime_kuma: 1006
vaultwarden: 1000
vaultwarden: 1010
wireguard: 1009
@ -137,7 +132,6 @@ volumes:
coturn_tls_certificate_key_file: "/etc/letsencrypt/live/turn.{{ domain }}/privkey.pem"
etebase_datadir: /mnt/etebasedata
hedgedoc_uploadsdir: /mnt/hedgedocuploads
lldap_datadir: /mnt/lldapdata
mailserver_datadir: /mnt/mailserver
mailserver_tls_certificate_file: "/etc/letsencrypt/live/mail.{{ domain }}/fullchain.pem"
mailserver_tls_certificate_key_file: "/etc/letsencrypt/live/mail.{{ domain }}/privkey.pem"

View file

@ -1,25 +0,0 @@
- name: "(Re)Create {{ project_dir }} project directory"
file:
path: "{{ project_dir }}"
state: "{{ item }}"
loop:
- absent
- directory
- name: Template docker-compose.yaml & configuration.yml to project directory
template:
src: "{{ item }}"
dest: "{{ project_dir }}/{{ item }}"
owner: "{{ host_uid }}"
group: "{{ host_uid }}"
mode: '640'
loop:
- docker-compose.yaml
- configuration.yml
# Separate task because template module cannot chown/chgrp to a non-existing user/group
- name: "Change group of homeserver.yaml to Authelia GID ({{ users['authelia'] + uid_shift }})"
file:
path: "{{ project_dir }}/configuration.yml"
group: "{{ users['authelia'] + uid_shift }}"
become: true

View file

@ -1,96 +0,0 @@
theme: 'auto'
totp:
issuer: '{{ domain }}'
identity_validation:
reset_password:
jwt_secret: '{{ authelia_secrets["jwt_secret"] }}'
authentication_backend:
refresh_interval: '1m'
ldap:
implementation: 'custom'
address: 'ldap://lldap:3890'
base_dn: '{{ ldap_base_dn }}'
users_filter: '(&({username_attribute}={input})(objectClass=person))'
groups_filter: '(member={dn})'
user: '{{ authelia_secrets["ldap_user"] }}'
password: '{{ authelia_secrets["ldap_password"] }}'
attributes:
distinguished_name: 'distinguishedName'
username: 'uid'
mail: 'mail'
member_of: 'memberOf'
group_name: 'cn'
password_policy:
standard:
enabled: true
min_length: 12
max_length: 128
require_uppercase: true
require_lowercase: true
require_number: true
require_special: true
access_control:
default_policy: 'deny'
rules:
- domain: 'auth.{{ domain }}'
policy: 'bypass'
- domain: 'ldap.{{ domain }}'
policy: 'two_factor'
subject: 'group:lldap_admin'
- domain: 'syncthing.{{ domain }}'
policy: 'two_factor'
subject: 'user:viyurz'
session:
cookies:
- name: 'authelia_session'
domain: '{{ domain }}'
authelia_url: 'https://auth.{{ domain }}'
storage:
encryption_key: '{{ authelia_secrets["encryption_key"] }}'
postgres:
address: postgres.{{ domain }}
database: authelia
username: '{{ authelia_secrets["postgres_user"] }}'
password: '{{ authelia_secrets["postgres_password"] }}'
notifier:
smtp:
address: 'submissions://mail.{{ domain }}:{{ ports["mailserver_smtps"] }}'
username: '{{ authelia_secrets["smtp_user"] }}'
password: '{{ authelia_secrets["smtp_password"] }}'
sender: 'Authelia <authelia@{{ domain }}>'
identity_providers:
oidc:
hmac_secret: '{{ authelia_secrets["hmac_secret"] }}'
jwks:
- key: |
{{ authelia_secrets["jwks_key"] | indent(width=10) }}
clients:
- client_id: '{{ authelia_secrets["hedgedoc_client_id"] }}'
client_name: HedgeDoc
client_secret: '{{ authelia_secrets["hedgedoc_client_secret_hash"] }}'
redirect_uris: 'https://hedgedoc.{{ domain }}/auth/oauth2/callback'
scopes:
- 'openid'
- 'profile'
- 'email'
token_endpoint_auth_method: client_secret_post
- client_id: '{{ authelia_secrets["synapse_client_id"] }}'
client_name: Synapse
client_secret: '{{ authelia_secrets["synapse_client_secret_hash"] }}'
redirect_uris: 'https://matrix.{{ domain }}/_synapse/client/oidc/callback'
scopes:
- 'openid'
- 'profile'
- 'email'

View file

@ -1,16 +0,0 @@
services:
authelia:
container_name: authelia
image: docker.io/authelia/authelia:4
restart: always
user: {{ users['authelia'] }}:{{ users['authelia'] }}
networks:
- authelia
ports:
- 127.0.0.1:{{ ports['authelia'] }}:9091
volumes:
- ./configuration.yml:/config/configuration.yml
networks:
authelia:
name: authelia

View file

@ -6,14 +6,14 @@ CMD_DB_PASSWORD='{{ hedgedoc_secrets["postgres_password"] }}'
CMD_DOMAIN='hedgedoc.{{ domain }}'
CMD_PROTOCOL_USESSL=true
CMD_SESSION_SECRET='{{ hedgedoc_secrets["session_secret"] }}'
CMD_ALLOW_EMAIL_REGISTER=false
CMD_EMAIL=false
CMD_OAUTH2_PROVIDERNAME=Authelia
CMD_OAUTH2_CLIENT_ID='{{ authelia_secrets["hedgedoc_client_id"] }}'
CMD_OAUTH2_CLIENT_SECRET='{{ authelia_secrets["hedgedoc_client_secret"] }}'
CMD_OAUTH2_AUTHORIZATION_URL=https://auth.{{ domain }}/api/oidc/authorization
CMD_OAUTH2_TOKEN_URL=https://auth.{{ domain }}/api/oidc/token
CMD_OAUTH2_USER_PROFILE_URL=https://auth.{{ domain }}/api/oidc/userinfo
CMD_OAUTH2_PROVIDERNAME=Keycloak
CMD_OAUTH2_CLIENT_ID='{{ hedgedoc_secrets["client_id"] }}'
CMD_OAUTH2_CLIENT_SECRET='{{ hedgedoc_secrets["client_secret"] }}'
CMD_OAUTH2_AUTHORIZATION_URL=https://kc.{{ domain }}/realms/master/protocol/openid-connect/auth
CMD_OAUTH2_TOKEN_URL=https://kc.{{ domain }}/realms/master/protocol/openid-connect/token
CMD_OAUTH2_USER_PROFILE_URL=https://kc.{{ domain }}/realms/master/protocol/openid-connect/userinfo
CMD_OAUTH2_SCOPE=openid email profile
CMD_OAUTH2_USER_PROFILE_USERNAME_ATTR=preferred_username
CMD_OAUTH2_USER_PROFILE_DISPLAY_NAME_ATTR=name

View file

@ -1,4 +1,4 @@
- name: "Backup PostgreSQL authelia database"
- name: "Backup PostgreSQL keycloak database"
shell: >
docker exec postgres
pg_dump -c {{ role_name }} |

View file

@ -0,0 +1,19 @@
- name: "(Re)Create {{ project_dir }} project directory"
file:
path: "{{ project_dir }}"
state: "{{ item }}"
loop:
- absent
- directory
- name: Template Dockerfile, docker-compose.yaml & .env to project directory
template:
src: "{{ item }}"
dest: "{{ project_dir }}/{{ item }}"
owner: "{{ host_uid }}"
group: "{{ host_uid }}"
mode: '640'
loop:
- Dockerfile
- docker-compose.yaml
- .env

View file

@ -5,18 +5,18 @@
pull: true
debug: true
when: docker_pull_images | bool
register: authelia_docker_compose_pull_result
register: keycloak_docker_compose_pull_result
- name: Display pulled image(s) name
set_fact:
authelia_pulled_images: "{{ authelia_pulled_images | default([]) + [item.pulled_image.name] }}"
loop: "{{ authelia_docker_compose_pull_result['actions'] | default([]) | selectattr('pulled_image', 'defined') }}"
keycloak_pulled_images: "{{ keycloak_pulled_images | default([]) + [item.pulled_image.name] }}"
loop: "{{ keycloak_docker_compose_pull_result['actions'] | default([]) | selectattr('pulled_image', 'defined') }}"
- name: Include backup tasks
include_tasks:
file: backup.yml
# Make a backup if we didn't already make one and we pulled a new image
when: not run_backup | default(false) and authelia_pulled_images is defined
when: not run_backup | default(false) and keycloak_pulled_images is defined
- name: Create/Restart project services
community.docker.docker_compose:

View file

@ -0,0 +1,12 @@
QUARKUS_TRANSACTION_MANAGER_ENABLE_RECOVERY=true
#KEYCLOAK_ADMIN=
#KEYCLOAK_ADMIN_PASSWORD=
KC_DB_URL_HOST=postgres.{{ domain }}
KC_DB_URL_DATABASE=keycloak
KC_DB_USERNAME={{ keycloak_secrets['postgres_user'] }}
KC_DB_PASSWORD='{{ keycloak_secrets["postgres_password"] }}'
KC_PROXY_HEADERS=xforwarded
KC_HOSTNAME=https://kc.{{ domain }}

View file

@ -0,0 +1,15 @@
FROM quay.io/keycloak/keycloak:25.0 as builder
ENV KC_DB=postgres
WORKDIR /opt/keycloak
RUN keytool -genkeypair -storepass password -storetype PKCS12 -keyalg RSA -keysize 2048 -dname "CN=server" -alias server -ext "SAN:c=IP:127.0.0.1" -keystore conf/server.keystore
RUN /opt/keycloak/bin/kc.sh build
FROM quay.io/keycloak/keycloak:25.0
COPY --from=builder /opt/keycloak/ /opt/keycloak/
ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]
CMD ["start", "--optimized"]

View file

@ -0,0 +1,9 @@
services:
keycloak:
container_name: keycloak
build: .
restart: always
user: {{ users['keycloak'] }}:{{ users['keycloak'] }}
env_file: .env
ports:
- 127.0.0.1:{{ ports['keycloak'] }}:8443

View file

@ -1,25 +0,0 @@
- name: "Backup PostgreSQL lldap database & {{ volumes['lldap_datadir'] }} directory"
shell: >
docker exec postgres
pg_dump -c {{ role_name }} |
borg create
--compression lzma
"{{ borg_repodir }}::{{ role_name }}-{now:%Y-%m-%d_%H-%M-%S}"
"{{ volumes['lldap_datadir'] }}"
-
--stdin-name dump_{{ role_name }}.sql
environment:
DOCKER_HOST: "{{ docker_host }}"
BORG_PASSCOMMAND: "cat {{ borg_passphrase_file }}"
become: true
- name: Prune borg repository
command:
cmd: |
borg prune
--glob-archives='{{ role_name }}-*'
{{ borg_prune_options }}
{{ borg_repodir }}
environment:
BORG_PASSCOMMAND: "cat {{ borg_passphrase_file }}"
become: true

View file

@ -1,14 +0,0 @@
- name: Include backup tasks
include_tasks:
file: backup.yml
when: run_backup | default(false) | bool
- name: Include setup tasks
include_tasks:
file: setup.yml
when: run_setup | default(false) | bool
- name: Include update tasks
include_tasks:
file: update.yml
when: run_update | default(false) | bool

View file

@ -1,27 +0,0 @@
- name: "(Re)Create {{ project_dir }} project directory"
file:
path: "{{ project_dir }}"
state: "{{ item }}"
loop:
- absent
- directory
- name: Template docker-compose.yaml & .env to project directory
template:
src: "{{ item }}"
dest: "{{ project_dir }}/{{ item }}"
owner: "{{ host_uid }}"
group: "{{ host_uid }}"
mode: '600'
loop:
- docker-compose.yaml
- .env
- name: "Create (if not exists) directory {{ volumes['lldap_datadir'] }} & set permissions"
file:
path: "{{ volumes['lldap_datadir'] }}"
state: directory
owner: "{{ users['lldap'] + uid_shift }}"
group: "{{ users['lldap'] + uid_shift }}"
mode: '700'
become: true

View file

@ -1,24 +0,0 @@
- name: Pull project services
community.docker.docker_compose:
project_src: "{{ project_dir }}"
recreate: never
pull: true
debug: true
when: docker_pull_images | bool
register: lldap_docker_compose_pull_result
- name: Display pulled image(s) name
set_fact:
lldap_pulled_images: "{{ lldap_pulled_images | default([]) + [item.pulled_image.name] }}"
loop: "{{ lldap_docker_compose_pull_result['actions'] | default([]) | selectattr('pulled_image', 'defined') }}"
- name: Include backup tasks
include_tasks:
file: backup.yml
# Make a backup if we didn't already make one and we pulled a new image
when: not run_backup | default(false) and lldap_pulled_images is defined
- name: Create/Restart project services
community.docker.docker_compose:
project_src: "{{ project_dir }}"
restarted: "{{ run_setup | default(false) | bool }}"

View file

@ -1,7 +0,0 @@
UID={{ users['lldap'] }}
GID={{ users['lldap'] }}
TZ={{ timezone }}
LLDAP_LDAP_BASE_DN={{ ldap_base_dn }}
LLDAP_JWT_SECRET='{{ lldap_secrets["jwt_secret"] }}'
LLDAP_KEY_SEED='{{ lldap_secrets["key_seed"] }}'
LLDAP_DATABASE_URL='postgres://{{ lldap_secrets["postgres_user"] }}:{{ lldap_secrets["postgres_password"] }}@postgres.{{ domain }}/lldap'

View file

@ -1,17 +0,0 @@
services:
lldap:
container_name: lldap
image: docker.io/lldap/lldap:2024-06-13-alpine-rootless
restart: always
user: {{ users['lldap'] }}:{{ users['lldap'] }}
env_file: .env
networks:
- authelia
ports:
- {{ ports['lldap'] }}:17170
volumes:
- {{ volumes['lldap_datadir'] }}:/data
networks:
authelia:
name: authelia

View file

@ -1,10 +0,0 @@
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name auth.{{ domain }};
location / {
proxy_pass http://127.0.0.1:{{ ports['authelia'] }};
}
}

View file

@ -0,0 +1,13 @@
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name kc.{{ domain }};
location / {
proxy_pass https://127.0.0.1:{{ ports['keycloak'] }};
#include /etc/nginx/snippets/websocket.conf;
#include /etc/nginx/snippets/proxy.conf;
}
}

View file

@ -1,14 +0,0 @@
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name ldap.{{ domain }};
include /etc/nginx/snippets/authelia-location.conf;
location / {
proxy_pass http://127.0.0.1:{{ ports['lldap'] }};
include /etc/nginx/snippets/authelia-authrequest.conf;
}
}

View file

@ -1,14 +0,0 @@
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name syncthing.{{ domain }};
include /etc/nginx/snippets/authelia-location.conf;
location / {
proxy_pass http://127.0.0.1:{{ ports['syncthing_webui'] }};
include /etc/nginx/snippets/authelia-authrequest.conf;
}
}

View file

@ -1,15 +0,0 @@
auth_request /internal/authelia/authz;
auth_request_set $user $upstream_http_remote_user;
auth_request_set $groups $upstream_http_remote_groups;
auth_request_set $name $upstream_http_remote_name;
auth_request_set $email $upstream_http_remote_email;
proxy_set_header Remote-User $user;
proxy_set_header Remote-Groups $groups;
proxy_set_header Remote-Email $email;
proxy_set_header Remote-Name $name;
auth_request_set $redirection_url $upstream_http_location;
error_page 401 =302 $redirection_url;

View file

@ -1,18 +0,0 @@
location /internal/authelia/authz {
internal;
proxy_pass http://127.0.0.1:{{ ports['authelia'] }}/api/authz/auth-request;
proxy_set_header X-Original-Method $request_method;
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Content-Length "";
proxy_set_header Connection "";
proxy_pass_request_body off;
proxy_http_version 1.1;
proxy_cache_bypass $cookie_session;
proxy_no_cache $cookie_session;
proxy_buffers 4 32k;
client_body_buffer_size 128k;
}

View file

@ -108,17 +108,12 @@ suppress_key_server_warning: true
# Single sign-on integration
oidc_providers:
- idp_id: authelia
idp_name: "Authelia"
idp_icon: "mxc://authelia.com/cKlrTPsGvlpKxAYeHWJsdVHI"
discover: false
issuer: "https://auth.{{ domain }}"
client_id: '{{ authelia_secrets["synapse_client_id"] }}'
client_secret: '{{ authelia_secrets["synapse_client_secret"] }}'
- idp_id: keycloak
idp_name: "Keycloak"
issuer: "https://kc.{{ domain }}/realms/master"
client_id: '{{ synapse_secrets["client_id"] }}'
client_secret: '{{ synapse_secrets["client_secret"] }}'
scopes: ["openid", "profile", "email"]
authorization_endpoint: 'https://auth.{{ domain }}/api/oidc/authorization'
token_endpoint: 'https://auth.{{ domain }}/api/oidc/token'
jwks_uri: 'https://auth.{{ domain }}/jwks.json'
allow_existing_users: true
user_mapping_provider:
config:
@ -126,3 +121,7 @@ oidc_providers:
localpart_template: "{% raw %}{{ user.preferred_username }}{% endraw %}"
display_name_template: "{% raw %}{{ user.name }}{% endraw %}"
email_template: "{% raw %}{{ user.email }}{% endraw %}"
backchannel_logout_enabled: true
password_config:
enabled: false

View file

@ -8,46 +8,17 @@ cifs_credentials:
username:
password:
authelia_secrets:
# Encryption key for the database, must be saved
encryption_key:
# Generate random client id : docker run --rm authelia/authelia:4 authelia crypto rand --length 72 --charset rfc3986
# Generate random secret + hash : docker run --rm authelia/authelia:4 authelia crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986
hedgedoc_client_id:
hedgedoc_client_secret:
hedgedoc_client_secret_hash:
synapse_client_id:
synapse_client_secret:
synapse_client_secret_hash:
hmac_secret:
jwks_key: | # openssl genrsa 4096
jwt_secret:
# LDAP bind dn
ldap_user:
ldap_password:
postgres_user:
postgres_password:
smtp_user:
smtp_password:
coturn_secrets:
static_auth_secret:
hedgedoc_secrets:
client_id:
client_secret:
postgres_user:
postgres_password:
session_secret:
lldap_secrets:
jwt_secret:
key_seed:
keycloak_secerts:
postgres_user:
postgres_password: