更新时间:2024-11-12 GMT+08:00

参考:Jenkins对接Kubernetes集群的RBAC

前提条件

集群需要开启RBAC。

场景一:基于namespace的权限控制

新建ServiceAccount和role,然后做rolebinding

# kubectl create ns dev
# kubectl -n dev create sa dev

# cat <<EOF > dev-user-role.yml
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: dev
  name: dev-user-pod
rules:
- apiGroups: ["*"]
  resources: ["deployments", "pods", "pods/log"]
  verbs: ["get", "watch", "list", "update", "create", "delete"]
EOF
# kubectl create -f dev-user-role.yml

# kubectl create rolebinding dev-view-pod \
    --role=dev-user-pod \
    --serviceaccount=dev:dev \
    --namespace=dev

生成指定ServiceAccount的kubeconfig文件

  • 1.21以前版本的集群中,Pod中获取Token的形式是通过挂载ServiceAccount的Secret来获取Token,这种方式获得的Token是永久的。该方式在1.21及以上的版本中不再推荐使用,并且根据社区版本迭代策略,在1.25及以上版本的集群中,ServiceAccount将不会自动创建对应的Secret。

    1.21及以上版本的集群中,直接使用TokenRequest API获得Token,并使用投射卷(Projected Volume)挂载到Pod中。使用这种方法获得的Token具有固定的生命周期,并且当挂载的Pod被删除时这些Token将自动失效。详情请参见Token安全性提升说明

  • 如果您在业务中需要一个永不过期的Token,您也可以选择手动管理ServiceAccount的Secret。尽管存在手动创建永久ServiceAccount Token的机制,但还是推荐使用TokenRequest的方式使用短期的Token,以提高安全性。

由于本案例使用的集群版本为v1.25,ServiceAccount不会自动创建对应的Secret。下面手动创建名为dev-secret的Secret,并与名为dev的ServiceAccount关联。

手动创建的Secret要与待关联的ServiceAccount处于同一命名空间,否则可能创建失败。

# kubectl apply -f - <<EOF
apiVersion: v1 
kind: Secret 
metadata:
  namespace: dev  # 此处表示命名空间   
  name: dev-secret   
  annotations:     
    kubernetes.io/service-account.name: dev 
type: kubernetes.io/service-account-token 
EOF

检查dev-secret是否创建成功。若命名空间dev的Secrets中出现dev-secret,则说明创建成功。

# kubectl get secrets -n dev
NAME               TYPE                                  DATA        AGE
default-secret     kubernetes.io/dockerconfigjson           1      2d22h
dev-secret       kubernetes.io/service-account-token      3      4h14m
paas.elb           cfe/secure-opaque                        1      2d22h

利用dev-secret生成kubeconfig文件。

# API_SERVER="https://172.22.132.51:6443"
# CA_CERT=$(kubectl -n dev get secret dev-secret -o yaml | awk '/ca.crt:/{print $2}')
# cat <<EOF > dev.conf
apiVersion: v1
kind: Config
clusters:
- cluster:
    certificate-authority-data: $CA_CERT
    server: $API_SERVER
  name: cluster
EOF

# TOKEN=$(kubectl -n dev get secret dev-secret -o go-template='{{.data.token}}')
# kubectl config set-credentials dev-user \
    --token=`echo ${TOKEN} | base64 -d` \
    --kubeconfig=dev.conf

# kubectl config set-context default \
    --cluster=cluster \
    --user=dev-user \
    --kubeconfig=dev.conf

# kubectl config use-context default \
    --kubeconfig=dev.conf

利用以下命令行进行验证。

# kubectl --kubeconfig=dev.conf get po
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:dev:dev" cannot list pods in the namespace "default"

# kubectl -n dev --kubeconfig=dev.conf run nginx --image nginx --port 80 --restart=Never
# kubectl -n dev --kubeconfig=dev.conf get po
NAME      READY     STATUS    RESTARTS   AGE
nginx     1/1       Running   0          39s

Jenkins中验证权限是否符合预期

  1. 添加有权限控制的kubeconfig到Jenkins系统中

  2. 启动Jenkins任务,部署到namespace default失败,部署到namespace dev成功。

