= Keycloak =
<<TableOfContents()>>
== Установка и запуск ==
После перебора различных вариантов установки и запуска Keycloak самым комфортным оказался 'docker-compose'.
Пример конфигурации Keycloak с letsencrypt, базой данных PostgreSQL и автоматическим бекапированием на S3:
{{{#!highlight yaml
---
services:
proxy:
image: traefik:v3.3
command:
- "--entrypoints.web.address=:80"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.myresolver.acme.tlschallenge=true"
- "--certificatesresolvers.myresolver.acme.email=mail@example.com" # <---------------------------
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
ports:
- target: 80
published: 80
protocol: tcp
mode: host
- target: 443
published: 443
protocol: tcp
mode: host
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./letsencrypt:/letsencrypt
keycloak:
image: quay.io/keycloak/keycloak:26.0.1
restart: unless-stopped
command: start
ports:
- "127.0.0.1:8080:8080"
environment:
KC_PROXY_ADDRESS_FORWARDING: "true"
KC_HOSTNAME_STRICT: "false"
KC_HOSTNAME_STRICT_HTTPS: "false"
KC_HOSTNAME: keycloak.example.com # <---------------------------
KC_HTTP_ENABLED: "true"
KC_PROXY_HEADERS: xforwarded
KEYCLOAK_ADMIN: tmpadmin
KEYCLOAK_ADMIN_PASSWORD: secret # <---------------------------
# ---
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/kc
KC_DB_USERNAME: db_username # <---------------------------
KC_DB_PASSWORD: db_secret # <---------------------------
# ---
labels:
- "traefik.http.routers.kc.rule=Host(`keycloak.example.com`)" # <---------------------------
- "traefik.http.routers.kc.service=kc"
- "traefik.http.routers.kc.entrypoints=websecure"
- "traefik.http.routers.kc.tls.certresolver=myresolver"
- "traefik.http.services.kc.loadbalancer.server.port=8080"
postgres:
image: postgres:16
environment:
POSTGRES_USER: db_username # <---------------------------
POSTGRES_PASSWORD: db_password # <---------------------------
POSTGRES_DB: kc
PGDATA: /var/lib/postgresql/data/pgdata
ports:
- "127.0.0.1:5432:5432"
volumes:
- postgresql_data:/var/lib/postgresql/data/pgdata
deploy:
resources:
limits:
cpus: '0.50'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
command: >
postgres -c max_connections=1000
-c shared_buffers=256MB
-c effective_cache_size=768MB
-c maintenance_work_mem=64MB
-c checkpoint_completion_target=0.7
-c wal_buffers=16MB
-c default_statistics_target=100
healthcheck:
test: [ "CMD-SHELL", "pg_isready -U db_username -d kc" ] # <---------------------------
interval: 30s
timeout: 10s
retries: 5
restart: unless-stopped
tty: true
stdin_open: true
backup:
image: eeshugerman/postgres-backup-s3:16
environment:
SCHEDULE: '@daily'
BACKUP_KEEP_DAYS: 14
S3_ENDPOINT: https://s3.endpoint.com # <---------------------------
S3_REGION: my-region # <---------------------------
S3_ACCESS_KEY_ID: access_key # <---------------------------
S3_SECRET_ACCESS_KEY: secret_key # <---------------------------
S3_BUCKET: bucket_name # <---------------------------
S3_PREFIX: backups # <---------------------------
POSTGRES_HOST: postgres
POSTGRES_DATABASE: kc
POSTGRES_USER: db_username # <---------------------------
POSTGRES_PASSWORD: db_password # <---------------------------
volumes:
postgresql_data: {}
}}}
== Интеграция ==
=== Grafana ===
Источники:
. [[ https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/generic-oauth/ ]]
. [[ https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/keycloak/ ]]
. [[ https://stackoverflow.com/questions/68741412/grafana-generic-oauth-role-assignment ]]
Интеграция с Grafana осуществляется в соответствии с документацией.
Однако, необходимо учитывать особенность: Grafana осуществляет поиск ролей в 'id_token', а не 'access_token'. По умолчанию, роли пользователя не попадают в id_token, поэтому надо подкрутить конфигурацию клиента - [[ https://stackoverflow.com/questions/68741412/grafana-generic-oauth-role-assignment ]].
=== SSH ===
Пример конфигурации, которая позволяет предоставлять административный доступ к серверу через `ssh` путем назначения роли пользователю или группе.
План:
1. настраиваем сервер
2. устанавливаем расширение в Keycloak
На этапе настройки сервера устанавливаем [[https://github.com/kha7iq/kc-ssh-pam | kc-ssh-pam]]. В процессе установки можно внести ряд дополнений:
Запрещаем дальнейшие действия в случае неудачной проверки пользователя в Keycloak. Это может быть полезным, если локальный пользователь с таким же именем уже существует на сервере или в Keycloak такого пользователя. В таком случае, пользователь сможет зайти под локальной учетной записью, что не является предпочтительным.
{{{
# --- вместо
auth sufficient pam_exec.so expose_authtok log=/var/log/kc-ssh-pam.log /opt/kc-ssh-pam/kc-ssh-pam
auth optional pam_script.so
# --- использовать
auth [success=2 default=ignore] pam_exec.so expose_authtok log=/var/log/kc-ssh-pam.log /opt/kc-ssh-pam/kc-ssh-pam
auth optional pam_script.so
auth requisite pam_deny.so
}}}
В скрипте `/usr/share/libpam-script/pam_script_auth` добавляем строку
{{{#!highlight bash
adduser $PAM_USER sudo
}}}
чтобы новый пользователь сразу добавлялся в группу `sudo`.
Также, здесь необходимо отключить пароль для `sudo`.
{{{#!highlight bash
sudo visudo
# --- вместо
%sudo ALL=(ALL:ALL) ALL
# --- используем
%sudo ALL=NOPASSWD: ALL
}}}
На втором этапе устанавливаем расширение [[https://github.com/sventorben/keycloak-restrict-client-auth | keycloak-restrict-client-auth ]] для Keycloak. Это расширение нам требуется для ограничение доступа к Realm клиенту, созданному на предыдущем этапе. "Из коробки" (на сколько понимаю) Keycloak не предоставляет такую возможность.
Установку выполняем по [[https://github.com/sventorben/keycloak-restrict-client-auth/blob/main/README.md | README.md ]] для ''Client Role based'' режима. Здесь можно использовать более простой вариант аутентификации
{{devops/keycloak/ssh-flow|SSH authorization flow | width=50%}}
Кроме того, требуется указать новый способ аутентификации в настройках клиента ''ssh-login'': Clients > ssh-login > Advanced > Authentication flow overrides > Direct Grant Flow
По итогу получаем, что при назначении роли `restricted-access` пользователю или группе предоставляется `ssh` доступ с ''sudo'' к преднастроенным серверам.
== Рецепты ==
=== Восстанавливаем доступ администратора ===
Для восстановления пароля требуется доступ к серверу, на котором установлен Keycloak.
Создаем временного администратора
{{{#!highlight bash
bin/kc.sh bootstrap-admin user
}}}
Далее:
1. заходим под временным администратором,
2. сбрасываем пароль,
3. заходим под администратором,
4. удаляем временного администратора.
=== Как получить токен
В целях отладки можно выполнить `curl` запрос к Keycloak и получить токен
{{{#!highlight bash
curl -d "grant_type=password" \
-d "scope=openid" \
-d "client_id=client_id" \
-d "client_secret=client_secret" \
-d "username=username" \
-d "password=secret" \
https://keycloak.example.com/realms/REALM_NAME/protocol/openid-connect/token | jq .
}}}