@int128

int128.hatenablog.com

kubeloginコマンドを利用してOpenID ConnectでKubernetesにアクセスする

TL;DR

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

以前に KeycloakのOpenID ConnectでKubernetesにアクセスする - GeekFactory という記事を書きましたが、本稿ではkubeloginコマンドを利用してOpenID Connectでログインする方法を紹介します。

Getting Started

1. Keycloak

Keycloakは Helm chart からインストールできます。

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

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

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

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

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

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

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

2. 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

kube-awsの場合は cluster.yaml で以下を設定します。設定したら kube-aws update でスタックを更新します。

       oidc:
         enabled: true
         issuerUrl: https://keycloak.example.com/auth/realms/hello
         clientId: kubernetes
         groupsClaim: groups

3. kubectl

OpenID Connectを利用してKubernetesにアクセスするようにkubectlを設定します。引数のうち、IDトークンとリフレッシュトークンはkubeloginコマンドが設定してくれるので指定しなくてOKです。

kubectl config set-credentials hello.k8s.local \
  --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

https://github.com/int128/kubelogin/releases からkubeloginコマンドを取得します。

kubeloginコマンドを実行すると以下のメッセージが表示されます。

% kubelogin
2018/03/23 18:01:40 Reading config from /home/user/.kube/config
2018/03/23 18:01:40 Using current context: hello.k8s.local
2018/03/23 18:01:40 Using issuer: https://keycloak.example.com/auth/realms/hello
2018/03/23 18:01:40 Using client ID: kubernetes
2018/03/23 18:01:41 Starting OpenID Connect authentication:

## Automatic (recommended)

Open the following URL in the web browser:

http://localhost:8000/

## Manual

If you cannot access to localhost, instead open the following URL:

https://keycloak.example.com/auth/realms/hello/protocol/openid-connect/auth?client_id=kubernetes&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&response_type=code&scope=openid+email&state=********

Enter the code:

ブラウザで http://localhost:8000/ を開くとKeycloakにリダイレクトされます。Keycloakで認証が成功するとlocalhostにリダイレクトで戻ります。コンソールには以下のメッセージが表示されているはずです。

2018/03/23 18:01:46 Exchanging code and token...
2018/03/23 18:01:46 Verifying ID token...
2018/03/23 18:01:46 You are logged in as foo@example.com (********)
2018/03/23 18:01:46 Updated /home/user/.kube/config

以上で ~/.kube/config のIDトークンとリフレッシュトークンが更新されます。

kubectlを実行してみましょう。

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

なお、あらかじめユーザやグループにロールを付与しておく必要があります。管理者権限を付与する例を以下に示します。

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: keycloak-admin-role-binding
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
# グループにロールを付与する場合
- kind: Group
  name: /admin

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

まとめ

kubeloginコマンドを利用してIDトークンを取得し、kubectlコマンドでKubernetesにアクセスする方法を紹介しました。