场景二:基于具体资源的权限控制

  1. 生成SA和role及绑定:

    # kubectl -n dev create sa sa-test0304
    
    # cat <<EOF > test0304-role.yml
    kind: Role
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      namespace: dev
      name: role-test0304
    rules:
    - apiGroups: ["*"]
      resources: ["deployments"]
      resourceNames: ["tomcat03", "tomcat04"]
      verbs: ["get", "update", "patch"]
    EOF
    # kubectl create -f test0304-role.yml
    
    # kubectl create rolebinding test0304-bind \
        --role=role-test0304 \
        --serviceaccount=dev:sa-test0304\
        --namespace=dev

  2. 生成kubeconfig文件:

    • 1.21以前版本的集群中,Pod中获取Token的形式是通过挂载ServiceAccount的Secret来获取Token,这种方式获得的Token是永久的。该方式在1.21及以上的版本中不再推荐使用,并且根据社区版本迭代策略,在1.25及以上版本的集群中,ServiceAccount将不会自动创建对应的Secret。

      1.21及以上版本的集群中,直接使用TokenRequest API获得Token,并使用投射卷(Projected Volume)挂载到Pod中。使用这种方法获得的Token具有固定的生命周期,并且当挂载的Pod被删除时这些Token将自动失效。详情请参见Token安全性提升说明

    • 如果您在业务中需要一个永不过期的Token,您也可以选择手动管理ServiceAccount的Secret。尽管存在手动创建永久ServiceAccount Token的机制,但还是推荐使用TokenRequest的方式使用短期的Token,以提高安全性。

    由于本案例使用的集群版本为v1.25,ServiceAccount不会自动创建对应的Secret。下面手动创建名为test-secret的Secret,并与sa-test0304关联。

    # kubectl apply -f - <<EOF
    apiVersion: v1 
    kind: Secret 
    metadata:
      namespace: dev   
      name: test-secret   
      annotations:     
        kubernetes.io/service-account.name: sa-test0304 
    type: kubernetes.io/service-account-token 
    EOF

    检查test-secret是否创建成功。若命名空间dev的Secrets中出现test-secret,则说明创建成功。

    # kubectl get secrets -n dev
    NAME               TYPE                                  DATA        AGE
    default-secret     kubernetes.io/dockerconfigjson           1      2d22h
    dev-secret         kubernetes.io/service-account-token      3      4h14m
    paas.elb           cfe/secure-opaque                        1      2d22h
    test-secret       kubernetes.io/service-account-token      3      25m

    利用test-secret生成kubeconfig文件。

    # API_SERVER=" https://192.168.0.153:5443"
    # CA_CERT=$(kubectl -n dev get secret test-secret -o yaml | awk '/ca.crt:/{print $2}')
    # cat <<EOF > test0304.conf
    apiVersion: v1
    kind: Config
    clusters:
    - cluster:
        certificate-authority-data: $CA_CERT
        server: $API_SERVER
      name: cluster
    EOF
    
    # TOKEN=$(kubectl -n dev get secret test-secret -o go-template='{{.data.token}}')
    # kubectl config set-credentials test0304-user \
        --token=`echo ${TOKEN} | base64 -d` \
        --kubeconfig=test0304.conf
    
    # kubectl config set-context default \
        --cluster=cluster \
        --user=test0304-user \
        --kubeconfig=test0304.conf
    
    # kubectl config use-context default \
        --kubeconfig=test0304.conf

  3. Jenkins中的运行效果符合预期。

    Pipeline脚本,依次更新tomcat03/04/05的deployment。

           try { 
             kubernetesDeploy(
                 kubeconfigId: "test0304",
                 configs: "test03.yaml")
             println "hooray, success"
            } catch (e) {
                println "oh no! Deployment failed! "
                println e
            }
            echo "test04"
            try { 
             kubernetesDeploy(
                 kubeconfigId: "test0304",
                 configs: "test04.yaml")
             println "hooray, success"
            } catch (e) {
                println "oh no! Deployment failed! "
                println e
            }
            echo "test05"
            try { 
             kubernetesDeploy(
                 kubeconfigId: "test0304",
                 configs: "test05.yaml")
             println "hooray, success"
            } catch (e) {
                println "oh no! Deployment failed! "
                println e
            }

    查看运行效果:

    图1 test03
    图2 test04