KeycloakをDockerにデプロイ:アプリの集中IAM・SSOシステムを構築する

Security tutorial - IT technology blog
Security tutorial - IT technology blog

チームを壊す「認証の混乱」

最初はアプリが1つだけ。ユーザーが登録してログインすれば完了。ところが2つ目のサービスが立ち上がる——別の管理ダッシュボード、内部API、顧客向けポータル。それぞれが独自のユーザーテーブル、独自のログインフォーム、独自のパスワードリセットフローを持つことになる。

6ヶ月後、サポート受信箱には「Xに使ったパスワードを忘れた」というメッセージが溢れる。開発者はすべてのサービスで認証コードを重複して書く。セキュリティ監査で3つのアプリがMD5でパスワードを保存していることが発覚する。あるチームはJWTを使い、別のチームはセッションを使い、また別のチームは……トークンをまったく失効させない。

これは規律の問題ではない。アーキテクチャの問題だ。

根本原因:サービス全体に散らばった認証ロジック

各サービスが独自の認証を持つと、次のような問題が生じる:

  • 同期がずれていく複数のユーザーデータベース
  • パスワードポリシーやMFAを一元的に強制できる場所がない
  • ユーザーが各アプリに個別にログインしなければならない
  • 誰がいつ何にアクセスしたかの監査証跡がない
  • 失効処理が面倒——1つのアカウントを無効化するにはすべてのシステムを操作する必要がある

認証はインフラだ。各マイクロサービスに専用のデータベースサーバーを与えたりはしない。それぞれに独自の認証スタックを持たせるのは同じ過ちであり、問題が起きるまで気づきにくいだけだ。

3つの選択肢、明確な勝者は1つ

ほとんどのチームはこれらのアプローチから選ぶことになる。それぞれの正直なトレードオフを示す:

選択肢1:自前のSSOを作る

社内で共有認証サービスを構築すれば完全な制御が得られる。ただし数ヶ月かかる。OAuth 2.0とOpenID Connectにはトークンイントロスペクション、リフレッシュフロー、PKCEなど数十のエッジケースがある——そして作ったものを永遠に誰かがメンテナンスしなければならない。これを試みたほとんどのチームは動くv1を出荷した後、静かに改善をやめる。

選択肢2:Auth as a Service(Auth0、Okta、Cognito)

クラウドホスト型プロバイダーはMFA、ソーシャルログイン、SAML、SCIMプロビジョニングをすべて箱から出してすぐ使える形で提供する。ただしトレードオフも現実的だ。Auth0は10,000 MAUで月1,000ドル以上になることがある。Cognitoはカスタムトークンクレームに難がある。ユーザーデータは他社のサーバーに置かれる。規制された業界やプライバシーに敏感なチームにとって、最後の点だけで採用を断念する理由になる。

選択肢3:セルフホスト型Keycloak

KeycloakはRed Hatのオープンソースアイデンティティプラットフォームだ。OAuth 2.0、OpenID Connect、SAML 2.0、ソーシャルログイン、MFA、ユーザーフェデレーション(LDAP/Active Directory)、きめ細かい認可——すべて含まれており、ライセンス費用はゼロ。自社インフラで動かす。データは外に出ない。

自前のサーバーをすでに管理しているチームにとって、DockerでのKeycloakが最も実用的な選択肢だ。セットアップコストは一度だけ払えばいい。

KeycloakをDockerにデプロイする

以下の手順で、PostgreSQLをバックエンドとしたDocker Composeによる本番対応のKeycloakインスタンスを起動できる。

1. Docker Composeファイルを作成する

mkdir keycloak && cd keycloak
nano docker-compose.yml
version: '3.8'

services:
  postgres:
    image: postgres:16
    container_name: keycloak_db
    restart: unless-stopped
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - keycloak_net

  keycloak:
    image: quay.io/keycloak/keycloak:24.0
    container_name: keycloak
    restart: unless-stopped
    command: start
    environment:
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: ${DB_PASSWORD}
      KC_HOSTNAME: auth.yourdomain.com
      KC_PROXY_HEADERS: xforwarded
      KC_HTTP_ENABLED: "true"
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: ${ADMIN_PASSWORD}
    ports:
      - "8080:8080"
    depends_on:
      - postgres
    networks:
      - keycloak_net

volumes:
  postgres_data:

networks:
  keycloak_net:

KC_PROXY_HEADERS: xforwarded について補足しておく:これはKeycloak 20以前の非推奨オプション KC_PROXY: edge を置き換えるものだ。KC_HTTP_ENABLED: "true" と組み合わせることで、リバースプロキシからの X-Forwarded-* ヘッダーを信頼するようKeycloakに伝え、実際のクライアントIPとプロトコルを正しく把握できるようになる。

2. 環境変数を設定する

Composeファイルにパスワードをハードコードしてはいけない。同じディレクトリに .env ファイルを作成する:

nano .env
DB_PASSWORD=your_strong_db_password_here
ADMIN_PASSWORD=your_strong_admin_password_here

