- 単一のLinux VM上でKeycloakをrootless Dockerで分離実行し、システムのnginxがTLS終端とリバースプロキシを担うSSOデプロイ手順
- 前提条件は、SSHアクセス、Keycloak用のドメインまたはサブドメイン、
Aレコード、パスワードなしのkeycloakユーザー、/srv/keycloakホームディレクトリ、Docker rootlessのインストール
docker-compose.ymlはpostgres:16とquay.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.0、SAMLなど主要なSSOプロトコルと相互運用できる
- 構成はKeycloak
23.0.6を基準に作成され、25.0.5までテスト済み
- 全体構成はnginxを中央のリバースプロキシに置き、サービスごとのrootless Docker名前空間へlocalhost経由でトラフィックを渡す方式
- Webトラフィックは
0.0.0.0:80と0.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/subgidにkeycloakユーザーの範囲を追加
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}構文は、.envにKC_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.ymlのKC_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の追加とテーマ設定を進められる
1件のコメント
Hacker Newsのコメント
最近ホームラボに認証を導入するにあたって Authelia を選んだ
Keycloak も動くが大きすぎるし、traefik forward auth と一緒に使うには追加サービスがさらに必要になる
Authelia にはユーザー編集 UI がなく、バックエンドの LDAP サーバーとの双方向同期でもないが、静的ファイルと環境変数だけで設定できるので、多くのケースによく合う
いくつかのサービスに認証を追加し、対応可能なサービスには SSO を付ける程度なら、Authelia から始めてみる価値がある
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
Caddy をリバースプロキシとして使い、Authelia と統合[1]してうまく動いている
すべてのサービスに堅牢な 2 要素認証を導入しており、セルフホストのアプリに VPN なしでアクセスしても十分安全だと感じている
ただし、Authelia は 1 年以上新しいリリースがないので、セキュリティ面が心配だ
[0] https://github.com/lldap/lldap
[1] https://www.authelia.com/integration/proxies/caddy/
[1] https://github.com/greenpau/caddy-security
ホームラボ環境では非常に良いユースケースであり、より大規模なソリューションの機能が不要、または望まないなら特によく合う
探してみても単純なスキーマ、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 も追加したい。
個人的には、これがいちばんシンプルな選択肢だと思う。
https://github.com/lastlogin-io/obligator
セルフホスト可能な複数の OpenID Connect サーバー の未完成な比較表を作った[0]。
驚いた点の 1 つは、Keycloak のコードベースが本当に巨大だということだ。
[0]: https://github.com/lastlogin-io/obligator?tab=readme-ov-file...
Keycloak がセキュリティ問題を解決してくれるという考えは滑稽だ。
CVE リスト を見れば意味が分かる。
報告された CVE がないクローズドで不透明なソリューションも、「安全だ」と言うには滑稽だ。
また、最新リリースの 22.0.2 には既知の脆弱性が 3 件しかない。
https://www.cvedetails.com/vulnerability-list/vendor_id-25/p...
少なくともその記事で 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/
常時 10 個以上のサービスを Docker と Kubernetes 上で保護しているが、各サービスごとに保護設定を独立して行うのが好きでないなら、authentik では苦労することになる
authentik には、複数のサブドメイン(app1.example.com、app2.example.com など)を 1 つの設定で保護している場合、セッション失効後に再認証すると、ユーザーがランダムに別のサービスへリダイレクトされる重大なバグ[0]がある
[0]: https://github.com/goauthentik/authentik/issues/6886
互換性を壊す変更なしには修正できず、cc グラントを使う多くのツールと動作しない
これを見て、候補から完全に外した
https://github.com/goauthentik/authentik/issues/6139
Angular の設定で手助けできることがあれば、GitHub Discussions を通じて喜んで協力します
Authentik のほうが良さそうに見えたが、別の返信で触れられていたバグはかなり厳しそうだ
Keycloak の問題は、古いプロジェクトなので変化が非常に多かったことにある
JBOSS プロジェクトとして始まり、IdP としての有用性はオンプレミスのクラスタ認証で真価を発揮するが、それ以上ではないと思う
Fortune 500 のある部門で AWS ECS 上に Keycloak を大規模導入したが、きちんとクラスタリングさせるまでの過程は千年戦争のようだった
DNS ディスカバリはまともに動かず、クラスタディスカバリは UDP ベースだったためクラウド環境では機能しなかった
あるサーバー上の状態を持つログイン情報が別のサーバーには存在しないため、単純な負荷分散は不可能で、選択肢は スティッキーセッション だけだった
Keycloak を
docker runで起動して Auth0 のように差し込んで使うのは簡単だが、25 万マイル走った 1996 年式 Ford F-150 を買うようなものだ走るし動きもするが、保守は本当にひどい
記憶が正しければ、デフォルトのディスカバリ方式はマルチキャストを使うが、一般的なクラウドネットワークや Swarm/Kubernetes のオーバーレイネットワークでは有効になっていない
RDBMS を一種のクォーラムメカニズムとして使う database ping も見たが、かなり脆弱に見え、最終手段のように感じた
最終的には、Swarm クラスタの DNS でノードを検出する Kubernetes クラスタドライバを使うことができた
動くようにするまでかなり苦労したが、その後は知る限り安定している
最近はネイティブの EC2 ネットワーキングドライバ もあるようだが、自分では調べていない
ECS へのデプロイはいつ頃だったのか気になる
こちらでは UDP は DNS 用にしか許可されていない
Keycloak は素晴らしいが、初心者にはかなり分かりにくいことがある
機能が多く、ドキュメントも最高とは言えない
最近 Zitadel を使ってみたが、ずっと使いやすかった
ただし、組み込みデータベースで動かせないので、セルフホストは少し難しい
今後は Postgres をデータベースとして使う方向なので、ほかのツールと一緒にデプロイしやすくなることを期待している
上司が最近 Keycloak のことを「贈り物を贈り続けてくる存在」と呼んでいたが、実際には、何かのやり方を突き止めるための Jira チケットが次々に増えるという意味だった
それでも、EKS クラスタを作成し Keycloak をデプロイし、SAML/OIDC クライアントを作成し、外部 ID プロバイダを追加し、AWS IAM Identity Provider まで設定する Terraform は持っている
何ができるのか、UI でどうやるのか、そして mrparkers Terraform provider でどう自動化するのかさえ分かれば、使うこと自体はとても簡単になる
友人が必要としていて :)
何年も Keycloak を使った結果、正直いろいろな奇妙な挙動にうんざりして、代替を探し始めた
Authentik など複数の候補も悪くなさそうだったが、Zitadel[1] が目に留まり、それ以来振り返っていない
[1] https://zitadel.com/blog/zitadel-vs-keycloak
決め手になったのは何だったのか気になる