GeekFactory

int128.hatenablog.com

nextcloudとKeycloakのSAML SSO

TL;DR

以下の環境で確認しています。

  • nextcloud 12.0
  • Keycloak 3.4.0.Final

具体的な手順

Keycloakで新しいClientを追加します。認証連携したいユーザがいるRealmで作業してください。

  • クライアントID: https://nextcloud.example.com/index.php/apps/user_saml/saml/metadata
  • クライアントプロトコル: saml
  • クライアント署名が必須: OFF
  • 有効なリダイレクト URI: https://nextcloud.example.com/*
  • マッパー
    • SAML属性 username → User Property username
    • SAML属性 email → User Property email

nextcloudで「SSO & SAML authentication」を設定します。

  • Attribute to map the UID to: username
  • Identity Provider Data
    • Identifier of the IdP entity (must be a URI): https://keycloak.example.com/auth/realms/YOUR_REALM
    • URL Target of the IdP where the SP will send the Authentication Request Message: https://keycloak.example.com/auth/realms/YOUR_REALM/protocol/saml
    • Public X.509 certificate of the IdP: インストレーションでSAML Metadata IDPSSODescriptorを選択するとXMLが表示されるので、<dsig:X509Certificate> 要素に入っているBASE64文字列を入力します。-----BEGIN CERTIFICATE----------END CERTIFICATE----- で囲むのをお忘れなく。
  • Attribute mapping
    • Attribute to map the displayname to: username
    • Attribute to map the email address to: email

なお、SAML SSOを有効にするとローカルユーザでログインできなくなるため、ローカルユーザのウィンドウを開いたまま作業してください。シークレットウィンドウでnextcloudを開いて動作確認するとよいでしょう。

デフォルトでは、SSO後にユーザが存在しない場合は自動的に作成してくれます。

まとめ

KeycloakでID管理を統合すると便利なのでおすすめです。ついでに、バックエンドストレージをS3にしておくとメンテナンスも楽です。

Deploy static site on nginx pod

滅多に変更しない静的ページを配置したい時に便利です。

apiVersion: v1
kind: ConfigMap
metadata:
  name: landing-page
data:
  index.html: |
    <!DOCTYPE html>
    <html>
    <body>
      <h2>Welcome</h2>
    </body>
    </html>
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: landing-page
spec:
  rules:
  - host: www.example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: landing-page
          servicePort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: landing-page
spec:
  type: ClusterIP
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  selector:
    app: landing-page
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: landing-page
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: landing-page
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - name: http
          containerPort: 80
        volumeMounts:
        - name: html
          mountPath: /usr/share/nginx/html
        livenessProbe:
          httpGet:
            path: /
            port: http
          initialDelaySeconds: 2
          timeoutSeconds: 3
        readinessProbe:
          httpGet:
            path: /
            port: http
          initialDelaySeconds: 2
          timeoutSeconds: 3
          periodSeconds: 5
      volumes:
      - name: html
        configMap:
          name: landing-page
kubectl apply -f landing-page.yaml

KeycloakのOpenID ConnectでKubernetesにアクセスする

TL;DR

  • Kubernetesの認証はクライアント証明書やID/パスワードだけでなく、OpenID Connectに対応している。
  • RBACを設定することで、ユーザやグループによるアクセス制御ができる。
  • 以下の設定が必要になる。
    • Keycloak(OpenID Connect IdP)
    • kube-apiserver(Kubernetes APIサーバ)
    • kubectl(Kubernetesクライアント)

Getting Started

Keycloak

OpenID Connectで認証できるようにKeycloakを設定します。ここではKeycloakに以下のレルムとユーザが存在しており、ユーザはグループに入っているものとします。

  • レルム名: hello
  • ユーザ名: foo
  • グループ名: admin

まず、新しいクライアントを作成します。Settingsタブで下表の内容を設定します。

項目 設定値
Client ID kubernetes
Client Prorocol openid-connect
Access Type confidential
Valid Redirect URIs urn:ietf:wg:oauth:2.0:oob

Credentialsタブを開くとSecretが表示されるのでメモしておきます。後述するkubectlの設定で必要になります。

Mappersタブで新しいマッパーを作成します。ここではグループによるアクセス制御を行いたいので groups クレームを返すように下表の内容を設定します。

項目 設定値
Name groups
Mapper Type Group Membership
Token Claim Name groups

