使用Keycloak OIDC Provider进行身份认证

本文介绍如何在ACK托管集群中部署Keycloak服务,并将Keycloak作为K8s OpenID Connect的认证服务器,实现在ACK托管集群上的身份认证。

前提条件

环境准备

  • 已准备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

  1. 在ACK集群中部署Keycloak服务。

    1. 使用以下示例代码,创建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
    2. 执行以下命令,在ACK托管集群中部署Keycloak服务,用于内网访问。

      kubectl apply -f keycloak.yaml
  2. 部署相关配置。

    1. 使用以下示例代码,创建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
    2. 执行以下命令,部署相关配置。

      kubectl apply -f keycloak-secret.yaml
    3. 使用以下示例代码,创建keylock-pki.yaml文件。

      apiVersion: v1
      data:
        tls.crt: ${服务端证书base64编码}
        tls.key: ${服务端私钥base64编码}
      kind: Secret
      metadata:
        name: keycloak-pki
        namespace: default
      type: IngressTLS
    4. 执行以下命令,部署服务端证书。

      kubectl apply -f keycloak-pki.yaml
  3. 部署Ingress。

    1. 使用以下示例代码,创建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
    2. 执行以下命令,部署Ingress。

      kubectl apply -f keycloak-ingress.yaml

      Ingress部署完成后,会产生对外的IP入口,服务域名解析会解析至该IP。

  4. 部署Keycloak Deployment。

    1. 使用以下示例代码,创建keycloak-deploy.yaml文件。

      展开查看keycloak-deploy.yaml文件

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        labels:
          app: keycloak
        name: keycloak
        namespace: default
      spec:
        selector:
          matchLabels:
            app: keycloak
        template:
          metadata:
            labels:
              app: keycloak
          spec:
            containers:
              - args:
                  - start-dev
                env:
                  - name: KEYCLOAK_ADMIN
                    valueFrom:
                      secretKeyRef:
                        key: keycloak_admin
                        name: keycloak-secret
                  - name: KEYCLOAK_ADMIN_PASSWORD
                    valueFrom:
                      secretKeyRef:
                        key: keycloak_admin_password
                        name: keycloak-secret
                  - name: KC_PROXY
                    value: edge
                  - name: KC_DB_URL_HOST
                    valueFrom:
                      secretKeyRef:
                        key: db_url
                        name: keycloak-secret
                  - name: KC_DB
                    value: mysql
                  - name: KC_DB_USERNAME
                    valueFrom:
                      secretKeyRef:
                        key: db_username
                        name: keycloak-secret
                  - name: KC_DB_PASSWORD
                    valueFrom:
                      secretKeyRef:
                        key: db_passwd
                        name: keycloak-secret
                image: 'quay.io/keycloak/keycloak:latest'
                imagePullPolicy: Always
                name: keycloak
                ports:
                  - containerPort: 8080
                    name: http
                    protocol: TCP
                readinessProbe:
                  httpGet:
                    path: /realms/master
                    port: 8080
    2. 执行以下命令,部署Keycloak Deployment。

      kubectl apply -f keycloak-deploy.yaml
  5. 在浏览器中输入https://${Keycloak服务域名}访问Keycloak服务,出现如下页面表示服务部署成功。123.png

步骤二:配置Keycloak

User设置

  1. 在Keycloak服务访问页面,单击Administration Console,输入用户名和密码登录Admin控制台。

    用户名和密码为您在步骤2.a的keycloak-secret.yaml中配置的管理员登录信息。

  2. 在页面左上角的下拉列表中,选择master,然后单击Create Realm123.png

  3. Create realm页面,输入Realm namemyrealm,然后单击Create,创建一个名为myrealm的新Realm,Realm在Keycloak中代表租户。

  4. 在页面左上角的下拉列表中,选择已创建的myrealm,在左侧导航栏选择Users,然后单击Add useruser.png

  5. Create user页面,输入Usernamemyuser,其它为可选项,设置完后,单击Create创建用户myuser。

  6. myuser页面,单击Attributes,添加用户myuser的相关属性,设置KeynameValueack,然后单击Save。该属性可以被注入到ID Token中。345.png

  7. myuser页面,单击Credentials,然后单击Set Password

  8. 设置密码时将Temporary置为On,然后单击Save,第一次登录后需修改密码。22.png

    登录Keycloak的地址为https://${Keycloak 服务域名}/realms/${用户所在realm}/account

Client设置

Client为请求Keycloak对用户进行身份验证的客户端。用户设置完成后,需完成Client的设置。

  1. myrealm页面左侧导航栏选择Clients,然后在Clients页面单击Create client

  2. Create client页面,进行General SettingsCapability configLogin settings相关设置。

    1. General Settings页面,设置Client IDName,本例均设置为ack,然后单击Nextgeneral.png

    2. Capability config页面,设置Client authentication为on,表示设置Client的access type为confidential,其他选项保持默认,然后单击Next123.png

    3. Login settings页面,设置Valid redirect URIshttp://* ,用于设置浏览器登录成功后有效的重定向URL,本例的http://* 表示匹配所有HTTP重定向的网址。然后单击Saveimage.png

Client scopes设置

Client scopes是在多个客户端之间共享的一组通用协议Mapper和Role。

  1. myrealm页面左侧导航栏选择Client scopes,然后在Client scopes页面单击Create client scope

  2. Create client scope页面,设置Nameack-kubernetes,其他配置项保持默认,然后单击Saveimage.png

  3. ack-kubernetes页面,单击Mappers,然后单击Configure a new mapper

  4. Configure a new mapper页面,创建一个User Attribute,用于把用户属性注入到ID Token 中,即上文步骤6添加的nameack字段。image.png

  5. Add mapper页面,设置NamenameUser Attributename(即选择上文设置的name: ack属性),Token Claim Namename(表示该属性在Token中的命名)。其他配置项保持默认。然后单击Saveimage.png

  6. 在左侧导航栏,单击Clients返回Clients页面,然后选择名为ack的Client。

  7. 在ack的client页面,单击Client scopes,然后单击Add client scope,在弹出对话框中,选择待添加的Client scope,然后单击Addimage.png

步骤三:配置ACK托管集群的Kube API Server参数

K8s本身支持OIDC协议,对接外部Idp进行身份认证。您需要配置ACK托管集群中Kube API Server的相关OIDC参数来开启身份认证功能。

  1. 登录容器服务管理控制台,在左侧导航栏选择集群

  2. 集群列表页面,单击目标集群名称,然后在左侧导航栏,选择运维管理 > 组件管理

  3. 组件管理页面,在核心组件页签下找到Kube API Server组件,单击卡片右下方的配置

  4. Kube API Server 参数配置页面,填写如下配置项,其他配置项保持默认,然后单击确认API server.png

    配置项

    说明

    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编码的根证书。

步骤四:进行身份验证

  1. 使用以下命令,请求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的内容填入。

    safe.png

  2. (可选)通过jwt-cli进行解析,可以看到iss即为API Server配置好的oidcIssuerURL,name字段为上述Client scopes中步骤5设置的User Attribute。

  3. 使用以下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
  4. 使用以下命令,发送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如下。image.png

    再请求一个没有权限的资源,可看到K8s识别了用户ack,并且成功进行了权限的校验。image.png