個人的に知識があいまいな認証回りの最小限の知見整理として、Traefik を用いて Keycloak で発行したアクセストークンが無いとアプリケーションにアクセスできないようにする最小限の設定を考えてみました。

ちなみに GPT-4o にフォローしてもらって大枠を整理し、動作に不具合が起きた部分を再度調べて…という流れで進めました。

【要注意】 この手順に従って構成した環境は本番利用するにはセキュリティ面で問題があるので、あくまで Keycloak や Traefik の設定ポイントの参考としてください。

検証した各ミドルウェアのバージョン

  • minikube: v1.33.1
  • Keycloak: 24.0.4
  • Traefik: 2.11.2
    • traefik-jwt-plugin: v0.7.1

前提条件

  1. Minikube がインストールされている
  2. kubectl がインストールされている
  3. Helm がインストールされている

ステップ 1: Minikube のセットアップ

まず、Minikube を起動します。

minikube start

ステップ 2: Keycloak のデプロイ

Keycloak を最低限の構成でデプロイするために、以下の Kubernetes マニフェストファイルを作成します。keycloak-deployment.yamlという名前で保存してください。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: keycloak
  labels:
    app: keycloak
spec:
  replicas: 1
  selector:
    matchLabels:
      app: keycloak
  template:
    metadata:
      labels:
        app: keycloak
    spec:
      containers:
        - name: keycloak
          image: quay.io/keycloak/keycloak:24.0.4
          args: ["start-dev"]
          env:
            - name: KEYCLOAK_ADMIN
              value: admin
            - name: KEYCLOAK_ADMIN_PASSWORD
              value: admin
            - name: KC_PROXY
              value: "edge"
          ports:
            - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: keycloak
spec:
  ports:
    - port: 8080
      targetPort: 8080
  selector:
    app: keycloak

次に、Keycloak のデプロイメントとサービスを作成します。

kubectl apply -f keycloak-deployment.yaml

これで、管理者ユーザー名が admin、パスワードが admin で構成された Keycloak の環境が立ち上がりました。

Keycloak には CLUSTER-IP 経由で接続できました。CLUSTER-IP の取得には kubectl get svc keycloak を使用します。

得られた CLUSTER-IP の値はメモして把握しておきます。

ステップ 3: Traefik のデプロイ

Helm を使用して traefik-jwt-plugin を使用できる Traefik v2.11 をインストールしていきます。

まずは下記で Helm のリポジトリの準備をします。

helm repo add traefik https://helm.traefik.io/traefik
helm repo update

続いて以下の内容で traefik-values.yaml というファイルを作成します。

additionalArguments:
  - "--log.level=DEBUG"
  - "--api.insecure=true" # ダッシュボードを有効にする
  - "--entrypoints.web.address=:80"
  - "--entrypoints.websecure.address=:443"
  - "--providers.kubernetescrd"
  - "--providers.kubernetesingress"
  - "--experimental.plugins.jwt.moduleName=github.com/traefik-plugins/traefik-jwt-plugin"
  - "--experimental.plugins.jwt.version=v0.7.1"
service:
  type: LoadBalancer
rbac:
  enabled: true
experimental:
  plugins:
    jwt:
      moduleName: github.com/traefik-plugins/traefik-jwt-plugin
      version: v0.7.1

次に、バージョン 27.0.2 のチャートを使用することで Traefik v2.11 をインストールします。

helm install traefik traefik/traefik --version 27.0.2 -f traefik-values.yaml

ステップ 4: Whoami アプリのデプロイ

アクセストークンが無いと使用できないアプリケーションのサンプルとして、Whoami アプリを利用します。

Whoami アプリをデプロイするために、以下の Kubernetes マニフェストファイルを作成します。whoami-deployment.yamlという名前で保存してください。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: whoami
  labels:
    app: whoami
spec:
  replicas: 1
  selector:
    matchLabels:
      app: whoami
  template:
    metadata:
      labels:
        app: whoami
    spec:
      containers:
        - name: whoami
          image: containous/whoami
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: whoami
spec:
  ports:
    - name: web
      port: 80
      targetPort: 80
  selector:
    app: whoami

次に、Whoami アプリのデプロイメントとサービスを作成します。

kubectl apply -f whoami-deployment.yaml

ステップ 5: Traefik の Ingress と Middleware の設定

