3 ポイント 投稿者 GN⁺ 2024-02-12 | 1件のコメント | WhatsAppで共有
  • 単一のLinux VM上でKeycloakをrootless Dockerで分離実行し、システムのnginxがTLS終端とリバースプロキシを担うSSOデプロイ手順
  • 前提条件は、SSHアクセス、Keycloak用のドメインまたはサブドメイン、Aレコード、パスワードなしのkeycloakユーザー、/srv/keycloakホームディレクトリ、Docker rootlessのインストール
  • docker-compose.ymlpostgres:16quay.io/keycloak/keycloak:25.0.1を使用し、Keycloakは**127.0.0.1:8080のみにバインド**し、.envでパスワードとKC_HOSTNAMEを管理する
  • nginxはyour.tld.comへのHTTPリクエストをHTTPSへリダイレクトしてproxy_passで転送し、Certbot証明書の発行後にKC_PROXY_HEADERS: xforwardedを有効化する
  • カスタムテーマや--optimizedでの実行が必要な場合は、マルチステージDockerfileで独自イメージを作成し、本番運用中はJGroupsのバッファサイズとQuarkusのトランザクションリカバリ設定を追加調整できる

デプロイ構成と前提

  • Keycloakは、Webアプリケーションでユーザー管理とSSOを自前で実装するのが難しい場合に使えるオープンソースIAMの選択肢
  • OpenID Connect (OIDC)OAuth 2.0SAMLなど主要なSSOプロトコルと相互運用できる
  • 構成はKeycloak 23.0.6を基準に作成され、25.0.5までテスト済み
  • 全体構成はnginxを中央のリバースプロキシに置き、サービスごとのrootless Docker名前空間へlocalhost経由でトラフィックを渡す方式
    • Webトラフィックは0.0.0.0:800.0.0.0:443に入り、nginxが127.0.0.1:8080のKeycloakへ転送する
    • 単一ホストのリソースを共有しつつ、サービス環境を分離する経済的な構成を目指す

事前準備

  • 基本環境としてLinux VM、SSHによるリモートアクセス、Keycloakサービス用のドメインまたはサブドメインが必要
    • ドメインにはAレコードが必要で、任意でAAAAレコードも追加できる
  • Docker rootlessの基本設定は、別のMastodon投稿の手順に従う
    • パスワードなしの新しいnon-rootユーザーkeycloakを作成
    • ホームディレクトリを/srv/keycloakに設定
    • /etc/subuid/etc/subgidkeycloakユーザーの範囲を追加
    • dockerd-rootless-setuptool.shでDocker rootlessをインストール
    • keycloakユーザーのサービス自動起動を設定

Docker ComposeでKeycloakを実行

  • 新規作成したkeycloakユーザーに入るときはmachinectl shell keycloak@を使う
    • sudo -u keycloak -H bash方式は、XDG_RUNTIME_DIR環境変数が準備されないため避ける
  • 永続データとDockerファイル用ディレクトリを先に作成する
    • PostgreSQLデータのパスは/srv/keycloak/data/postgres16
    • Dockerファイルは~/dockerに置く
  • docker-compose.ymlは公式KeycloakイメージとPostgreSQLを直接使用する
    • PostgreSQLイメージはpostgres:16
    • Keycloakイメージはquay.io/keycloak/keycloak:25.0.1
    • 本番環境では:latestではなく特定のイメージタグの使用を推奨
  • Keycloakコンテナの主な設定は次のとおり
    • KC_DB: postgres
    • KC_DB_URL: jdbc:postgresql://postgres_db/keycloak
    • KC_HOSTNAME: ${KC_HOSTNAME:-your.tld.com}
    • KC_HTTP_ENABLED: true
    • HTTP_ADDRESS_FORWARDING: true
    • KEYCLOAK_ADMIN: admin
    • ポートは'127.0.0.1:8080:8080'としてlocalhostのみにバインドする
  • .envには機微な値や環境ごとに変わる値を置く
    • POSTGRES_PASSWORD
    • KEYCLOAK_ADMIN_PASSWORD
    • KC_HOSTNAME
  • ${KC_HOSTNAME:-your.tld.com}構文は、.envKC_HOSTNAMEがあればその値を使い、なければyour.tld.comをデフォルト値として使う
  • /srv/keycloak/data/postgres16はPostgreSQLの永続データを含むため、定期バックアップの対象
    • 完全に初期化するには、該当するPostgreSQLフォルダを削除して再作成すれば、次回起動時に作り直される
  • ~/dockerをGitリポジトリとして管理するなら、.env.gitignoreに追加し、docker-compose.ymlだけをコミットできる