パスワードの生成には toolcraft.app/ja/tools/security/password-generator のジェネレーターを使っている——ブラウザ上で完全に動作し、サーバーへの通信は一切ない。アイデンティティインフラ向けの認証情報を生成する際は、この点が重要になる。強力なパスワードの条件と強度の確認方法についても理解しておくと、インフラ全体の認証情報管理がより確実になる。

3. Keycloakを起動する

docker compose up -d
docker compose logs -f keycloak

確認すべきログ:Keycloak 24.0 on JVM (powered by Quarkus) started。初回起動は30〜60秒かかる——Keycloakは起動時にデータベースマイグレーションを実行するためだ。

4. 最初のレルムを作成する

レルムは独立した名前空間だ。あるレルムのユーザー、クライアント、ロールは別のレルムからは見えない。テナント境界、あるいは組織レベルのコンテナと考えるとわかりやすい。

  1. http://localhost:8080(またはあなたのドメイン)を開く
  2. 管理者認証情報でログインする
  3. 左上の master にカーソルを合わせ → レルムの作成 をクリック
  4. myapp のような名前を付けて → 作成 をクリック

実際のアプリケーションは master レルムに置かないこと。masterはKeycloak管理専用として予約されている。

5. アプリケーションをクライアントとして登録する

Keycloakに認証を委譲するアプリケーションはクライアントと呼ばれる。

  1. レルム内で クライアントクライアントの作成 に進む
  2. クライアントタイプOpenID Connect に設定する
  3. クライアントID をアプリ名(例:webapp)に設定する
  4. バックエンドサービス(コンフィデンシャルクライアント)の場合は クライアント認証 を有効にする
  5. 有効なリダイレクトURI を設定する:例 https://yourapp.com/*
  6. 保存 → クレデンシャル タブを開いてクライアントシークレットをコピーする

6. テストユーザーを作成する

# 管理UIからも作成可能:ユーザー → ユーザーの追加
docker exec -it keycloak /opt/keycloak/bin/kcadm.sh \
  create users \
  -r myapp \
  -s username=testuser \
  -s enabled=true \
  --server http://localhost:8080 \
  --realm master \
  --user admin \
  --password ${ADMIN_PASSWORD}

次にパスワードを設定する:ユーザー → testuser → クレデンシャル → パスワードの設定

7. 認証フローをテストする

KeycloakはOIDCディスカバリードキュメントを標準公開している——準拠しているライブラリはすべてこれを自動的に読み込む:

curl https://auth.yourdomain.com/realms/myapp/.well-known/openid-configuration

トークンを直接リクエストする場合(APIテストに便利):

curl -X POST \
  https://auth.yourdomain.com/realms/myapp/protocol/openid-connect/token \
  -d 'grant_type=password' \
  -d 'client_id=webapp' \
  -d 'client_secret=YOUR_CLIENT_SECRET' \
  -d 'username=testuser' \
  -d 'password=testpassword'

レスポンスには access_token(署名済みJWT)、refresh_token、有効期限のタイムスタンプが含まれる。バックエンドサービスはKeycloakの公開鍵に対してアクセストークンを検証する——データベース参照は不要で、リクエストのたびにKeycloakへのラウンドトリップも発生しない。

NginxをKeycloakの前段に置く

Composeファイルには KC_PROXY_HEADERS: xforwarded が設定されており、Keycloakがリバースプロキシの背後にあることを伝えている。Nginxの設定は以下のようになる:

server {
    listen 443 ssl;
    server_name auth.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/auth.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/auth.yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

MFAを有効にする

ここで集中管理の真価が発揮される。レルム内のすべてのアプリに多要素認証(MFA)を追加するのに約30秒しかかからない:

  1. レルムに移動 → 認証ポリシー
  2. OTPポリシー でTOTPアルゴリズムとウィンドウを設定する
  3. フローブラウザ フローを編集し、OTPを必須にする

以上だ。このレルムに接続されたすべてのアプリケーションが、ログイン時にMFAを強制するようになる。アプリケーションコードの変更はゼロ。

日常業務で実際に変わること

これが稼働すると、具体的にこう変わる:

  • シングルサインオン:一度ログインすれば、レルム内のすべてのアプリにアクセスできる——サービス間で再認証は不要
  • 即時失効:Keycloakでアカウントを無効化すると、そのユーザーはすべての接続アプリへのアクセスを即座に失う——「eventually」ではなく即時に
  • 一貫したセキュリティポリシー:パスワードの複雑さ、MFA要件、セッションタイムアウト——すべて一度設定すれば、どこでも強制される
  • 監査証跡:すべてのログイン、ログアウト、トークン交換が1か所にログされる
  • ソーシャルログイン:アプリケーションコードに触れずに、任意のアプリにGoogleやGitHubログインを追加できる

セットアップは多くても半日の作業だ。見返りはすぐに得られる——サポートチケットの減少、サービス間で重複した認証ロジックの解消、実際に監査できるセキュリティポリシー。同じユーザーベースを共有する2つ以上のアプリケーションを運用しているなら、集中型IAMはもはや選択肢ではない。インフラがあるべき姿に過ぎない。

Share: