Keycloak

Установка и запуск

После перебора различных вариантов установки и запуска Keycloak самым комфортным оказался 'docker-compose'.

Пример конфигурации Keycloak с letsencrypt, базой данных PostgreSQL и автоматическим бекапированием на S3:

---                                                                                                                    
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

Источники:

Интеграция с Grafana осуществляется в соответствии с документацией. Однако, необходимо учитывать особенность: Grafana осуществляет поиск ролей в 'id_token', а не 'access_token'. По умолчанию, роли пользователя не попадают в id_token, поэтому надо подкрутить конфигурацию клиента - https://stackoverflow.com/questions/68741412/grafana-generic-oauth-role-assignment.

SSH

Пример конфигурации, которая позволяет предоставлять административный доступ к серверу через ssh путем назначения роли пользователю или группе.

План:

  1. настраиваем сервер
  2. устанавливаем расширение в Keycloak

На этапе настройки сервера устанавливаем 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 добавляем строку

adduser $PAM_USER sudo

чтобы новый пользователь сразу добавлялся в группу sudo.

Также, здесь необходимо отключить пароль для sudo.

sudo visudo

# --- вместо
%sudo  ALL=(ALL:ALL) ALL
# --- используем
%sudo ALL=NOPASSWD: ALL

На втором этапе устанавливаем расширение keycloak-restrict-client-auth для Keycloak. Это расширение нам требуется для ограничение доступа к Realm клиенту, созданному на предыдущем этапе. "Из коробки" (на сколько понимаю) Keycloak не предоставляет такую возможность.

Установку выполняем по README.md для Client Role based режима. Здесь можно использовать более простой вариант аутентификации

SSH authorization flow

Кроме того, требуется указать новый способ аутентификации в настройках клиента ssh-login: Clients > ssh-login > Advanced > Authentication flow overrides > Direct Grant Flow

По итогу получаем, что при назначении роли restricted-access пользователю или группе предоставляется ssh доступ с sudo к преднастроенным серверам.

Рецепты

Восстанавливаем доступ администратора

Для восстановления пароля требуется доступ к серверу, на котором установлен Keycloak.

Создаем временного администратора

bin/kc.sh bootstrap-admin user

Далее:

  1. заходим под временным администратором,
  2. сбрасываем пароль,
  3. заходим под администратором,
  4. удаляем временного администратора.

Как получить токен

В целях отладки можно выполнить curl запрос к Keycloak и получить токен

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 .