ローカルテスト

  • Docker Composeスタックを起動してログを確認する
docker compose up -d && docker compose logs --follow
  • VMのKeycloakローカルポートへアクセスするには、リバースSSHトンネルを作成する
ssh you@111.11.11.11 -L :8080:127.0.0.1:8080 -p 22 -N -v
  • ブラウザで127.0.0.1:8080を開き、Keycloakのウェルカム画面を確認する

nginxリバースプロキシとTLS

  • keycloakユーザーからログアウトしたあと、システムのnginx設定を進める
  • your.tld.comは実際のドメインに置き換え、ドメインレジストラでAレコードを追加してVMのIPへDNSクエリが向くように設定する
  • nginxのサイト設定ファイルを作成して有効化する
nano /etc/nginx/sites-available/your.tld.com.conf
ln -s /etc/nginx/sites-available/your.tld.com.conf /etc/nginx/sites-enabled/
  • nginx設定ではHTTPリクエストをHTTPSへリダイレクトし、HTTPSサーバーブロックでKeycloakへプロキシする
    • proxy_set_header Host $host
    • proxy_set_header X-Real-IP $remote_addr
    • proxy_set_header X-Forwarded-For $remote_addr
    • proxy_set_header X-Forwarded-Proto $scheme
    • proxy_pass http://127.0.0.1:8080
  • SSL設定はMozilla SSL configurator for nginxを使い、nginxのバージョンに合うデフォルト値を生成する方法を推奨
  • 設定をテストしたあとnginxをリロードする
nginx -t
systemctl reload nginx
  • Certbotで証明書を発行すると、your.tld.com.confの必要な行が自動更新される
certbot --nginx -d your.tld.com
  • その後、ssl_trusted_certificate /etc/letsencrypt/live/your.tld.com/chain.pem;を有効化し、nginxを再度リロードする
  • nginxの背後で動作するように、docker-compose.ymlKC_PROXY_HEADERS: xforwardedを有効化したうえでDockerスタックを再起動する
docker compose down && docker compose up -d && docker compose logs --follow

デバッグ手順

  • 設定後、your.tld.comに接続し、.envのパスワードでadminユーザーとしてログインできることを確認する
  • 最初の確認ポイントはDocker Composeのログ
docker compose logs --follow
  • nginxのアクセスログとエラーログは、次のファイルを追跡して確認する
tail -f /var/log/nginx/your.tld.com-access.log
tail -f /var/log/nginx/your.tld.com-error.log
  • Keycloakデータベースを直接確認する必要がある場合は、keycloakユーザーに入り、PostgreSQLコンテナ内でpsqlを実行する
machinectl shell keycloak@
cd ~/docker
docker compose exec postgres_db /bin/bash
psql -h localhost -p 5432 -U keycloak keycloak

カスタムイメージとoptimized実行

  • 基本構成はquay.ioのビルド済みイメージを使用する
  • テーマのカスタマイズや--optimizedモードでの実行が必要な場合は、独自イメージをビルドする
  • Dockerfileは公式Keycloakイメージをベースにマルチステージビルドを使用する
    • builderステージでENV KC_DB=postgresを設定
    • /opt/keycloak/bin/kc.sh buildを実行
    • 最終イメージへ/opt/keycloak/をコピー
    • ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]を指定
  • docker-compose.ymlでは次のように変更する
    • image:行を削除またはコメントアウト
    • build: .を有効化
    • command: start --optimizedを追加
  • その後Dockerスタックを停止し、任意で明示的なビルドを実行してから再起動する
docker compose down
docker compose build
docker compose up -d && docker compose logs --follow

本番環境で追加調整した項目

  • JGroups関連のMulticastSocket受信バッファ警告は、ホストの/etc/sysctl.confにバッファ値を追加して解決する
