本文介绍如何在ACK托管集群中部署Keycloak服务,并将Keycloak作为K8s OpenID Connect的认证服务器,实现在ACK托管集群上的身份认证。
前提条件
已创建Kubernetes托管版集群,且集群为1.22及以上版本。
(可选)已安装jwt-cli,用于解析JWT Token。
环境准备
已准备Keycloak服务的可用域名。
证书
为确保Keycloak在生产环境下的安全性,您需要执行以下命令,生成根证书和对应的服务端证书。其中服务端证书的SAN中要签入已准备的Keycloak服务的可用域名。
openssl genrsa -out rootCA.key 2048 #生成ca key。 openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 3650 -out rootCA.crt #生成 ca cert。 openssl genrsa -out server.key 2048 #生成server key。 openssl req -new -key server.key -out server.csr openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server.crt -days 3650 -sha256 -extensions v3_req -extfile <(echo '[v3_req]'; echo 'subjectAltName = DNS:${服务域名}')
数据库
Keycloak的数据默认持久化在本地文件中,在生产环境中需要通过数据库来持久化数据,Keycloak支持的数据库及版本如下。
数据库
Option value属性值
版本
MariaDB Server
mariadb
10.11
Microsoft SQL Server
mssql
2022
MySQL
mysql
8.0
Oracle Database
oracle
19.3
PostgreSQL
postgres
15
本文示例选择阿里云RDS数据库MySQL版用于持久化Keycloak数据,并创建名为Keycloak的数据库。关于数据库实例创建和使用的具体操作,请参见快速创建RDS MySQL实例。
步骤一:在ACK托管集群中部署Keycloak
在ACK集群中部署Keycloak服务。
使用以下示例代码,创建keycloak.yaml文件。
apiVersion: v1 kind: Service metadata: labels: app: keycloak name: keycloak namespace: default spec: ports: - name: http port: 80 protocol: TCP targetPort: 8080 selector: app: keycloak type: ClusterIP
执行以下命令,在ACK托管集群中部署Keycloak服务,用于内网访问。
kubectl apply -f keycloak.yaml
部署相关配置。
使用以下示例代码,创建keycloak-secret.yaml文件。
以下代码中的数据库配置用于连接Keycloak数据库,管理员配置用于首次登录Keycloak使用。
apiVersion: v1 data: db_passwd: ${数据库密码base64编码} db_username: ${数据库用户名base64编码} db_url: ${数据库url/host base64编码} keycloak_admin: ${keycloak管理用户名base64编码} keycloak_admin_password: ${keycloak管理员密码base64编码} kind: Secret metadata: name: keycloak-secret namespace: default type: Opaque
执行以下命令,部署相关配置。
kubectl apply -f keycloak-secret.yaml
使用以下示例代码,创建keylock-pki.yaml文件。
apiVersion: v1 data: tls.crt: ${服务端证书base64编码} tls.key: ${服务端私钥base64编码} kind: Secret metadata: name: keycloak-pki namespace: default type: IngressTLS
执行以下命令,部署服务端证书。
kubectl apply -f keycloak-pki.yaml
部署Ingress。
使用以下示例代码,创建keycloak-ingress.yaml文件。
以下代码中服务域名,替换为您前期已准备的Keycloak服务的可用域名。
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: labels: ingress-controller: nginx name: keycloak namespace: default spec: ingressClassName: nginx rules: - host: ${服务域名} http: paths: - backend: service: name: keycloak port: number: 80 path: / pathType: ImplementationSpecific tls: - hosts: - ${服务域名} secretName: keycloak-pki
执行以下命令,部署Ingress。
kubectl apply -f keycloak-ingress.yaml
Ingress部署完成后,会产生对外的IP入口,服务域名解析会解析至该IP。
部署Keycloak Deployment。
使用以下示例代码,创建keycloak-deploy.yaml文件。
执行以下命令,部署Keycloak Deployment。
kubectl apply -f keycloak-deploy.yaml
在浏览器中输入
https://${Keycloak服务域名}
访问Keycloak服务,出现如下页面表示服务部署成功。
步骤二:配置Keycloak
User设置
在Keycloak服务访问页面,单击Administration Console,输入用户名和密码登录Admin控制台。
用户名和密码为您在步骤2.a的keycloak-secret.yaml中配置的管理员登录信息。
在页面左上角的下拉列表中,选择master,然后单击Create Realm。
在Create realm页面,输入Realm name为myrealm,然后单击Create,创建一个名为myrealm的新Realm,Realm在Keycloak中代表租户。
在页面左上角的下拉列表中,选择已创建的myrealm,在左侧导航栏选择Users,然后单击Add user。
在Create user页面,输入Username为myuser,其它为可选项,设置完后,单击Create创建用户myuser。
在myuser页面,单击Attributes,添加用户myuser的相关属性,设置Key为name,Value为ack,然后单击Save。该属性可以被注入到ID Token中。
在myuser页面,单击Credentials,然后单击Set Password。
设置密码时将Temporary置为On,然后单击Save,第一次登录后需修改密码。
登录Keycloak的地址为
https://${Keycloak 服务域名}/realms/${用户所在realm}/account
。
Client设置
Client为请求Keycloak对用户进行身份验证的客户端。用户设置完成后,需完成Client的设置。
在myrealm页面左侧导航栏选择Clients,然后在Clients页面单击Create client。
在Create client页面,进行General Settings、Capability config、Login settings相关设置。
在General Settings页面,设置Client ID和Name,本例均设置为ack,然后单击Next。
在Capability config页面,设置Client authentication为on,表示设置Client的access type为confidential,其他选项保持默认,然后单击Next。
在Login settings页面,设置Valid redirect URIs为http://* ,用于设置浏览器登录成功后有效的重定向URL,本例的http://* 表示匹配所有HTTP重定向的网址。然后单击Save。
Client scopes设置
Client scopes是在多个客户端之间共享的一组通用协议Mapper和Role。
在myrealm页面左侧导航栏选择Client scopes,然后在Client scopes页面单击Create client scope。
在Create client scope页面,设置Name为ack-kubernetes,其他配置项保持默认,然后单击Save。
在ack-kubernetes页面,单击Mappers,然后单击Configure a new mapper。
在Configure a new mapper页面,创建一个User Attribute,用于把用户属性注入到ID Token 中,即上文步骤6添加的name和ack字段。
在Add mapper页面,设置Name为name,User Attribute为name(即选择上文设置的name: ack属性),Token Claim Name为name(表示该属性在Token中的命名)。其他配置项保持默认。然后单击Save。
在左侧导航栏,单击Clients返回Clients页面,然后选择名为ack的Client。
在ack的client页面,单击Client scopes,然后单击Add client scope,在弹出对话框中,选择待添加的Client scope,然后单击Add。
步骤三:配置ACK托管集群的Kube API Server参数
K8s本身支持OIDC协议,对接外部Idp进行身份认证。您需要配置ACK托管集群中Kube API Server的相关OIDC参数来开启身份认证功能。
登录容器服务管理控制台,在左侧导航栏选择集群。
在集群列表页面,单击目标集群名称,然后在左侧导航栏,选择 。
在组件管理页面,在核心组件页签下找到Kube API Server组件,单击卡片右下方的配置。
在Kube API Server 参数配置页面,填写如下配置项,其他配置项保持默认,然后单击确认。
配置项
说明
oidcIssuerURLOIDC
提供商 URL
打开
https://${Keycloak 服务域名}/realms/myrealm/.well-known/openid-configuration
,将issuer对应的值填入oidcIssuerURL。本示例为:https://${Keycloak 服务域名}/realms/myrealm
。重要集群中API Server会访问oidcIssuerURL配置项对应的地址,如果您的服务域名为公网域名,请确保集群已开启公网访问能力,具体操作请参见为已有集群开启公网访问能力。
如果集群开启公网访问后,API Server仍无法访问oidcIssuerURL配置项中的地址,您可以通过
kubectl get endpoints
来检查Kubernetes后端的IP数量。如果IP大于1个,请登录Worker节点尝试访问oidcIssuerURL,并检查公网配置、安全组规则等。
如果只有1个IP,请提交工单处理。
oidcClientIdOIDC Token
客户端 ID
填写Client设置的步骤2中设置的Client ID,本示例为ack。
oidcUsernameClaim
用作用户名的 JWT claim
填写Client scopes设置的步骤5中配置的Token Claim Name的值,本示例为name,该值也为集群内的用户身份。
oidcUsernamePrefix
为用户名增加前缀
填写“-”,表示没有前缀。
oidcCAContent
请求 OIDC 提供商 URL 所需的 CA 证书,请输入 base64 后的证书
填写环境准备证书中生成的Base64编码的根证书。
步骤四:进行身份验证
使用以下命令,请求ID Token。请求完成后,可从Request body中获取ID Token。
curl -ks -X POST https://${Keycloak服务域名}/realms/myrealm/protocol/openid-connect/token \ -d grant_type=password -d client_id=ack \ -d username=myuser -d password=${已设置的用于登录Keycloak的密码} -d scope=openid \ -d client_secret=${client credential}
代码块中需替换的参数如下:
待替换内容
说明
Keycloak服务域名
替换为您已准备的可用的Keycloak服务域名。
password
替换为步骤一的步骤2.a中设置的密码。
client_secret
登录Keycloak控制台页面,在Clients页面,选择已创建的名为ack的Client,然后单击Credentials,复制Client secret的内容填入。
(可选)通过jwt-cli进行解析,可以看到
iss
即为API Server配置好的oidcIssuerURL,name
字段为上述Client scopes中步骤5设置的User Attribute。使用以下YAML示例,创建ClusterRole。
kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: keycloak-example rules: - apiGroups: [""] resources: ["namespaces"] verbs: ["get","list"] # 允许读取namespace信息。 --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: ack-crb roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: keycloak-example subjects: - kind: User name: ack apiGroup: rbac.authorization.k8s.io
使用以下命令,发送API Server请求。
curl -k https://${API server地址}/api/v1/namespaces -H "Authorization: Bearer ${id token}"
API server地址
:根据集群所处网络环境,填入API Server地址。在容器服务管理控制台的集群信息页面,在基本信息页签下,查看API Server 公网连接端点和API Server内网连接端点。id token
:为步骤1获取到的ID Token。
请求后可得到允许请求的Namespace如下。
再请求一个没有权限的资源,可看到K8s识别了用户ack,并且成功进行了权限的校验。