kube-apiserver

kube-apiserverに下表の引数を渡すように設定します。

引数名 設定値
--oidc-issuer-url https://keycloak.example.com/auth/realms/hello
--oidc-client-id kubernetes
--oidc-groups-claim groups

kopsの場合は kops edit cluster で以下を設定します。設定したら kops update clusterkops rolling-update cluster でmastersを再作成しておきます。

spec:
  kubeAPIServer:
    oidcClientID: kubernetes
    oidcGroupsClaim: groups
    oidcIssuerURL: https://keycloak.example.com/auth/realms/hello

kubectl

OpenID ConnectのIDトークンを使用してKubernetesにアクセスするようにkubectlを設定します。kubectlはログイン処理は行ってくれないため、あらかじめIDトークンを取得しておく必要があります。ここではcurlコマンドでIDトークンを取得します。 ブラウザで以下のURLにアクセスします。

https://keycloak.example.com/auth/realms/hello/protocol/openid-connect/auth
?client_id=kubernetes
&client_secret=YOUR_CLIENT_SECRET
&response_type=code
&scope=openid
&scope=email
&scope=profile
&redirect_uri=urn:ietf:wg:oauth:2.0:oob

以下のコマンドを実行します。YOUR_CODE はブラウザに表示されたコードに置き換えてください。

curl -d grant_type=authorization_code \
  -d client_id=kubernetes \
  -d client_secret=YOUR_CLIENT_SECRET \
  -d redirect_uri=urn:ietf:wg:oauth:2.0:oob \
  -d code=YOUR_CODE \
  https://keycloak.example.com/auth/realms/hello/protocol/openid-connect/token

以下のようなJSONが返ってきたら成功です。

{"access_token":"...",
 "expires_in":300,
 "refresh_expires_in":1800,
 "refresh_token":"...リフレッシュトークン...",
 "token_type":"bearer",
 "id_token":"...IDトークン...",
 "not-before-policy":0,
 "session_state":"...",
 "scope":"..."}

新しいコンテキストでIDトークン認証を設定します。

kubectl config set-credentials hello.k8s.local-oidc \
  --auth-provider oidc \
  --auth-provider-arg idp-issuer-url=https://keycloak.example.com/auth/realms/hello \
  --auth-provider-arg client-id=kubernetes \
  --auth-provider-arg client-secret=YOUR_CLIENT_SECRET \
  --auth-provider-arg id-token=IDトークン \
  --auth-provider-arg refresh-token=リフレッシュトークン

kubectl config set-context hello.k8s.local-oidc --cluster hello.k8s.local --user hello.k8s.local-oidc

IDトークン認証のコンテキストでkubectlを実行してみましょう。

% kubectl --context hello.k8s.local-oidc get po
Error from server (Forbidden): pods is forbidden: User "https://keycloak.example.com/auth/realms/hello#874c4a74-faf3-45a0-bcfe-9ddf4fb802ea" cannot list pods in the namespace "default"

現在のユーザにはまだ何も権限を付与していないためForbiddenエラーが返ってくるはずです。もしUnauthorizedエラーが返ってきた場合は認証に失敗しているので、設定を再確認してください。トラブルシューティングでは kubectl --v=10デバッグログを出力するようにすると便利です。

現在のユーザに管理者権限を付与します。

# rbac-oidc.yaml
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: keycloak-admin-group
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: User
  name: https://keycloak.example.com/auth/realms/hello#874c4a74-faf3-45a0-bcfe-9ddf4fb802ea
kubectl apply -f rbac-oidc.yaml

以下のようにPodsが表示されたら成功です。

% kubectl get po
NAME                        READY     STATUS    RESTARTS   AGE
echoserver-7f76cc58-w8v4d   1/1       Running   0          2d

グループ単位で管理者権限を付与することも可能です。以下のように subjectsGroup を指定します。

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: keycloak-admin-group
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: Group
  name: /admin

Keycloakが返すIDトークンの内容は https://jwt.io などでデコードして確認できます。ユーザ名やグループ名を調べる時に便利です。

まとめ

KeycloakがOpenID Connect IdPになり、IDトークンを使用してKubernetesにアクセスする方法を紹介しました。

現状、kubectlはOpenID Connectの認可コードフローに対応していないので、IDトークンの取得は自力で行う必要があります。コマンドラインツールの登場が待ち望まれます。