net.core.rmem_max = 26214400
net.core.wmem_max = 1048576
  • 設定反映にはsysctl -pを実行する
  • Quarkus XAトランザクションリカバリ警告は、Keycloakサービスにボリュームマウントと環境変数を追加して解決する
    • ボリューム: /srv/keycloak/data/keycloak/ObjectStore/:/ObjectStore/
    • 環境変数: QUARKUS_TRANSACTION_MANAGER_ENABLE_RECOVERY: true
  • 起動前にホスト上にObjectStoreディレクトリを作成し、コンテナ内で/ObjectStoreの所有者をユーザーID1000に変更する

設定後のステップ

  • 最終状態は、rootless Docker内のKeycloakサービスがシステムnginxリバースプロキシの背後で実行され、nginxがSSL終端を担当する構成
  • コンテナの自動更新は、別のMastodon投稿の自動更新手順を参照
  • 次の設定ステップは、Keycloak管理コンソールでメールを追加すること
  • その後、realmの追加とテーマ設定を進められる
    • テーマにはkeycloakifykeycloakify-starterを利用できる
    • DockerfileにはkeycloakifyテーマJARを/opt/keycloak/providers/へコピーするコメントアウト済みの行が含まれている

1件のコメント

 
GN⁺ 2024-02-12
Hacker Newsのコメント
  • 最近ホームラボに認証を導入するにあたって Authelia を選んだ
    Keycloak も動くが大きすぎるし、traefik forward auth と一緒に使うには追加サービスがさらに必要になる
    Authelia にはユーザー編集 UI がなく、バックエンドの LDAP サーバーとの双方向同期でもないが、静的ファイルと環境変数だけで設定できるので、多くのケースによく合う
    いくつかのサービスに認証を追加し、対応可能なサービスには SSO を付ける程度なら、Authelia から始めてみる価値がある

    • FusionAuth で働いている
      SSO、簡単な設定、管理者 UI が必要なら FusionAuth も検討に値する。UI/UX は 2000 年代半ばの雰囲気だが、自分でダウンロードして実行でき[0]、Docker フレンドリーで[1]、OIDC などの設定を Terraform[3] で管理するなど、複数の設定方法[2]を提供している
      無料で使えるが、オープンソースではない[4]
      0: https://fusionauth.io/download
      1: https://fusionauth.io/docs/get-started/download-and-install/...
      2: https://fusionauth.io/docs/operate/deploy/configuration-mana...
      3: https://fusionauth.io/docs/operate/deploy/terraform
      4: https://fusionauth.io/license-faq#28
    • Authelia を 2 年以上運用しており、軽量な LDAP 実装である LLDAP[0] で LDAP 連携を構成した
      Caddy をリバースプロキシとして使い、Authelia と統合[1]してうまく動いている
      すべてのサービスに堅牢な 2 要素認証を導入しており、セルフホストのアプリに VPN なしでアクセスしても十分安全だと感じている
      ただし、Authelia は 1 年以上新しいリリースがないので、セキュリティ面が心配だ
      [0] https://github.com/lldap/lldap
      [1] https://www.authelia.com/integration/proxies/caddy/
    • 最近同じ道をたどり、個人的には Caddy プラグインの caddy-security[1] を選んだ
      [1] https://github.com/greenpau/caddy-security
    • Authelia には 小規模な導入環境 という非常に独特な強みがあり、Keycloak や authentik はこの領域にはうまくはまりにくい
      ホームラボ環境では非常に良いユースケースであり、より大規模なソリューションの機能が不要、または望まないなら特によく合う
    • 脇道だが、最近 LDAP の代替 があるのか気になっている
      探してみても単純なスキーマ、JSON、HTTP のような形は見つからず、ほとんどすべてを作り直した人たちが LDAP だけは作り直していないのが信じがたい
      知る限り、事実上ほかの標準は Active Directory しかない
      また、アクセス制御を外部に委譲する標準やプロトコルがあるのかも気になる
      Authelia は URL パターンでアクセス制御できるが、たとえばファイルサーバーなら、認証済みユーザー ID とデータベースのキーを基準に LDAP サーバーへ権限を確認するような形を期待してしまう
      これは、サーバーが第三者サービスへのアクセス権を得る OAuth2 とは逆方向のように見える
  • 最近、ホームラボで比較的シンプルな SSO を構成しようと調べていて、Google や GitHub 認証で簡単にログインできることが主な目標だった。
    前職では JetBrains Hub[1] と Keycloak の両方を使ったことがあるが、どちらも設定がかなり面倒だった。
    JetBrains Hub は本当に簡単に始められたし、以前の経験でもそうだったが、Docker レジストリに latest タグがない点は不便だった。
    バージョン固定が良いのは分かるが、個人用途ではたいていすべての Docker コンテナを一度に更新したい。
    一方で Keycloak は立ち上げが非常に煩雑で、開発モードでは簡単だったが、本番構成で行き詰まった。
    記憶ではワイルドカードの Let's Encrypt 証明書に関係していて、数時間後に諦めた。
    結局 Dex[2] を選んだ。ドキュメント不足で後回しにしていたが、実際の設定はとても簡単で、基本的な YAML、SQLite データベース、サブドメイン 1 つで済んだ。
    Dex を優れた OAuth2 Proxy[3] とカスタム Nginx Proxy Manager テンプレートと組み合わせ、内部サービスごとに 2 行の SSO 設定で済むようにし、unRAID 向けの Dex Docker テンプレート[4] も作った。
    さらに、家の外からは Cloudflare Access と WAF を追加してセキュリティを強化し、もう少しインサイトを得るために CrowdSec も追加したい。

    1. https://www.jetbrains.com/hub/
    2. https://dexidp.io/
    3. https://github.com/oauth2-proxy/oauth2-proxy
    4. https://github.com/alex3305/unraid-docker-templates
    • obligator を一時的なストア、データベースなし、100% コードベースの設定で使っている。
      個人的には、これがいちばんシンプルな選択肢だと思う。
      https://github.com/lastlogin-io/obligator
    • Dex にないどんな機能を oauth2-proxy が提供しているのか気になる。
  • セルフホスト可能な複数の OpenID Connect サーバー の未完成な比較表を作った[0]。
    驚いた点の 1 つは、Keycloak のコードベースが本当に巨大だということだ。
    [0]: https://github.com/lastlogin-io/obligator?tab=readme-ov-file...

  • Keycloak がセキュリティ問題を解決してくれるという考えは滑稽だ。
    CVE リスト を見れば意味が分かる。

  • 少なくともその記事で https://www.keycloakify.dev/ に触れていたのはとても有用だ。
    標準的なテーマ方式の優れた代替手段に見える。

    • 素晴らしいプロジェクトだ。
      2 年使っているが、良いことしか言えない。
      メンテナーの対応はとても速く、最近では Keycloak と keycloakify の両方に、テーマ方式の将来互換性を高める変更も入った。
      現在公式には create-react-app で作ったアプリしかサポートしていないが、vite ブランチ が開発中で、個人的にはすでにしばらく vite と一緒に使っている。
    • 競合他社で働いているが、テーマシステムを作り直しているところなので、このプロジェクトを確認して作り直しのモデルにするのに良さそうだ。
  • authentik[1] と authelia[2] に注目している
    Authelia はかなり良さそうに見えるが、Keycloak には Angular 向けコネクタがある一方で、Authelia ではたとえば OIDC の Angular プラグインを自分で設定しなければならず、少し慎重になっていた
    それでも、Keycloak 向けの設定があれば導入はより簡単になりそうだ
    [1] https://goauthentik.io/
    [2] https://www.authelia.com/

    • authentik を検討している人には、「魔境だ」と警告したい
      常時 10 個以上のサービスを Docker と Kubernetes 上で保護しているが、各サービスごとに保護設定を独立して行うのが好きでないなら、authentik では苦労することになる
      authentik には、複数のサブドメイン(app1.example.com、app2.example.com など)を 1 つの設定で保護している場合、セッション失効後に再認証すると、ユーザーがランダムに別のサービスへリダイレクトされる重大なバグ[0]がある
      [0]: https://github.com/goauthentik/authentik/issues/6886
    • Authentik は OAuth クライアントクレデンシャルグラント の実装を完全に壊している
      互換性を壊す変更なしには修正できず、cc グラントを使う多くのツールと動作しない
      これを見て、候補から完全に外した
      https://github.com/goauthentik/authentik/issues/6139
    • Authelia のコアメンテナの一人です
      Angular の設定で手助けできることがあれば、GitHub Discussions を通じて喜んで協力します
    • ちょうど今、同じような判断をしようとしている
      Authentik のほうが良さそうに見えたが、別の返信で触れられていたバグはかなり厳しそうだ
  • Keycloak の問題は、古いプロジェクトなので変化が非常に多かったことにある
    JBOSS プロジェクトとして始まり、IdP としての有用性はオンプレミスのクラスタ認証で真価を発揮するが、それ以上ではないと思う
    Fortune 500 のある部門で AWS ECS 上に Keycloak を大規模導入したが、きちんとクラスタリングさせるまでの過程は千年戦争のようだった
    DNS ディスカバリはまともに動かず、クラスタディスカバリは UDP ベースだったためクラウド環境では機能しなかった
    あるサーバー上の状態を持つログイン情報が別のサーバーには存在しないため、単純な負荷分散は不可能で、選択肢は スティッキーセッション だけだった
    Keycloak を docker run で起動して Auth0 のように差し込んで使うのは簡単だが、25 万マイル走った 1996 年式 Ford F-150 を買うようなものだ
    走るし動きもするが、保守は本当にひどい

    • 小規模ではあったが、オンプレミスの Docker Swarm でやってみたことがあり、実際かなりつらかった
      記憶が正しければ、デフォルトのディスカバリ方式はマルチキャストを使うが、一般的なクラウドネットワークや Swarm/Kubernetes のオーバーレイネットワークでは有効になっていない
      RDBMS を一種のクォーラムメカニズムとして使う database ping も見たが、かなり脆弱に見え、最終手段のように感じた
      最終的には、Swarm クラスタの DNS でノードを検出する Kubernetes クラスタドライバを使うことができた
      動くようにするまでかなり苦労したが、その後は知る限り安定している
      最近はネイティブの EC2 ネットワーキングドライバ もあるようだが、自分では調べていない
    • Quarkus に完全移行してから改善があったと聞いている
      ECS へのデプロイはいつ頃だったのか気になる
    • OAuth と OpenID Connect、そして realm にアプリとクライアントを追加できる点が Keycloak の救いであり、使い続けた唯一の理由だった
    • クラスタディスカバリが UDP だったというのはつらい話だ
      こちらでは UDP は DNS 用にしか許可されていない
  • Keycloak は素晴らしいが、初心者にはかなり分かりにくいことがある
    機能が多く、ドキュメントも最高とは言えない
    最近 Zitadel を使ってみたが、ずっと使いやすかった
    ただし、組み込みデータベースで動かせないので、セルフホストは少し難しい

    • そう、自前のデータベースが必要だ
      今後は Postgres をデータベースとして使う方向なので、ほかのツールと一緒にデプロイしやすくなることを期待している
  • 上司が最近 Keycloak のことを「贈り物を贈り続けてくる存在」と呼んでいたが、実際には、何かのやり方を突き止めるための Jira チケットが次々に増えるという意味だった
    それでも、EKS クラスタを作成し Keycloak をデプロイし、SAML/OIDC クライアントを作成し、外部 ID プロバイダを追加し、AWS IAM Identity Provider まで設定する Terraform は持っている
    何ができるのか、UI でどうやるのか、そして mrparkers Terraform provider でどう自動化するのかさえ分かれば、使うこと自体はとても簡単になる

    • その Terraform がどこかでオープンソース公開されているのか気になる
      友人が必要としていて :)
  • 何年も Keycloak を使った結果、正直いろいろな奇妙な挙動にうんざりして、代替を探し始めた
    Authentik など複数の候補も悪くなさそうだったが、Zitadel[1] が目に留まり、それ以来振り返っていない
    [1] https://zitadel.com/blog/zitadel-vs-keycloak

    • 気に入っているようで何より
      決め手になったのは何だったのか気になる