尝试使用Keycloak来进行Kubernetes的OIDC认证
首先
通常情况下,Kubernetes并没有用户管理机制,需要借助外部机制来进行用户认证。如果使用kubeadm或minikube等工具构建原生的Kubernetes集群,通常会使用x509证书进行用户认证(admin证书可用于认证)。
在这里,我们将使用Keycloak作为OIDC(OpenID Connect)ID提供者之一来管理用户信息并进行OIDC认证,然后使用该认证信息(id_token)来进行Kubernetes(API)的认证。
Keycloak是一个网站,其网址是https://www.keycloak.org/。
另外,Kubernetes中的OIDC认证顺序可以参考官方文档中如下说明。
OpenID Connect令牌
https://kubernetes.io/zh/docs/reference/access-authn-authz/authentication/#openid-connect-tokens
前提意味着一个前面的条件或假设。 (The premise implies a previously stated condition or assumption.)
minikube v1.25.2(Kubernetes v1.23.3)のシングルNode構成で検証した
Keycloakは現時点の最新バージョンであるv18.0.0を用いることとし、Kubernetes(minikube)クラスター上に構築した(一般的なKubernetesクラスター上に構築するケースでもそんなに大差はない想定)
検証用なのでKeycloakは以下の条件で起動した
dev mode(本来であればprod modeで起動すべき)
KeycloakのデータストアはInternal DB(H2)を永続化して利用(本来であればPostgreSQLなど外部DBを利用すべき)
証明書は自己証明書を利用した(本来であれば信頼できるCAで署名されたものを利用すべき)
Keycloakの名前解決にはDNSではなくhostsファイルを使用した
Keycloakにはそれぞれkube-apiserverとクライアントが同じホスト名でアクセスできる必要があるため暫定対処
本来はDNSの名前解決でアクセスできるのが望ましい
Keycloakのアクセスエンドポイント(HTTP/HTTPS)はNodePortとした
Keycloakにはそれぞれkube-apiserverとクライアントが同じポートでアクセスできる必要があるため暫定対処
本来はingressやLBに証明書のインポートを行いTLS終端すべき
基本的にKeycloak公式ドキュメントに沿っているが、必要に応じて設定を変更している
Getting started/Kubernetes
https://www.keycloak.org/getting-started/getting-started-kube
3. 创建Keycloak的证书
首先,创建Keycloak将用于TLS的密钥对。
然后,在Kubernetes集群上,通过hostPath将密钥对挂载到部署Keycloak的Pod上,在minikube虚拟机上执行创建操作。
连接到minikube的SSH。
minikube ssh
ディレクトリ作成
sudo su -
mkdir /srv/keycloak-ssl
cd /srv/keycloak-ssl
创建证书设置文件
sslcert.conf:ssl证书配置文件
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
CN = keycloak
[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = keycloak
DNS.2 = keycloak.example.com
创建密钥对
# openssl req -x509 -nodes -days 730 -newkey rsa:2048 -keyout tls.key -out tls.crt -config sslcert.conf -extensions 'v3_req'
ls
sslcert.conf tls.crt tls.key
可以确定公钥的签发者和主题都是同一个自签名证书(即所谓的自制证书)。
# openssl x509 -in tls.crt --text --noout
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
53:e8:c5:1e:e4:6c:dd:13:59:f8:4d:81:0d:6f:56:97:06:e3:4f:f3
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = keycloak
Validity
Not Before: May 8 06:27:59 2022 GMT
Not After : May 7 06:27:59 2024 GMT
Subject: CN = keycloak
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
・・・
今回KeycloakのPodはhostPathを用いてキーペア格納フォルダをマウントするため権限を変更しておく。
※厳密にはtls.keyにRead権限を付与しておけば良いはず。
# chmod -R 777 /srv/keycloak-ssl
将公开密钥配置到主节点上。
# mkdir -p /etc/kubernetes/ssl/
# cp tls.crt /etc/kubernetes/ssl/kc-ca.crt
# ls /etc/kubernetes/ssl/
kc-ca.crt
创建Keycloak持久化目录。
当将Keycloak部署为Pod时,通常情况下信息将保存在Keycloak内部的本地数据库(H2)中。然而,当容器重新启动时,这些数据将会丢失。
理想情况下,应该指定外部数据库(如PostgreSQL)作为保存位置。但是,本次我们将数据保存到minikube虚拟机上的目录,并通过将其作为hostPath的volumeMounts挂载到Pod上来解决这个问题。
在minikube虚拟机上创建存储目录。
# mkdir -p /srv/keycloak
# chmod -R 777 /srv/keycloak
# ls -la /srv
total 0
drwxr-xr-x 4 root root 80 May 8 06:46 .
drwxr-xr-x 19 root root 500 May 8 06:24 ..
drwxrwxrwx 2 root root 40 May 8 06:46 keycloak
drwxrwxrwx 2 root root 100 May 8 06:27 keycloak-ssl
5. Keycloak建立
通过具有minikube操作权限的客户端,在Kubernetes集群上将Keycloak部署为Pod。
Keycloak的部署
我使用了一部分Keycloak官方文档作为参考,并修改了一些manifest。然后使用以下manifest进行部署。
Keycloak启动配置的注意事项。
dev modeで起動
HTTPSポートの指定(デフォルト8443)
NodePortでサービスを公開
TLSキーペアの指定と格納先をhostPathとしてマウント
keycloak.yaml 文件
apiVersion: v1
kind: Namespace
metadata:
name: keycloak
---
apiVersion: v1
kind: Service
metadata:
name: keycloak
namespace: keycloak
labels:
app: keycloak
spec:
ports:
- name: http
port: 8080
targetPort: 8080
nodePort: 31008 # NodePortを明示
- name: https # HTTPアクセス用に8443ポートを公開
port: 8443
targetPort: 8443
nodePort: 32084 # NodePortを明示
selector:
app: keycloak
type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: keycloak
namespace: 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:18.0.0
args: ["start-dev"] # dev modeで起動
env:
- name: KEYCLOAK_ADMIN
value: "admin"
- name: KEYCLOAK_ADMIN_PASSWORD
value: "admin"
- name: KC_PROXY
value: "edge"
- name: "KC_HTTP_ENABLED" # HTTP接続有効化(ブラウザアクセス用)
value: "true"
- name: "KC_HTTPS_PORT" # HTTPSポート
value: "8443"
- name: KC_HTTPS_CERTIFICATE_FILE # 公開鍵のパス
value: "/opt/keycloak/tls/tls.crt"
- name: KC_HTTPS_CERTIFICATE_KEY_FILE # 秘密鍵のパス
value: "/opt/keycloak/tls/tls.key"
ports:
- name: http
containerPort: 8080
- name: https # TLS用に8443ポートを公開
containerPort: 8443
readinessProbe:
httpGet:
path: /realms/master
port: 8080
volumeMounts: # 再起動してもデータが揮発しないよう暫定でhostPathにマウント
- name: "keycloak-persistent-storage"
mountPath: "/opt/keycloak/data"
- name: "keycloak-ssl" # キーペア格納先
mountPath: "/opt/keycloak/tls"
volumes:
- name: keycloak-persistent-storage
hostPath:
path: /srv/keycloak
type: DirectoryOrCreate
- name: keycloak-ssl # 証明書格納先
hostPath:
path: /srv/keycloak-ssl
type: DirectoryOrCreate
部署执行
# kubectl apply -f keycloak.yaml
namespace/keycloak created
service/keycloak created
deployment.apps/keycloak created
# kubectl -n keycloak get all
NAME READY STATUS RESTARTS AGE
pod/keycloak-679b66bbd8-l2n9g 1/1 Running 0 5m16s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/keycloak NodePort 10.102.40.27 <none> 8080:31008/TCP,8443:32084/TCP 5m16s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/keycloak 1/1 1 1 5m16s
NAME DESIRED CURRENT READY AGE
replicaset.apps/keycloak-679b66bbd8 1 1 1 5m16s
Keycloak名前解決設定
由于这次是验证,请在/etc/hosts文件中添加记录并进行相应的处理。
*在Keycloak的浏览器访问设备和minikube虚拟机上分别进行添加。
确认 minikube 虚拟机的 IP 地址。
# minikube ip
192.168.59.101
/etc/hosts 请将以下内容用中文本地化:
・・・
<minikube VM IP> keycloak.example.com
・・・
6. Keycloak访问确认
进行对Keycloak的访问确认。
检查curl访问(HTTPS)
使用curl命令从minikube虚拟机验证是否可以进行HTTPS访问。
由于Keycloak使用自签名证书,因此使用公钥对其进行CA签名。
因此,在进行TLS连接时,客户端需要信任Keycloak的CA证书(即公钥),以实现访问。
# curl -v --cacert /etc/kubernetes/ssl/kc-ca.crt https://keycloak.example.com:32084
浏览器访问确认(HTTP)
使用浏览器访问以下网址。
* 由于直接使用浏览器通过HTTPS访问无法显示页面(ERR_SSL_KEY_USAGE_INCOMPATIBLE),故改为使用HTTP访问。
http://keycloak.example.com:31008
7. Keycloak的配置。
通过浏览器访问Keycloak并进行以下配置。
访问管理界面
使用ID/Password=admin登录到管理控制台。
创建领域
在Keycloak中创建与租户概念相对应的Realm。本次创建的Realm名称为”kubernetes”。
创建Clients和ClientScopes。
创建一个客户端并设置相应的客户端范围,作为Keycloak的身份验证接收端。
制作客户端
Client ID: kubernetes
Access Type: confidential
Valid Redirect URIs: http://* https://*
创建客户范围
Client Scope: groups
Mappers: name groupsを追加
向客户端的客户端范围添加groups。
从客户端凭据中获取密钥
创建用户和组
创建用于认证的用户和组。
administrators、developersというグループを作成。
これらのグループに対して後程RBACによりKubernetesクラスターに対する権限を付与する
administrators: Kubernetesクラスターの管理者権限を持つグループ
developers: Kubernetesクラスターに対して特定の権限のみしか持たないグループ
尽管截图只显示了管理员的创建,但也要同样创建开发人员。
创建名为admin-user和dev-user的用户。
admin-user: administratorsグループに所属
dev-user: developersグループに所属
只显示了创建admin-user,但是需要同样创建dev-user。
密码可以设置为任意值(在此设置为P@ssw0rd)。
確認
确认可以通过以下命令在Keycloak上进行认证,并获取id-token和refresh-token。
# curl -k -d "grant_type=password" -d "scope=openid" -d "client_id=kubernetes" -d "client_secret=<client=kubernetesのSecret>" -d "username=<Keycloakで作成したユーザー名>" -d "password=<Keycloakで作成したユーザーパスワード>" https://keycloak.example.com:32084/realms/kubernetes/protocol/openid-connect/token | jq .
・・・
{
"access_token": ・・・,
"expires_in": 300,
"refresh_expires_in": 1800,
"refresh_token": ・・・
"id_token": ・・・,
"not-before-policy": 0,
"session_state": ・・・,
"scope": "openid profile email groups"
}
可以使用以下命令对每个令牌进行验证。
exp: token有効期限(UNIX時間)
iat: token発行時間(UNIX時間)
active: tokenの有効有無
# curl -k --user "kubernetes:<client=kubernetesのsecret>" -d "token=<token>" https://keycloak.example.com:32084/realms/kubernetes/protocol/openid-connect/token/introspect | jq .
・・・
{
"exp": 1651997178,
"iat": 1651996878,
・・・
"active": true
}
8. Kubernetes的API服务器配置
kube-apiserverがKeycloakを用いてOIDC認証を行うための設定を行う。
minikube VMにアクセスし、kube-apiserverのmanifestに起動OPおよびKeycloakCA証明書(=公開鍵)格納先をvolumeMountsする。
なお、kube-apiserverはStatic Podとして起動しているためmanifest変更後に自動的に再デプロイされる。
/etc/kubernetes/manifests/kube-apiserver.yaml
・・・
spec:
containers:
- command:
- kube-apiserver
・・・
- --oidc-issuer-url=https://keycloak.example.com:32084/realms/kubernetes
- --oidc-client-id=kubernetes
- --oidc-username-claim=name
- --oidc-groups-claim=groups
- --oidc-ca-file=/etc/kubernetes/ssl/kc-ca.crt
・・・
volumeMounts:
・・・
- mountPath: /etc/kubernetes/ssl
name: keycloak-ca-certificates
readOnly: true
・・・
volumes:
・・・
- hostPath:
path: /etc/kubernetes/ssl
type: DirectoryOrCreate
name: keycloak-ca-certificates
status: {}
9. 制定RBAC系统
Keycloakで作成したグループに対してRBACの設定を行う。
今回はそれぞれ以下のような権限設定とする。
administrators: クラスターに対する全ての権限を付与(ビルドインのClusterRole=cluster-adminをバインド)
developers: NamespaceとPodの参照権限のみを付与(独自でClusterRole=developer-roleを作成してバインド)
管理员角色配置文件.yaml
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: administrator-crb
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: Group
name: "administrators"
apiGroup: rbac.authorization.k8s.io
devrole.yaml-请将devrole.yaml进行释义。
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: developer-role
rules:
- apiGroups: [""]
resources: ["namespaces","pods"]
verbs: ["get", "watch", "list"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: developer-crb
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: developer-role
subjects:
- kind: Group
name: "developers"
apiGroup: rbac.authorization.k8s.io
クラスターに適用
# kubectl apply -f adminrole.yaml
clusterrolebinding.rbac.authorization.k8s.io/administrator-crb created
# kubectl apply -f devrole.yaml
clusterrole.rbac.authorization.k8s.io/developer-role created
clusterrolebinding.rbac.authorization.k8s.io/developer-crb created
10.kubectlの設定
kubectlにはOIDC ID プロバイダーにログインする仕組みが用意されていないため、
手動でログインおよびkubeconfigへのtoken設定を行う。
似乎存在一种可以通过kubectl进行登录的插件。kubelogin 可在 GitHub 上找到:https://github.com/int128/kubelogin。
步骤1:登录Keycloak
可以通过以下命令在Keycloak上进行身份验证,获取id-token和refresh-token。
# curl -k -d "grant_type=password" -d "scope=openid" -d "client_id=kubernetes" -d "client_secret=<client=kubernetesのsecret>" -d "username=<Keycloakで作成したユーザー名>" -d "password=<Keycloakで作成したユーザーパスワード>" https://keycloak.example.com:32084/realms/kubernetes/protocol/openid-connect/token | jq .
步骤2:设置kubeconfig配置文件
使用获得的id-token和refresh-token来配置kubeconfig。
# kubectl config set-credentials <Keycloakで作成したユーザー名> \
"--auth-provider=oidc" \
"--auth-provider-arg=idp-issuer-url=https://keycloak.example.com:32084/realms/kubernetes" \
"--auth-provider-arg=client-id=kubernetes" \
"--auth-provider-arg=idp-certificate-authority=<keycloak公開鍵のパス>" \
"--auth-provider-arg=client-secret=<client=kubernetesのsecret>" \
"--auth-provider-arg=id-token=<id-token>" \
"--auth-provider-arg=refresh-token=<refresh-token>"
# kubectl config set-context <Keycloakで作成したユーザー名>@<kubeconfigで定義されているk8sクラスタ名> --cluster=<kubeconfigで定義されているk8sクラスタ名> --user=<Keycloakで作成したユーザー名>
# kubectl config use-context <Keycloakで作成したユーザー名>@<kubeconfigで定義されているk8sクラスタ名>
Switched to context "<Keycloakで作成したユーザー名>@<kubeconfigで定義されているk8sクラスタ名>".
使用shell脚本一次性执行上述操作
#!/bin/bash
scope=openid
client_id=kubernetes
client_secret=<client=kubernetesのsecret>
username=<Keycloakで作成したユーザー名>
password=<Keycloakで作成したユーザーパスワード>
oidc_url=https://keycloak.example.com:32084/realms/kubernetes/protocol/openid-connect/token
realm_url=https://keycloak.example.com:32084/realms/kubernetes
certificate=<keycloak公開鍵のパス>
cluster=<kubeconfigで定義されているk8sクラスタ名>
### Generate Authentication token
json_data=`curl -k -d "grant_type=password" -d "scope=${scope}" -d "client_id=${client_id}" -d "client_secret=${client_secret}" -d "username=${username}" -d "password=${password}" ${oidc_url}`
id_token=`echo $json_data | jq '.id_token' | tr -d '"'`
refresh_token=`echo $json_data | jq '.refresh_token' | tr -d '"'`
access_token=`echo $json_data | jq '.access_token' | tr -d '"'`
### Print tokens
echo "ID_TOKEN=$id_token"; echo
echo "REFRESH_TOKEN=$refresh_token"; echo
echo "ACCESS_TOKEN=$access_token"; echo
### Introspect the id token
token=`curl -k --user "${client_id}:${client_secret}" -d "token=${id_token}" ${oidc_url}/introspect`
token_details=`echo $token | jq .`
echo $token_details
### Update kubectl config
kubectl config set-credentials ${username} \
"--auth-provider=oidc" \
"--auth-provider-arg=idp-issuer-url=${realm_url}" \
"--auth-provider-arg=client-id=${client_id}" \
"--auth-provider-arg=client-secret=${client_secret}" \
"--auth-provider-arg=refresh-token=${refresh_token}" \
"--auth-provider-arg=idp-certificate-authority=${certificate}" \
"--auth-provider-arg=id-token=${id_token}"
### Create new context
kubectl config set-context ${username}@${cluster} --cluster=${cluster} --user=${username}
### Set current context
kubectl config use-context ${username}@${cluster}
### Validate access with new context
kubectl get pods
11. 確認行動
使用在Keycloak上创建的用户来验证对Kubernetes集群的访问权限。
管理员 – 用户
对于集群,可以执行所有操作。
# kubectl -n kube-system get all
NAME READY STATUS RESTARTS AGE
pod/coredns-64897985d-dn9c4 1/1 Running 0 125m
pod/etcd-minikube 1/1 Running 0 125m
pod/kube-apiserver-minikube 1/1 Running 0 15m
pod/kube-controller-manager-minikube 1/1 Running 0 125m
pod/kube-proxy-gbw5v 1/1 Running 0 125m
pod/kube-scheduler-minikube 1/1 Running 0 125m
pod/storage-provisioner 1/1 Running 4 (15m ago) 125m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 125m
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/kube-proxy 1 1 1 1 1 kubernetes.io/os=linux 125m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/coredns 1/1 1 1 125m
NAME DESIRED CURRENT READY AGE
replicaset.apps/coredns-64897985d 1 1 1 125m
亲爱的用户
参照未经许可的资源会导致错误。
# kubectl -n kube-system get all
NAME READY STATUS RESTARTS AGE
coredns-64897985d-dn9c4 1/1 Running 0 128m
etcd-minikube 1/1 Running 0 128m
kube-apiserver-minikube 1/1 Running 0 17m
kube-controller-manager-minikube 1/1 Running 0 128m
kube-proxy-gbw5v 1/1 Running 0 128m
kube-scheduler-minikube 1/1 Running 0 128m
storage-provisioner 1/1 Running 4 (18m ago) 128m
Error from server (Forbidden): replicationcontrollers is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#user dev" cannot list resource "replicationcontrollers" in API group "" in the namespace "kube-system"
Error from server (Forbidden): services is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#user dev" cannot list resource "services" in API group "" in the namespace "kube-system"
Error from server (Forbidden): daemonsets.apps is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#user dev" cannot list resource "daemonsets" in API group "apps" in the namespace "kube-system"
Error from server (Forbidden): deployments.apps is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#user dev" cannot list resource "deployments" in API group "apps" in the namespace "kube-system"
Error from server (Forbidden): replicasets.apps is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#user dev" cannot list resource "replicasets" in API group "apps" in the namespace "kube-system"
Error from server (Forbidden): statefulsets.apps is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#user dev" cannot list resource "statefulsets" in API group "apps" in the namespace "kube-system"
Error from server (Forbidden): horizontalpodautoscalers.autoscaling is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#user dev" cannot list resource "horizontalpodautoscalers" in API group "autoscaling" in the namespace "kube-system"
Error from server (Forbidden): cronjobs.batch is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#user dev" cannot list resource "cronjobs" in API group "batch" in the namespace "kube-system"
Error from server (Forbidden): jobs.batch is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#user dev" cannot list resource "jobs" in API group "batch" in the namespace "kube-system"
继续
使用Keycloak的OIDC身份验证在Kubernetes上进行租户控制。
请阅读以下内容。
Keycloak/入门/Kubernetes
https://www.keycloak.org/入门/入门-kube
Kubernetes/身份验证
https://kubernetes.io/zh/docs/reference/access-authn-authz/authentication/#openid-connect-tokens
如何在Kubernetes中使用Keycloak OIDC提供者对用户进行身份验证
How to authenticate user with Keycloak OIDC Provider in Kubernetes
广告
我在使用Twitter。
如果你能关注我,我会很高兴。
@mochizuki875
我希望成为一名专业的IT工程师,我喜欢Kubernetes。
一只狐狸,而不是狸猫。
Tweets by mochizuki875