はじめに
OpenID Connect (OIDC)を利用してKubernetesに接続するユーザーの管理を行っています。
OIDC Providerとして利用しているdexidp/dexのLDAPコネクタにバグがあったため一部でパスワードが正しいにも関わらず認証できないユーザーが存在しました。
現在このバグはv2.39.1で修正されています。
kubectlコマンドが利用できないとkubernetesに関する操作が何もできなくなってしまうため、個別にconfigファイルを発行することにしました。
OIDCでユーザー認証をするはずだったユーザー個別に設定ファイルを準備したので、その顛末をまとめておきます。
参考資料
- https://aungzanbaw.medium.com/a-step-by-step-guide-to-creating-users-in-kubernetes-6a5a2cfd8c71
- https://kubernetes.io/docs/reference/access-authn-authz/certificate-signing-requests/#normal-user
基本的な流れ
次のようなステップ毎に説明をしていきます。
- 公開鍵認証方式による秘密鍵とCSRを準備する
- KubernetesにCSRをCertificateSigningRequestオブジェクトとして登録する
- 登録した情報を承認し、接続に必要な公開鍵を作成する
- configファイルを作成し、ユーザーの~/.kube/configなどに配置して利用してもらう
公開鍵認証方式による秘密鍵とCSRを準備する
一般的なTLSに利用する証明書ファイルを準備する方法とほぼ一緒です。
ここでCommon Name(CN)を設定しますが、ここにUserIDとして認識させたい文字列(例: user01@example.org) などを指定します。参考資料に挙げたMediumの記事ではOなどを付与したDNを指定していますが、CNにUserIDだけを指定します。
また公式ガイドではgenrsaを利用していますが、いまさら感があるのでMediumの記事のようにed25519を利用します。
$ openssl genpkey -out $(id -un).key -algorithm Ed25519
$ openssl req -new -key $(id -un).key -out $(id -un).csr -subj "/CN=$(id -un)@example.com"
"@example.com"の部分は実際に利用するユーザーIDとしてemail属性を参照するものとしています。uid属性を参照するのであればドメイン部分は不要だと思いますが、自分の環境に合わせてください。
KubernetesにCSRをCertificateSigningRequestオブジェクトとして登録する
公式ガイドなどではヒヤドキュメントを利用したOne time jobとして描かれていますが、次のようなスクリプトを準備しました。
#!/bin/bash
sed -e "s/__NAME__/$(id -un)/" -e "s/__CSR__/$(base64 < $(id -un).csr | tr -d '\n')/" <<EOF
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: __NAME__
spec:
request: __CSR__
signerName: kubernetes.io/kube-apiserver-client
expirationSeconds: 31536000 # 365 days
usages:
- client auth
EOF
前の手順でUIDを元にファイルを準備していることを引き続き利用しています。
$ bash gen-csr.sh > $(id -un).yaml
ここまでで、次のファイルが生成されました。
- $(id -un).key
- $(id -un).csr
- gen-csr.sh
- $(id -un).yaml
次に最後に生成した$(id -un).yamlファイルをapplyします。
Control Planeノードにログインするなどして管理者権限で次のように$(id -un).yamlファイルを適用します。
$ kubectl apply -f $(id -un).yaml
certificatesigningrequest.certificates.k8s.io/ubuntu created
$ id -un
ubuntu
登録した情報を承認し、接続に必要な公開鍵を作成する
ここまでで登録したCSRはCONDITIONがPendingとなっていて、そのままでは利用できません。
利用するために必要な証明書を生成してもらうためにappoveします。
$ kubectl certificate approve $(id -un)
この前後でcsrオブジェクトの状態は次のようにPendingからApproved,Issuedに変化しています。
$ kubectl get csr
NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
ubuntu 89s kubernetes.io/kube-apiserver-client kubernetes-admin 365d Pending
$ kubectl get csr
NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
ubuntu 2m25s kubernetes.io/kube-apiserver-client kubernetes-admin 365d Approved,Issued
$ kubectl get csr/$(id -un) -o jsonpath="{.status.certificate}" | base64 -d > $(id -un).crt
configファイルを作成し、ユーザーの~/.kube/configなどに配置して利用してもらう
ここまでで接続に必要な情報が生成できたので、ユーザーに送付するためのconfigファイルを生成します。
Control Plane Nodeで作業している前提で、さらに次の情報を埋め込んでいます。
- 接続先のAPI Server: kubecamp.example.com:6443
次のようなスクリプトでユーザー用の設定ファイルを配布しています。
#!/bin/bash
sed -e "s/__CA_CRT__/$(base64 < /etc/kubernetes/ssl/ca.crt | tr -d '\n')/" -e "s/__USER__/$(id -un)/g" <<EOF > config.$(id -un)
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: __CA_CRT__
server: https://kubecamp.example.com:6443
name: cluster.local
contexts:
- context:
cluster: cluster.local
user: __USER__@example.com
name: kubecamp.example.com@cluster.local
current-context: kubecamp.example.com@cluster.local
kind: Config
preferences: {}
users:
EOF
kubectl --kubeconfig config.$(id -un) config set-credentials $(id -un)@example.com --client-key=$(id -un).key --client-certificate=$(id -un).crt --embe
d-certs=true
スクリプト中ではuser:やname:にメールアドレス形式でユーザーIDを埋め込んでいますが、この部分はマッチすれば何でも構いません。CSRに設定したCNをユーザーIDとして利用するため、この段階では単純に設定ファイル内での整合性が取れていれば何でも構いません。
最終的に config.$(id -un) のファイル名でユーザーに渡すための設定ファイルが生成されます。
$ kubectl --kubeconfig config.$(id -un) get node
NAME STATUS ROLES AGE VERSION
node1 Ready control-plane 2y230d v1.28.6
node2 Ready control-plane 2y230d v1.28.6
node3 Ready <none> 2y230d v1.28.6
node4 Ready <none> 2y230d v1.28.6
$ kubectl --kubeconfig config.$(id -un) -n $(id -un) get cm
NAME DATA AGE
kube-root-ca.crt 1 34d
$ kubectl --kubeconfig config.$(id -un) -n kube-system get cm
Error from server (Forbidden): configmaps is forbidden: User "ubuntu@example.com" cannot list resource "configmaps" in API group "" in the namespace "kube-system"
ユーザーには自身のnamespace内にあるconfigmap(cm)やsecretのみにアクセスできるよう権限を付与しているので、正しく動作することを確認します。
ユーザー毎のRoleBindingやClusterRoleBindingの設定
これらは本来OpenID Connectで認証できたとしてユーザー毎に個別に設定しているはずなので省略します。
ただ、公式ガイドにはkubectlコマンドを駆使した例が掲載されていますが、あまり汎用性がないように思えるのでkubectl get -n $(id -un) rolebinding
やkbuectl get --all-namespaces rolebinding
のような方法で設定や確認をするべきだと思います。
少なくとも例に挙げられているkubectl create role developer --verb=create --verb=get --verb=list --verb=update --verb =delete --resource=pods
を実行した結果が次のような定義に展開されるということは把握しておくべきだと思います。
kubectl get role developer -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
creationTimestamp: "2024-04-17T04:01:42Z"
name: developer
namespace: default
resourceVersion: "307861872"
uid: a9adf946-7eb7-48f7-96ed-e3e5dfa115d6
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- create
- get
- list
- update
- delete
Roleオブジェクトのスコープはnamespaceなので、ユーザー毎に作成する必要があって、kubectl の create roleを使った方法では少し細やかな設定をするのが難しくなるのではないかと思われます。
さいごに
ここで作成したUser用の設定ファイルはOIDCで認証したUserと同じ権限を付与しますが、その他に有効期限を長期に設定したいバッチジョブなどを実行するプロセス用に利用することもできます。
Kubernetesを多人数で利用する際の手法としてOIDC認証は容易に導入できますが、少し使いにくい点があるのも事実です。
最初にMediumの記事だけを参考にした時には、組織属性(O=edit)が邪魔をして期待どおりに動作しませんでした。この記事がなにかしらの参考になれば幸いです。