Kubernetes API で OIDC 認証を使う
kubernetes で API を使用する際の認証には 様々な認証方法 がありますが、その 1 つに OIDC 認証 があります。OIDC 認証では外部の OIDC provider のユーザーやグループの認証情報を k8s クラスタ内の role にマッピングして kubectl や api などを実行できます。
セットアップ
今回は k8s クラスタとは別の環境に OIDC provider を構築して OIDC 認証を実現します。OIDC provider としては Authelia を使用し、認証に使用するユーザー、グループは LLDAP 側に登録した情報を参照する構成にします。Authelia, LLDAP は以前記事を書いたので詳細は以下を参照。
概要図としては次のようになります。
概要図
上記の構成は一例なので、例えば authelia 等を k8s クラスタ内に構築したり kubectl
を実行するホストをクラスタ内のノードとするような構成でも ok です。
Authelia と LLDAP の構築
Authelia と LLDAP を docker で構築するための docker-compose.yml
を以下のように準備。
---
services:
authelia:
image: authelia/authelia
container_name: authelia
restart: always
volumes:
- ./authelia:/config
- ./certs:/certs
ports:
- 9091:9091
environment:
- TZ=Asia/Tokyo
lldap:
image: lldap/lldap:stable
container_name: lldap
restart: always
ports:
- "3890:3890"
- "6360:6360"
- "17170:17170"
environment:
- LLDAP_VERBOSE=true
- LLDAP_JWT_SECRET=REPLACE_WITH_RANDOM
- LLDAP_KEY_SEED=REPLACE_WITH_RANDOM
- LLDAP_LDAP_BASE_DN=dc=ldap,dc=centre,dc=com
ディレクトリ構成
├── authelia
│ ├── configuration.yml
│ └── notification.txt
├── certs
│ ├── authelia.ops.com.crt
│ └── authelia.ops.com.key
├── docker-compose.yml
authelia の設定ファイル configuration.yml
は 認証・認可サーバの OSS Authelia を試す と同様ですが、今回の構成に合わせて以下の部分を更新します。
authentication_backend.ldap
はローカルの lldap
コンテナと接続するため以下のように設定。
authentication_backend:
refresh_interval: 1m
ldap:
implementation: custom
address: ldap://lldap:3890
timeout: 5s
start_tls: false
base_dn: "dc=ldap,dc=centre,dc=com"
users_filter: "(&({username_attribute}={input})(objectClass=person))"
additional_users_dn: "ou=people"
additional_groups_dn: "ou=groups"
attributes:
username: "uid"
display_name: "displayName"
mail: "mail"
group_name: "cn"
member_of: "memberOf"
distinguished_name: "distinguishedName"
groups_filter: "(member={dn})"
user: "uid=admin,ou=people,dc=ldap,dc=centre,dc=com"
password: "password"
kubernetes 接続用の OIDC client を設定
- client_id: 任意の値
- client_name: 任意の値
- client_secret: 任意の値を hash 化した値
docker run --rm authelia/authelia:latest authelia crypto hash generate pbkdf2 --variant sha512 --password kubernetes
Digest: $pbkdf2-sha512$310000$Q0NxDoHL9ciMU/uXTSR1vw$EFjVf2JNydCRLjWGGpA0VSwh7//ySsf05Q.T6SywcTZQhfoecc4NoQ8A5uB6BO49ES.pXBTdS/zY3FYTZG9kVw
- redirect_uri: k8s kube-apiserver の callback の URL を指定する。この構成では control plane 用のノード ip address が
192.168.3.131
なのでそれを設定。
また、後述の kubelogin
ではデフォルトで localhost:8000 で server を起動するため、localhost も追加する必要がある(ないと The 'redirect_uri' parameter does not match any of the OAuth 2.0 Client's pre-registered 'redirect_uris'."}
のエラーになる)。
clients:
- client_id: "kubernetes"
client_name: "kubernetes"
client_secret: "$pbkdf2-sha512$310000$Ye0y8UZeHcDKi3.oJ8BFAg$VwYhrAXz7JZesQ6j5TH/bCvxqrmnjxonwmdUA5QHjQdOoe7Cstfrnvg4..AeJve9qRnCQp5wWhGgucTpkEYgsg"
public: false
authorization_policy: "one_factor"
redirect_uris:
- https://192.168.3.131:6443/api/v1/auth/callback
- http://localhost:8000
scopes:
- "openid"
- "profile"
- "groups"
- "email"
require_pkce: true
pkce_challenge_method: "S256"
userinfo_signed_response_alg: "none"
token_endpoint_auth_method: "client_secret_basic"
上記を設定した configuration.yml
の全文は以下。
configuration.yml
authentication_backend:
refresh_interval: 1m
ldap:
implementation: custom
address: ldap://lldap:3890
timeout: 5s
start_tls: false
base_dn: "dc=ldap,dc=centre,dc=com"
users_filter: "(&({username_attribute}={input})(objectClass=person))"
additional_users_dn: "ou=people"
additional_groups_dn: "ou=groups"
attributes:
username: "uid"
display_name: "displayName"
mail: "mail"
group_name: "cn"
member_of: "memberOf"
distinguished_name: "distinguishedName"
groups_filter: "(member={dn})"
user: "uid=admin,ou=people,dc=ldap,dc=centre,dc=com"
password: "password"
access_control:
default_policy: "deny"
rules:
- domain: "authelia.ops.com"
policy: "one_factor"
session:
secret: "insecure_session_secret"
cookies:
- name: "authelia_session"
domain: "ops.com" # Should match whatever your root protected domain is
authelia_url: "https://authelia.ops.com:9091"
expiration: "1 hour" # 1 hour
inactivity: "5 minutes" # 5 minutes
regulation:
max_retries: 3
find_time: "2 minutes"
ban_time: "5 minutes"
storage:
encryption_key: "you_must_generate_a_random_string_of_more_than_twenty_chars_and_configure_this"
local:
path: "/config/db.sqlite3"
notifier:
filesystem:
filename: "/config/notification.txt"
identity_providers:
oidc:
jwks:
- key_id: "authelia"
algorithm: "RS256"
use: "sig"
key: |
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
clients:
- client_id: "kubernetes"
client_name: "kubernetes"
client_secret: "$pbkdf2-sha512$310000$Ye0y8UZeHcDKi3.oJ8BFAg$VwYhrAXz7JZesQ6j5TH/bCvxqrmnjxonwmdUA5QHjQdOoe7Cstfrnvg4..AeJve9qRnCQp5wWhGgucTpkEYgsg"
public: false
authorization_policy: "one_factor"
redirect_uris:
- https://192.168.3.131:6443/api/v1/auth/callback
- http://localhost:8000
scopes:
- "openid"
- "profile"
- "groups"
- "email"
require_pkce: true
pkce_challenge_method: "S256"
userinfo_signed_response_alg: "none"
token_endpoint_auth_method: "client_secret_basic"
LLDAP では OIDC 認証に使用する以下のユーザー・グループを作成しておきます
group | user | |
---|---|---|
developer | dev-user | dev-user@test.com |
kubelogin のインストール
kubelogin は kubectl 用の OIDC 認証の plugin で、kubectl 実行時に OIDC provider へのログインや id_token の取得などを行います。
いくつかインストール方法がありますが、ここでは krew でインストールします。
kubectl krew install oidc-login
kubelogin を使った認証の実行
セットアップが完了したので実際に OIDC 認証を試します。
ユーザーを role にマッピングする
kubelogin のセットアップ方法と使い方は docs/setup.md に記載があるので、まずはこちらに沿って OIDC 側のユーザーを k8s クラスタ内の role にマッピングして API を実行する方法を試します。
kubelogin セットアップのために kubectl で以下を実行。実行するマシンは kubectl が入っていればクラスタ内のノードでもクラスタ外のマシンのどちらでも良いです。
$ kubectl oidc-login setup \
--oidc-issuer-url=https://authelia.ops.com:9091 \
--oidc-client-id=kubernetes \
--oidc-client-secret=kubernetes
実行するとブラウザが開いて Authelia のログイン画面になるので事前に作成した dev-user
でログインします。ログインに成功すると同意リクエストができるので同意するを選択
同意すると localhost:8000
の kubelogin の画面で以下が表示されるので一旦ブラウザは閉じて ok
Authenticated
You have logged in to the cluster. You can close this window.
コマンドの方ではその後に実行する手順が表示されます。
出力
$ kubectl oidc-login setup \
--oidc-issuer-url=https://authelia.ops.com:9091 \
--oidc-client-id=kubernetes \
--oidc-client-secret=kubernetes
authentication in progress...
Opening in existing browser session.
## 2. Verify authentication
You got a token with the following claims:
{
"amr": [
"pwd"
],
"at_hash": "yZE0axs8-n-8wr8igbOSyQ",
"aud": [
"kubernetes"
],
"auth_time": 1725626733,
"azp": "kubernetes",
"client_id": "kubernetes",
"exp": 1725630569,
"iat": 1725626969,
"iss": "https://authelia.ops.com:9091",
"jti": "d0da435c-d96e-4a3a-adef-59caed7ae525",
"nonce": "7dwLQx1LDeJ3ThEqyA4xrG8e48RYjF5UpnqKyvu4nL4",
"sub": "6b2acb37-d3d7-4333-91cf-a9f7c503ee05"
}
## 3. Bind a cluster role
Run the following command:
kubectl create clusterrolebinding oidc-cluster-admin --clusterrole=cluster-admin --user='https://authelia.ops.com:9091#6b2acb37-d3d7-4333-91cf-a9f7c503ee05'
## 4. Set up the Kubernetes API server
Add the following options to the kube-apiserver:
--oidc-issuer-url=https://authelia.ops.com:9091
--oidc-client-id=kubernetes
## 5. Set up the kubeconfig
Run the following command:
kubectl config set-credentials oidc \
--exec-api-version=client.authentication.k8s.io/v1beta1 \
--exec-command=kubectl \
--exec-arg=oidc-login \
--exec-arg=get-token \
--exec-arg=--oidc-issuer-url=https://authelia.ops.com:9091 \
--exec-arg=--oidc-client-id=kubernetes \
--exec-arg=--oidc-client-secret=kubernetes
## 6. Verify cluster access
Make sure you can access the Kubernetes cluster.
kubectl --user=oidc get nodes
You can switch the default context to oidc.
kubectl config set-context --current --user=oidc
You can share the kubeconfig to your team members for on-boarding.
手順 2 は OIDC provider (authelia) から取得した id token の中身が書いてあるので 手順 3 の k8s クラスタ側で OIDC ユーザーにマッピングする role の作成から進めます。上記の手順では既存の cluster-admin
ロールを OIDC ユーザー https://authelia.ops.com:9091#6b2acb37-d3d7-4333-91cf-a9f7c503ee05
に関連付けるコマンドになっていますが、任意の role を作成して関連付けることもできます。
ここでは pod, service の権限のみ付加した role を作成して結びつけます。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: oidc-authelia-developer
rules:
- apiGroups: [""]
resources: ["pods", "services"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: oidc-authelia-developer
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: https://authelia.ops.com:9091#6b2acb37-d3d7-4333-91cf-a9f7c503ee05
roleRef:
kind: ClusterRole
name: oidc-authelia-reader
apiGroup: rbac.authorization.k8s.io
kubectl apply -f role.yml
次に手順 4 で k8s クラスタ内の control plane node 上の /etc/kubernetes/manifests/kube-apiserver.yaml
に以下を追加します。
- oidc-issuer-url: Authelia にアクセスする際の URL
- oidc-client-id: Authelia 側で設定した client id
spec:
containers:
- command:
...
- --oidc-issuer-url=https://authelia.ops.com:9091
- --oidc-client-id=kubernetes
kube-apiserver は static pod なのでマニフェストを変更して保存したら自動で kube-apiserver
pod が再起動して変更が反映されます。
手順5 では kubelogin
を実行したマシンに戻って表示されているコマンドを実行します。
$ kubectl config set-credentials oidc \
--exec-api-version=client.authentication.k8s.io/v1beta1 \
--exec-command=kubectl \
--exec-arg=oidc-login \
--exec-arg=get-token \
--exec-arg=--oidc-issuer-url=https://authelia.ops.com:9091 \
--exec-arg=--oidc-client-id=kubernetes \
--exec-arg=--oidc-client-secret=kubernetes \
これにより ~/.kube/config
に user が追加されます。
以上でクラスタに OIDC で認証するためのセットアップが完了したので、実際にユーザーで認証してみます。context を先ほど作成した oidc に変更
kubectl config set-context --current --user=oidc
この状態で kubectl get pod
等のコマンドでクラスタ内のリソースを確認しようとするとブラウザが開き、oidc ユーザーのログインが要求されます。セットアップ時のセッションが残っていればそのまま先程と同様に権限のリクエストが要求されるので同意を選択。
認証に成功すると oidc ユーザーとして各種 kubernetes api が実行できるようになります。
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
coredns-67d68df75b-ghhq5 1/1 Running 0 26h
coredns-67d68df75b-kwl64 1/1 Running 0 26h
etcd-k8s-m1 1/1 Running 7 (5d6h ago) 47d
kube-apiserver-k8s-m1 1/1 Running 0 19h
kube-controller-manager-k8s-m1 1/1 Running 179 (10m ago) 47d
kube-proxy-pwsvp 1/1 Running 5 (5d6h ago) 47d
kube-proxy-vgnzw 1/1 Running 6 (5d6h ago) 47d
kube-scheduler-k8s-m1 1/1 Running 127 (19h ago) 47d
snapshot-controller-0 1/1 Running 1 (5d6h ago) 13d
ユーザーは作成した role に結びついているので、role で許可されていない操作は拒否されます。
$ kubectl get deployments.apps
Error from server (Forbidden): deployments.apps is forbidden: User "dev-user@test.com" cannot list resource "deployments" in API group "apps" in the namespace "kube-system"
ログインした際の認証情報は kubectl を実行したホストの ~/.kube/cache/oidc-login
以下にキャッシュされます。
これを削除すると再度コマンド実行に OIDC ユーザーをログインが要求されるようになります。
$ cat ~/.kube/cache/oidc-login/9eb050114a87cc3298262bbee8b9c9b78362393d6e1461c307f73a70f70b8f98
{"id_token":"eyJhbGciOiJSUzI1NiIsImtpZCI6ImF1dGhlbGlhIiwidHlwIjoiSldUIn0.eyJhbXIiOlsicHdkIl0sImF0X2hhc2giOiI0ZXV2cGlLUVJCbFpUdTZvSl9nc3FBIiwiYXVkIjpbImt1YmVybmV0ZXMiXSwiYXV0aF90aW1lIjoxNzI1NjI4NjI5LCJhenAiOiJrdWJlcm5ldGVzIiwiY2xpZW50X2lkIjoia3ViZXJuZXRlcyIsImVtYWlsIjoiZGV2LXVzZXJAdGVzdC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiZXhwIjoxNzI1NjMyMjY2LCJncm91cHMiOlsiZGV2ZWxvcGVyIl0sImlhdCI6MTcyNTYyODY2NiwiaXNzIjoiaHR0cHM6Ly9hdXRoZWxpYS5vcHMuY29tOjkwOTEiLCJqdGkiOiI5NDZhZDk5Ni1lNzVlLTQyYTktOTNkOS0yZDUzZDdhMTVkYjgiLCJub25jZSI6IlUtVjRkTFY4anl0OGVGSFJVOUxGUlVNc3hYcEVIei1wcjEzM2o3bzhjZHMiLCJzdWIiOiI2YjJhY2IzNy1kM2Q3LTQzMzMtOTFjZi1hOWY3YzUwM2VlMDUifQ.UAaCgkuJGf8krxmRy2MeumN9SnpyTYGaq_ORRDOyn29NPfIwYON-D6Mb7UlVhJQihjDaUgTqTmHTzPj_kM62hBgKFS6M_tvRzJuc2ciwW87sst2gbnPNILReGo0P5t8OPK0YDw-WrK3dkjr4Jj8KOyRO2_n6UWJGQwnKP0aPkHETLSlsdlouilKRC7ubKwSzxtGtaPP15JgNX2Ex1D2KZJRV0hj-PtRxg9TAW6q7uwVny6GzT3EBNMMxkAcL_xWf-_EFWukPcAOPCl4uwycpa27X1SxHQKzAgru4W9Jvk5llvOLAV8-ECaf-K-UslpRttjbRTxTPK01zuhICQh5EIQ"}
email で認証する
上記の手順 3 で user に設定されている際の文字列 6b2acb37-d3d7-4333-91cf-a9f7c503ee05
は id token 内の sub
に入っている文字列に対応しています。
## 3. Bind a cluster role
Run the following command:
kubectl create clusterrolebinding oidc-cluster-admin --clusterrole=cluster-admin --user='https://authelia.ops.com:9091#6b2acb37-d3d7-4333-91cf-a9f7c503ee05'
{
"amr": [
"pwd"
],
"at_hash": "yZE0axs8-n-8wr8igbOSyQ",
"aud": [
"kubernetes"
],
"auth_time": 1725626733,
"azp": "kubernetes",
"client_id": "kubernetes",
"exp": 1725630569,
"iat": 1725626969,
"iss": "https://authelia.ops.com:9091",
"jti": "d0da435c-d96e-4a3a-adef-59caed7ae525",
"nonce": "7dwLQx1LDeJ3ThEqyA4xrG8e48RYjF5UpnqKyvu4nL4",
"sub": "6b2acb37-d3d7-4333-91cf-a9f7c503ee05"
}
kube-apiserver の OIDC 認証のデフォルト設定で username として token 内の sub
フィールドの値を読み取るように設定されていることが原因です(たぶん)。
--oidc-username-claim JWT claim to use as the user name. By default sub, which is expected to be a unique identifier of the end user. Admins can choose other claims, such as email or name, depending on their provider. However, claims other than email will be prefixed with the issuer URL to prevent naming clashes with other plugins.
sub
なので kube-apiserver と kubelogin 側の値を変更することで OIDC ユーザーの email などの値を username として使用することもできます。
kubectl oidc-login setup
で --oidc-extra-scope=email
を追加。
$ kubectl oidc-login setup \
--oidc-issuer-url=https://authelia.ops.com:9091 \
--oidc-client-id=kubernetes \
--oidc-client-secret=kubernetes \
--oidc-extra-scope=email
kube-apiserver.yaml では - --oidc-username-claim=email
を追加
spec:
containers:
- command:
...
- --oidc-issuer-url=https://authelia.ops.com:9091
- --oidc-client-id=kubernetes
- --oidc-username-claim=email
RoleBinding では sub 文字列の代わりに OIDC ユーザーの email を指定。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: authelia-developer
rules:
- apiGroups: [""]
resources: ["pods", "services"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: authelia-develope
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: dev-user@test.com
roleRef:
kind: ClusterRole
name: authelia-developer
apiGroup: rbac.authorization.k8s.io
context では --exec-arg=--oidc-extra-scope=email
を追加。
$ kubectl config set-credentials oidc \
--exec-api-version=client.authentication.k8s.io/v1beta1 \
--exec-command=kubectl \
--exec-arg=oidc-login \
--exec-arg=get-token \
--exec-arg=--oidc-issuer-url=https://authelia.ops.com:9091 \
--exec-arg=--oidc-client-id=kubernetes \
--exec-arg=--oidc-client-secret=kubernetes \
--exec-arg=--oidc-extra-scope=email
残りの部分は同様の手順で、OIDC ユーザーの email を使用してユーザーと k8s role のマッピングが行えます。
グループを role にマッピングする
kubectl oidc-login setup
で表示される手順や Kubernetes OpenID Connection authentication はユーザーを k8s 側の Role にマッピングする方法となっていますが、ユーザーだけでなく OIDC のグループをマッピングすることもできます。むしろ RBAC としてはこちらがメインの使い方になる気もしますが。
参考:
- https://github.com/int128/kubelogin/issues/114
- https://github.com/int128/kubelogin/issues/37
- https://developer.okta.com/blog/2021/10/08/secure-access-to-aws-eks
基本的には email で認証する
の設定と同様に、OIDC ユーザーの groups scope を認証に設定する方法となります。
手順 4 で k8s クラスタ内の control plane node 上の /etc/kubernetes/manifests/kube-apiserver.yaml
を編集する際に --oidc-groups-claim=groups
を追加します。
spec:
containers:
- command:
...
- --oidc-issuer-url=https://authelia.ops.com:9091
- --oidc-client-id=kubernetes
- --oidc-groups-claim=groups
また、kubectl oidc-login setup
コマンドに --oidc-extra-scope=groups
を追加。
$ kubectl oidc-login setup \
--oidc-issuer-url=https://authelia.ops.com:9091 \
--oidc-client-id=kubernetes \
--oidc-client-secret=kubernetes \
--oidc-extra-scope=groups
ブラウザが開いて初回セットアップ時と同様に権限の同意が要求されますが、初回と比較すると グループメンバーシップにアクセス
が追加されていることがわかります。こちらが groups
の scope に対応しています。
同意するとセットアップ手順が出力が表示されますが、id Token の中身をはじめのときと比較すると groups
が追加されていることがわかります。groups には要求に同意した oidc ユーザーが所属するグループが設定されています。
{
"amr": [
"pwd"
],
"at_hash": "3ePkHFLhhnfUiAQUWHJ5dw",
"aud": [
"kubernetes"
],
"auth_time": 1725628629,
"azp": "kubernetes",
"client_id": "kubernetes",
"exp": 1725633263,
"groups": [
"developer"
],
"iat": 1725629663,
"iss": "https://authelia.ops.com:9091",
"jti": "98cd6896-da71-48f5-b0b2-70f627d510ea",
"nonce": "3IyA0zi6gA5gkyOG0DXcZ5ukNGa321Hls9i5X9gEpOc",
"sub": "6b2acb37-d3d7-4333-91cf-a9f7c503ee05"
}
k8s 側でロールを作成する部分は同様ですが、RoleBinding の subject で user の代わりに oidc 側のグループ名 developer
を指定します。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: oidc-authelia-developer
rules:
- apiGroups: [""]
resources: ["pods", "services"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: oidc-authelia-developer
subjects:
- kind: Group
name: developer
roleRef:
kind: ClusterRole
name: oidc-authelia-reader
apiGroup: rbac.authorization.k8s.io
kubectl config set-credentials
で context を設定する際も oidc-extra-scope=groups
を追加します。
$ kubectl config set-credentials oidc \
--exec-api-version=client.authentication.k8s.io/v1beta1 \
--exec-command=kubectl \
--exec-arg=oidc-login \
--exec-arg=get-token \
--exec-arg=--oidc-issuer-url=https://authelia.ops.com:9091 \
--exec-arg=--oidc-client-id=kubernetes \
--exec-arg=--oidc-client-secret=kubernetes \
--exec-arg=--oidc-extra-scope=groups
これでユーザーと同様に kubectl
を実行すると、ユーザーが属する group に基づいた権限でコマンドが実行できます。実行可能な権限も role に基づいて設定されています。
$ kubectl auth can-i --list
Resources Non-Resource URLs Resource Names Verbs
selfsubjectreviews.authentication.k8s.io [] [] [create]
selfsubjectaccessreviews.authorization.k8s.io [] [] [create]
selfsubjectrulesreviews.authorization.k8s.io [] [] [create]
pods [] [] [get list watch create update patch delete]
services [] [] [get list watch create update patch delete]
[/api/*] [] [get]
[/api] [] [get]
[/apis/*] [] [get]
[/apis] [] [get]
[/healthz] [] [get]
[/healthz] [] [get]
[/livez] [] [get]
[/livez] [] [get]
[/openapi/*] [] [get]
[/openapi] [] [get]
[/readyz] [] [get]
[/readyz] [] [get]
[/version/] [] [get]
[/version/] [] [get]
[/version] [] [get]
[/version] [] [get]
kubelogin を使わない方法
k8s ドキュメントの Option 1 - OIDC Authenticator に示すように kubeconfig に直接 OIDC 関連の情報を指定することで kubelogin を使わずに OIDC 認証することもできます。やや手間がかかるので直接この方法を使用する機会はあまりなさそうですが、備忘録として記載しておきます。
ドキュメントによると必要な設定項目は以下。
kubectl config set-credentials USER_NAME \
--auth-provider=oidc \
--auth-provider-arg=idp-issuer-url=( issuer url ) \
--auth-provider-arg=client-id=( your client id ) \
--auth-provider-arg=client-secret=( your client secret ) \
--auth-provider-arg=refresh-token=( your refresh token ) \
--auth-provider-arg=idp-certificate-authority=( path to your ca certificate ) \
--auth-provider-arg=id-token=( your id_token )
この中で他の項目は authelia の config に基づいて設定できますが、refresh_token
と id_token
は実際にログインして取得する必要があります。OpenID Connect Tokens の 1. Log in to IdP ~ 2. Provide access_token, id_token, and refresh_token
に相当。
まず authelia の configuration.yml で scopes に offline_access
を追加します。
scopes:
- "openid"
- "profile"
- "groups"
- "email"
- "offline_access"
追加したらコンテナを再作成するなどして変更を適用。
次に authelia から authorization_code を取得するため認証用の URL を作成しますが、Authelia 側で PKCE(Proof Key for Code Exchange)を有効化しているので code_verifier と code_challenge を生成する必要があります。
参考
code_verifier は例えば以下のコマンドで作成できます。使用可能文字種:半角英数字(a〜z、A~Z、0~9)および記号(-._~)からなるランダムな文字列、文字数:43文字〜128文字
という制限があるので注意。
$ openssl rand -base64 50 | tr -d '=+/'
UDEcJyk3ncBVylCGQVKUjYSKGUKQYv7ecg92o5s4Sjd9C6b3TsE0DLA9A86Pz9VhU
code_challenge は code_verifier を sha256 で hash 化して base64url encode したものになります。例えば以下のようなコードで生成できます。
import base64
import hashlib
import sys
argv = sys.argv
code_verify = argv[1]
# Hash the input using SHA-256
hashed_bytes = hashlib.sha256(code_verify.encode('utf-8')).digest()
# Encode the hashed result in base64url format (replace + and / with - and _)
base64url_encoded = base64.urlsafe_b64encode(hashed_bytes).rstrip(b'=').decode('utf-8')
print(base64url_encoded)
$ python3 hash.py UDEcJyk3ncBVylCGQVKUjYSKGUKQYv7ecg92o5s4Sjd9C6b3TsE0DLA9A86Pz9VhU
nWL_jv0qY60oGXA5QDF_RLG7jscL1g8hy8O6XXzxwC4
これを元に以下のような URL を作ります。
https:/authelia.ops.com:9091/api/oidc/authorize?client_id=kubernetes&redirect_uri=http://localhost:8000&response_type=code&scope=openid+profile+email&state=xyz123456789&code_challenge_method=S256&code_challenge=nWL_jv0qY60oGXA5QDF_RLG7jscL1g8hy8O6XXzxwC4
リクエストパラメータは以下の通り。
parameter | value | 説明 |
---|---|---|
client_id | kubernetes | authelia 側で指定した client id |
redirect_uri | http://localhost:8000 | 何でも良いが authelia の redirect_uris に指定したものである必要がある |
response_type | openid profile offline_access |
authelia の scope |
state | xyz123456789 | 任意の値で良いがある程度の文字列長が必要 |
code_challenge_method | S256 | authelia 側で設定しているのと同じ S256 を指定 |
code_challenge | nWL_jv0qY60oGXA5QDF_RLG7jscL1g8hy8O6XXzxwC4 | 上記で生成した値 |
ブラウザで上記の URL を入力すると authelia のログイン、権限の同意が要求されるので同意します。
同意すると redirect_uri で指定した http://localhost:8000
にリダイレクトされます。アプリが起動していないので localhost で接続が拒否されました
となりますが、返された URL の中に authorization_code が記載されているのでメモします。
http://localhost:8000/?code=authelia_ac_bGHj7DSy0Ipf5IWdmvI0PPnXyM-9-WLEuaFqxtTeknc.cvfQGwUyxEUnVNBO6NWXOUxzSXuDnYbrqM7tPPImyK0&iss=https%3A%2F%2Fauthelia.ops.com%3A9091&scope=openid+profile+email&state=xyz123456789
code は上記のうち code=
から &iss=
の間の authelia_ac_bGHj7DSy0Ipf5IWdmvI0PPnXyM-9-WLEuaFqxtTeknc.cvfQGwUyxEUnVNBO6NWXOUxzSXuDnYbrqM7tPPImyK0
になります。
次に id_token 等を取得するために authelia の token endpoint /api/oidc/token
に以下のようなリクエストを実行します。
curl -H "Authorization: Basic $(echo -n "kubernetes:kubernetes" | base64)" \
--url 'https://authelia.ops.com:9091/api/oidc/token' \
--header 'content-type: application/x-www-form-urlencoded' \
--data "grant_type=authorization_code&code=authelia_ac_xqP9D_24o92BFkPZpuvJd5Q47-xMSPmZb0fbzQr8IC4.hteua34O3FFpTugL7THLUtPo2uI1EUtdBPPzTnzJLLY&redirect_uri=http://localhost:8000&code_verifier=UDEcJyk3ncBVylCGQVKUjYSKGUKQYv7ecg92o5s4Sjd9C6b3TsE0DLA9A86Pz9VhU"
header には client id
と client_secret
をそれぞれ入力して base64 encode した値を Basic Authentication として指定。
リクエストパラメータは以下の通り。
- code: 上記で取得した authorization_code
- redirect_uri: authorization_code を取得した際に指定したものと同じ url を指定
- code_verifier: 始めに生成した値と同じものを指定
認証に成功すると id_token と refresh_token を含む以下のようなレスポンスが返ってきます。
{
"access_token": "authelia_at_ZeMaoOGCXCSYfO9hJ-NrnAC0FACRDQJVDEXsTHHpN_A.Blo7i7F7nGkr7utT8TAW9_g_4aBhr1rX0R-Jy7TL4xk",
"expires_in": 3600,
"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImF1dGhlbGlhIiwidHlwIjoiSldUIn0.eyJhbXIiOlsicHdkIl0sImF0X2hhc2giOiJfdE9DRkUtTHNUZ3c5eDZMRkljb0lnIiwiYXVkIjpbImt1YmVybmV0ZXMiXSwiYXV0aF90aW1lIjoxNzI1NzE0ODcyLCJhenAiOiJrdWJlcm5ldGVzIiwiY2xpZW50X2lkIjoia3ViZXJuZXRlcyIsImVtYWlsIjoiZGV2LXVzZXJAdGVzdC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiZXhwIjoxNzI1NzE4NTI3LCJncm91cHMiOlsiZGV2ZWxvcGVyIl0sImlhdCI6MTcyNTcxNDkyNywiaXNzIjoiaHR0cHM6Ly9hdXRoZWxpYS5vcHMuY29tOjkwOTEiLCJqdGkiOiI5ZmY4YzczZi0wMTg4LTQ1ZjEtOTE5YS02YTU0NTNmODA2MTIiLCJuYW1lIjoiZGV2LXVzZXIiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJkZXYtdXNlciIsInN1YiI6IjM5ZWYzOTA1LTcwOGItNGY1Yi1hZmQ1LTdhZjRlYzc0ZWM3NyJ9.PEQ0eXC8czDzeXXopfluRpOoWrkmOIuAmyQ0q7ir3SY7eQq31dXwadS6up3ZIplHMQo8iYKD_Vogr9WIohDQCE5qJs53ovxEebTdWfAcL4gO6rPqmyYh5O6pKkS8v002l_M92aeNtyu7nAr6QFYteU1CiqmzlKZCF6777tfFGKpfDNIv113PJ4ybQeEeE7x3-fm3vcVge10NYSc4fcXxgvY7oeOCpZuvA4RnIDuFYybn4UwSV8rr4bLvQRHfnuHn2CDavaKNneIDsM4srlK5fT73NeIVKuD8HXOR9e8Ijk5NhLJf2QxoS3EKWTCd6fJEnXJa7w9axywlOdO9KelNvw",
"refresh_token": "authelia_rt_TKjWw8uvsID2MxKqbuA4KItBKofxqz7UU4D5exlsdLY.X2eaFppn9saEAmlWN5tNSufp2idNmY8mPDHjdGilOUY",
"scope": "openid profile email groups offline_access",
"token_type": "bearer"
}
これでようやく必要な情報が揃ったので、k8s ドキュメントに沿って credential を設定します。
kubectl config set-credentials oidc-test \
--auth-provider=oidc \
--auth-provider-arg=idp-issuer-url=https://authelia.ops.com:9091 \
--auth-provider-arg=client-id=kubernetes \
--auth-provider-arg=client-secret=kubernetes \
--auth-provider-arg=refresh-token=authelia_rt_TKjWw8uvsID2MxKqbuA4KItBKofxqz7UU4D5exlsdLY.X2eaFppn9saEAmlWN5tNSufp2idNmY8mPDHjdGilOUY \
--auth-provider-arg=id-token="eyJhbGciOiJSUzI1NiIsImtpZCI6ImF1dGhlbGlhIiwidHlwIjoiSldUIn0.eyJhbXIiOlsicHdkIl0sImF0X2hhc2giOiJfdE9DRkUtTHNUZ3c5eDZMRkljb0lnIiwiYXVkIjpbImt1YmVybmV0ZXMiXSwiYXV0aF90aW1lIjoxNzI1NzE0ODcyLCJhenAiOiJrdWJlcm5ldGVzIiwiY2xpZW50X2lkIjoia3ViZXJuZXRlcyIsImVtYWlsIjoiZGV2LXVzZXJAdGVzdC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiZXhwIjoxNzI1NzE4NTI3LCJncm91cHMiOlsiZGV2ZWxvcGVyIl0sImlhdCI6MTcyNTcxNDkyNywiaXNzIjoiaHR0cHM6Ly9hdXRoZWxpYS5vcHMuY29tOjkwOTEiLCJqdGkiOiI5ZmY4YzczZi0wMTg4LTQ1ZjEtOTE5YS02YTU0NTNmODA2MTIiLCJuYW1lIjoiZGV2LXVzZXIiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJkZXYtdXNlciIsInN1YiI6IjM5ZWYzOTA1LTcwOGItNGY1Yi1hZmQ1LTdhZjRlYzc0ZWM3NyJ9.PEQ0eXC8czDzeXXopfluRpOoWrkmOIuAmyQ0q7ir3SY7eQq31dXwadS6up3ZIplHMQo8iYKD_Vogr9WIohDQCE5qJs53ovxEebTdWfAcL4gO6rPqmyYh5O6pKkS8v002l_M92aeNtyu7nAr6QFYteU1CiqmzlKZCF6777tfFGKpfDNIv113PJ4ybQeEeE7x3-fm3vcVge10NYSc4fcXxgvY7oeOCpZuvA4RnIDuFYybn4UwSV8rr4bLvQRHfnuHn2CDavaKNneIDsM4srlK5fT73NeIVKuD8HXOR9e8Ijk5NhLJf2QxoS3EKWTCd6fJEnXJa7w9axywlOdO9KelNvw"
あとは設定した user を使用するように context を設定すれば、kubelogin のときと同様に OIDC 側の認証情報で各種 kubectl
コマンドが実行できるようになります。
Discussion