Keycloak から発行されたトークンを検証するための Middleware と Whoami アプリへの Ingress を設定します。traefik-config.yamlという名前で以下の内容を保存してください。

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: jwt
spec:
  plugin:
    jwt:
      PayloadFields:
        - exp
      Keys:
        - http://<KeyCloakのCLUSTER-IPの値>:8080/realms/myrealm/protocol/openid-connect/certs
      Alg: RS256
      Required: true
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: whoami
  annotations:
    traefik.ingress.kubernetes.io/router.middlewares: default-jwt@kubernetescrd
spec:
  rules:
    - http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: whoami
                port:
                  number: 80

次に、この設定を適用します。

kubectl apply -f traefik-config.yaml

ステップ 6: Keycloak の設定

ブラウザで Keycloak の管理コンソールにアクセスします。URL は次の通りです。

http://<Keycloak の CLUSTER-IP の値>:8080

ここで、デフォルトの管理者アカウント(ユーザー名: admin, パスワード: admin)を使用してログインします。

レルムとクライアントの作成

※ Keycloak はデータの永続化をしていないので、ここからの作業は minikube を落とさないように進めましょう…

  1. 新しいレルムを作成します(myrealm)。
  2. myrealm にクライアントを追加します(whoami-client)。
    • General settings
      • Client type: OpenID Connect
      • Client ID: whoami-client
    • Capability config
      • Client authentication: On
    • Login settings
      • Valid redirect URIs: http://127.0.0.1:8080

ユーザーの作成

  1. myrealm にユーザーを追加します。
    • ユーザー名: testuser
    • パスワード: password

クライアントシークレットの把握

  1. whoami-client クライアントの設定画面を開き、Credentials タブで、Client Secret の値をコピーします。

動作確認

動作確認の準備

Traefik のアプリへのアクセスは、ポートフォワーディングで動作しました(その想定で上記を設定しています)。

kubectl port-forward deployment/traefik 8080:80

アクセストークンの取得

認可コードフローで取得してみます。

まずはブラウザ(シークレットウィンドウを利用)で下記にアクセスします。

http://<Keycloak の CLUSTER-IP>:8080/realms/myrealm/protocol/openid-connect/auth?response_type=code&client_id=whoami-client&redirect_uri=http://127.0.0.1:8080&scope=openid

ログイン画面が出るので、myrealm に追加しておいた下記のユーザーでログインします。

  • ユーザー名: testuser
  • パスワード: password

ログインすると authorization header missing とエラー画面が出ますが Traefik の設定によるもので正常です。

そのままブラウザは閉じずに・・・ブラウザの URL 部分に code= で認証コードが出力されるので、これをコピーします。

コピーした認証コードを用いて、下記の curl を実行します。

curl -X POST "http://<KeycloakのCLUSTER-IP>:8080/realms/myrealm/protocol/openid-connect/token" \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "code=<先ほど取得した認証コード>" \
    -d "redirect_uri=http://127.0.0.1:8080" \
    -d "grant_type=authorization_code" \
    -d "client_id=whoami-client" \
    -d "client_secret=<whoami-clientのクライアントシークレット>" | jq .

成功すると、Open ID Connect のトークンが得られ、access_token の値がアクセストークンになります。

アプリへのアクセス

アクセストークンが無いとエラーになります。

$ curl http://127.0.0.1:8080
authorization header missing

有効期限内のアクセストークンがあれば whoami アプリの応答を返します。

$ curl -H "Authorization: Bearer <有効なアクセストークン>" http://127.0.0.1:8080
Hostname: whoami-b577bd888-2hbtd
IP: 127.0.0.1
IP: ::1
IP: 10.244.0.4
IP: fe80::()
RemoteAddr: 10.244.0.10:(ポート番号)
GET / HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: curl/7.47.0
Accept: */*
Accept-Encoding: gzip
Authorization: Bearer <アクセストークン>
X-Forwarded-For: 127.0.0.1
X-Forwarded-Host: 127.0.0.1:8080
X-Forwarded-Port: 8080
X-Forwarded-Proto: http
X-Forwarded-Server: traefik-()
X-Real-Ip: 127.0.0.1

有効期限が切れたアクセストークンが渡されると期限切れのエラーになります。

$ curl -H "Authorization: Bearer <期限切れのアクセストークン>" http://127.0.0.1:8080
token is expired

終わりに

この記事では、Minikube 上に Keycloak と Traefik、Whoami アプリをデプロイし、Traefik で Whoami アプリへのアクセスに Keycloak 発行のアクセストークンを必須とする方法を示しました。

繰り返しになりますが、セキュリティのために本番環境では TLS の設定やその他のセキュリティ対策を講じることが必須です。