
# 参考：Jenkins对接Kubernetes集群的RBAC
#### 前提条件
集群需要开启RBAC。
#### 场景一：基于namespace的权限控制
**新建ServiceAccount和Role，然后定义一个RoleBinding，将ServiceAccount绑定到Role**
```
# 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文件**
![](https://support.huaweicloud.com/bestpractice-cce/public_sys-resources/note_3.0-zh-cn.png)
- 1.21以前版本的集群中，Pod中获取Token的形式是通过挂载ServiceAccount的Secret来获取Token，这种方式获得的Token是永久的。该方式在1.21及以上的版本中不再推荐使用，并且根据社区版本迭代策略，在1.25及以上版本的集群中，ServiceAccount将不会自动创建对应的Secret。 1.21及以上版本的集群中，直接使用[TokenRequest](https://kubernetes.io/zh-cn/docs/reference/kubernetes-api/authentication-resources/token-request-v1/) API[获得Token](https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/service-accounts-admin/#bound-service-account-token-volume)，并使用投射卷（Projected Volume）挂载到Pod中。使用这种方法获得的Token具有固定的生命周期，并且当挂载的Pod被删除时这些Token将自动失效。详情请参见[Token安全性提升说明](https://support.huaweicloud.com/usermanual-cce/cce_10_0477.html)。
  
- 如果您在业务中需要一个永不过期的Token，您也可以选择[手动管理ServiceAccount的Secret](https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/service-accounts-admin/#manual-secret-management-for-serviceaccounts)。尽管存在手动创建永久ServiceAccount Token的机制，但还是推荐使用[TokenRequest](https://kubernetes.io/zh-cn/docs/reference/kubernetes-api/authentication-resources/token-request-v1/)的方式使用短期的Token，以提高安全性。
 
由于本案例使用的集群版本为v1.25，ServiceAccount不会自动创建对应的Secret。下面手动创建名为dev-secret的Secret，并与名为dev的ServiceAccount关联。
![](https://support.huaweicloud.com/bestpractice-cce/public_sys-resources/notice_3.0-zh-cn.png)
手动创建的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. 在Jenkins Dashboard页面单击左侧"Manage Jenkins"，选择"Security \> Manage Credentials"中，单击Jenkins默认的"global"凭据存储域，您也可以自行新建域。 
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000002149826948.png "点击放大")
   
   
2. 单击"Add Credentials"，创建新的凭据。"Kind"选择"Secret file"，"Scope"选择"Global (Jenkins, nodes, items, all child items, etc)"，"File"选择下载的kubeconfig配置文件，其他参数保持默认，单击"Create"。
3. 启动Jenkins任务，部署到namespace default失败，部署到namespace dev成功。 
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0266402292.png "点击放大")
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0266402293.png "点击放大")
   
   
 
#### 场景二：基于具体资源的权限控制
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文件： 
   ![](https://support.huaweicloud.com/bestpractice-cce/public_sys-resources/note_3.0-zh-cn.png)
   - 1.21以前版本的集群中，Pod中获取Token的形式是通过挂载ServiceAccount的Secret来获取Token，这种方式获得的Token是永久的。该方式在1.21及以上的版本中不再推荐使用，并且根据社区版本迭代策略，在1.25及以上版本的集群中，ServiceAccount将不会自动创建对应的Secret。 1.21及以上版本的集群中，直接使用[TokenRequest](https://kubernetes.io/zh-cn/docs/reference/kubernetes-api/authentication-resources/token-request-v1/) API[获得Token](https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/service-accounts-admin/#bound-service-account-token-volume)，并使用投射卷（Projected Volume）挂载到Pod中。使用这种方法获得的Token具有固定的生命周期，并且当挂载的Pod被删除时这些Token将自动失效。详情请参见[Token安全性提升说明](https://support.huaweicloud.com/usermanual-cce/cce_10_0477.html)。
     
   
   - 如果您在业务中需要一个永不过期的Token，您也可以选择[手动管理ServiceAccount的Secret](https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/service-accounts-admin/#manual-secret-management-for-serviceaccounts)。尽管存在手动创建永久ServiceAccount Token的机制，但还是推荐使用[TokenRequest](https://kubernetes.io/zh-cn/docs/reference/kubernetes-api/authentication-resources/token-request-v1/)的方式使用短期的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
           }
   ```
   查看运行效果：
   图1test03   
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0266405132.png "点击放大")
   图2test04   
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0266405133.png "点击放大")
   
   
 
