身份与权限控制

Kubernetes中提供了良好的多租户认证管理机制,如RBAC、ServiceAccount还有各种Policy等。

一. Kubernetes API 请求过程

首先看一下请求的发起,请求的发起分为两个部分:

  • 第一个部分是人机交互的过程。 是大家非常熟悉的用 kubectl 对 api-server 的一个请求过程;
  • 第二个部分是 Pod 中的业务逻辑与 api-server 之间的交互。

当我们的 api-server 收到请求后,就会开启访问控制流程。这里面分为三个步骤:

  • Authentication 认证阶段:判断请求用户是否为能够访问集群的合法用户。如果用户是个非法用户,那 api-server 会返回一个 401 的状态码,并终止该请求;
  • 如果用户合法的话,我们的 api-server 会进入到访问控制的第二阶段 Authorization:鉴权阶段。在该阶段中 api-server 会判断用户是否有权限进行请求中的操作。如果无权进行操作,api-server 会返回 403 的状态码,并同样终止该请求;
  • 如果用户有权进行该操作的话,访问控制会进入到第三个阶段:AdmissionControl。在该阶段中 api-server 的 Admission Control 会判断请求是否是一个安全合规的请求。如果最终验证通过的话,访问控制流程才会结束。

此时我们的请求将会转换为一个 Kubernetes objects 相应的变更请求,最终持久化到 ETCD 中。

二. Kubernetes 认证

Kubernetes 中的用户模型

对于认证来说,首先我们要确定请求的发起方是谁。并最终通过认证过程将其转换为一个系统可识别的用户模型用于后期的鉴权,那么先来看一下 Kubernetes 中的用户模型。

1. Kubernetes 没有自身的用户管理能力

什么是用户管理能力呢?我们无法像操作 Pod 一样,通过 API 的方式创建删除一个用户实例。同时我们也无法在 ETCD 中找到用户对应的存储对象。

2. Kubernetes 中的用户通常是通过请求凭证设置

在 Kubernetes 的访问控制流程中用户模型是如何产生的呢?答案就在请求方的访问控制凭证中,也就是我们平时使用的 kube-config 中的证书,或者是 Pod 中引入的 Server Account。经过 Kubernetes 认证流程之后,api-server 会将请求中凭证中的用户身份转化为对应的 User 和 Groups 这样的用户模型。在随后的鉴权操作和审计操作流程中,api-server 都会使用到改用户模型实例。

3. Kubernetes支持的请求认证方式主要包括:

Basic 认证

该认证方式下,管理员会将 Username 和 Password 组成的白名单放置在 api-server 读取的静态配置文件上面进行认证,该方式一般用于测试场景,在安全方面是不推荐且不可拓展的一种方式。

X509 证书认证

该方式是 api-server 中相对应用较多的使用方式,首先访问者会使用由集群 CA 签发的,或是添加在 api-server Client CA 中授信 CA 签发的客户端证书去访问 api-server。api-server 服务端在接收到请求后,会进行 TLS 的握手流程。除了验证证书的合法性,api-server 还会校验客户端证书的请求源地址等信息。开启双向认证,X509 认证是一个比较安全的方式,也是 Kubernetes 组件之间默认使用的认证方式,同时也是 kubectl 客户端对应的 kube-config 中经常使用到的访问凭证。

Bearer Tokens(JSON Web Tokens)
  • Service Account
  • OpenID Connect
  • Webhooks

该方式的 Tokens 是通用的 JWT 的形式,其中包含了签发者、用户的身份、过期时间等多种元信息。它的认证方式也是常见的私钥加签,公钥验签的一个基本流程。基于 Token 的认证使用场景也很广泛,比如 Kubernetes Pod 应用中经常使用到的 Service Account,其中就会自动绑定一个签名后的 JWT Token 用于请求 api-server。

另外 api-server 还支持基于 OpenID 协议的 Token 认证,可以通过对 api-server 的配置连接一个指定的外部 IDP,同时可以通过 Keycloak,Dex 这样的开源服务来管理 IDP,请求者可以按照自己熟悉的方式在原身份认证服务上进行登录认证,并最终返回一个相应的 JWT token,为了后面的 api-server 的鉴权流程。

除此之外,还可以使用 Webhooks 的方式,将请求的 Token 发送到指定外部服务进行 Token 的验签。

三. X509 证书认证

对于一个集群证书体系来说,认证机构 (CA) 是一个非常重要的证书对。它会被默认放置在集群 Master 节点上的 /etc/Kubernetes/pki/ 目录下。集群中所有组件之间的通讯用到的证书,其实都是由集群根 CA 来签发的。在证书中有两个身份凭证相关的重要字段:一个是 CN,一个是 O。

  • CA 公钥: /etc/kubernetes/pki/ca.crt
  • CA 私钥:/etc/kubernetes/pki/ca.key
  • Comman Name(CN):apiserver在认证过程中将其作为用户(user)
  • Organization(O):apiserver在认证过程中将其作为组(group)

可以通过openssl进行证书的解析:

[root@k8s-master pki]# openssl x509 -in ca.crt -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 0 (0x0)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=kubernetes
        Validity
            Not Before: Mar 13 09:43:32 2020 GMT
            Not After : Mar 11 09:43:32 2030 GMT
        Subject: CN=kubernetes    ##将其作为用户
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
    .....略

3.1 证书签发API

  • Kubernetes提供了证书签发的API:certificates.k8s.io/v1beta1
  • 客户端将证书的签发请求发送到API Server
  • 签发请求会以csr资源模型的形式持久化
  • 新创建好的csr模型会保持在pending的状态,直到有权限的管理员对其approve
  • 一旦csr完成approved,请求对应的证书即被签发

根据csr证书请求文件生成一个CertificateSigningRequest

[root@k8s-master rbac-test]# cat << EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
  name: lisi.devs
spec:
  request: $(cat lisi.csr | base64 | tr -d '\n')
  usages:
  - digital signature
  - key encipherment
  - server auth
EOF

查看这个csr并颁发

[root@k8s-master rbac-test]# kubectl get csr
NAME        AGE     REQUESTOR          CONDITION
lisi.devs   2m14s   kubernetes-admin   Pending

[root@k8s-master rbac-test]# kubectl certificate approve lisi.devs
certificatesigningrequest.certificates.k8s.io/lisi.devs approved
[root@k8s-master rbac-test]# kubectl get csr
NAME        AGE     REQUESTOR          CONDITION
lisi.devs   9m33s   kubernetes-admin   Approved,Issued

##保存成.crt证书文件
[root@k8s-master rbac-test]# kubectl get csr lisi.devs -o jsonpath='{.status.certificate}'\ | base64 --decode > lisi.crt

3.2 签发用户证书

首先开发人员需用通过 openssl 等证书工具生成私钥,然后创建对应的 X509 csr 请求文件,需要在 sbuj 字段中指定用户 user 和组 group,最后通过 API 创建 K8s csr 实例并等待管理员审批。

对于集群管理员,他可以直接读取集群根 CA,并通过 X509 的 csr 请求文件签发证书,所以它无需定义或审批 csr 实例。下面有一个 openssl 签发示例,命令中需要指明 csr 和 ca.crt 的文件路径,以及签发证书的过期时间信息。

另外各个云厂商也会基于登录用户和目标集群直接生成访问凭证,一键化流程,方便用户的使用。

开发人员:

  • 生成私钥(可借助openssl等证书工具)
# openssl genrsa -out mike.key 2048
  • 生成证书请求csr,用户为mike,组为devs
[root@k8s-master rbac-test]# openssl req -new -key mike.key -out mike.csr -subj "/CN=mike/O=devs"
[root@k8s-master rbac-test]# ls
mike.csr  mike.key
  • 通过API创建K8s csr实例并等待管理员的审批

集群管理员:

  • 基于csr文件或实例通过集群ca keypari签发证书,下面是openssl签发示例:
[root@k8s-master rbac-test]# openssl x509 -req -in mike.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key \
> -CAcreateserial -out mike.crt -days 365
Signature ok
subject=/CN=mike/O=devs
Getting CA Private Key
[root@k8s-master rbac-test]# ls mike.crt 
mike.crt

四. Service Account

除了证书认证之外,Service Account 也是 api-server 中应用比较广泛的一种方式。

  • Service Account是Kubernetes中唯一能够通过API方式管理的API Server访问凭证
  • 通常用于Pod中的业务进程与API Server的交互
  • 当一个namespace创建完成后,会同时在该namespace下生成名为default的一个Service Account和对应的secret实例
  • 同样用户也可以通过API创建其他名称的Service Account,并在该namespace下挂载到运行时刻的Pod中。
  • 每个Pod在创建后都会自动设置spec.serviceAccount为default(除非指定了其他ServiceAccout)
  • 每个container启动后都会挂载该service account的token和ca.crt到/var/run/secrets/kubernetes.io/serviceaccount/

4.1 验证一下Service Account

先建立一个名称空间,会自动创建一个default及secret
[root@k8s-master rbac-test]# kubectl create namespace liqingru
namespace/liqingru created
[root@k8s-master rbac-test]# kubectl get sa -n liqingru
NAME      SECRETS   AGE
default   1         7s
[root@k8s-master rbac-test]# kubectl get secret -n liqingru
NAME                  TYPE                                  DATA   AGE
default-token-8l8vn   kubernetes.io/service-account-token   3      20s
[root@k8s-master rbac-test]# kubectl get sa  default -n liqingru -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: "2020-04-07T15:29:54Z"
  name: default
  namespace: liqingru
  resourceVersion: "617738"
  selfLink: /api/v1/namespaces/liqingru/serviceaccounts/default
  uid: 2a9bfae2-3983-475b-a806-188fca51da71
secrets:
- name: default-token-8l8vn
[root@k8s-master rbac-test]# kubectl get secret -n liqingru default-token-8l8vn -o yaml
apiVersion: v1
data:
  ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJd01ETXhNekE1TkRNek1sb1hEVE13TURNeE1UQTVORE16TWxvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTUQ1ClZmeE04ckVMMFdkUnYxaVlrc21ZMGdnSU9qdUVna2U2Sy8yWGFCeVB2TEUwQWZDZ0lYUnhIOUlxSk5PNk42djcKWlVtWXg4bGRBZmF5ZE9weEpmcmhqSlZaWTVqdGdGT0Y1THJnTVl4UjNWZkN5THd3YlcvUmc1NjlkanBsMFlEcApjQlg3Vk1PT3R5RDVNSHZ6eGRrNDhQSzQ0cnhheG5oRkVLV25CNHBXVCttNUFHbkVZcCtqNmc1QnVCdmpxdHFtCnR2MWlqeWRuVlliUVF2QVljMElicVgvUWlxQlY3c2hwbktWejRvRjUzNVRMYS9ORS9VOENwaVk2cmdOUkgwVlgKRm5EYzRuRzFrS080ZEQ2ZHRWaEYySW40NVVhaFpINll3VlRxVloxSURla2JVNUZVcGo1QnJ1YXpaZVdQUVh4Lwo2bnFXQXE1UjRFdVdsbHYyYzZVQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFLZmlZN2tybjdlSXRPZjFZUWRaeTJTczFkb3kKUmh1aS94MHNQQW5kalp0MDkwd1VSY0hlQ1d2bVoyWFRobUgwVWpZblB3SWFRWS9DQlhmc0lpNS9oMi9uU3lJMApTMzA4akhRNU5VWXNwYXh1YS9kYzZJSkJ4V3hyb2NxZmxDS2pGSkFzWkF0OTlCaG5LaHBiLzlMUU8vT0pra2pSClBPcTNndEx5Z0pSaHg0Ukc5TWNLeWl4VHJ4SkE1T0JLdDh5aTlUTkhqSnBIMnE2QlVOYUc3NWVIT1JSdXZRUzMKZGZZZ2ZQa1o4U0RRd3NIU3BabSsweEFoVEpCVEVZM0tXSEZXcEZWY3g3V1FOV3dpbVhjL25PNWRpNlBTeWFCNQpQaUJXeUxKRk9ZQjBSeGM0ZWsrNlZIMktPNTc2SDFYRHdBU1l1VW5weVJXS2c5N1NNdUV0dXFSRVFrND0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
  namespace: bGlxaW5ncnU=
  token: ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNklsRlJiR2hvV1VoVlpsSjBSVWgzVGtodGNrTnJWVVV3YzAxd1dqTmlNek41Tm5oMVkycFdhM2xMVlVraWZRLmV5SnBjM01pT2lKcmRXSmxjbTVsZEdWekwzTmxjblpwWTJWaFkyTnZkVzUwSWl3aWEzVmlaWEp1WlhSbGN5NXBieTl6WlhKMmFXTmxZV05qYjNWdWRDOXVZVzFsYzNCaFkyVWlPaUpzYVhGcGJtZHlkU0lzSW10MVltVnlibVYwWlhNdWFXOHZjMlZ5ZG1salpXRmpZMjkxYm5RdmMyVmpjbVYwTG01aGJXVWlPaUprWldaaGRXeDBMWFJ2YTJWdUxUaHNPSFp1SWl3aWEzVmlaWEp1WlhSbGN5NXBieTl6WlhKMmFXTmxZV05qYjNWdWRDOXpaWEoyYVdObExXRmpZMjkxYm5RdWJtRnRaU0k2SW1SbFptRjFiSFFpTENKcmRXSmxjbTVsZEdWekxtbHZMM05sY25acFkyVmhZMk52ZFc1MEwzTmxjblpwWTJVdFlXTmpiM1Z1ZEM1MWFXUWlPaUl5WVRsaVptRmxNaTB6T1RnekxUUTNOV0l0WVRnd05pMHhPRGhtWTJFMU1XUmhOekVpTENKemRXSWlPaUp6ZVhOMFpXMDZjMlZ5ZG1salpXRmpZMjkxYm5RNmJHbHhhVzVuY25VNlpHVm1ZWFZzZENKOS5Lb3hVYWFLZ29YRGVBSUotemVvZ21uVjVnNktWSmxFQ2UtZ3NLSTJaTWdPZ2FRcEU2UHM4SnVXM2pBNHF5XzByZk5Za19Jd01Va3BBYW92N3VlanEtajZBYmNreTN5RllpRzRjWmNDWHVVMWZtR3BCemFWNWZPTVBBU0RkRmdnbjktT0U2TEZLNEd1eExaSXhzSFVteUZEM180WF9jM0x2MUZnWEoxT2UyRWVPOTYyNEUzY2daNjZLck5aWUFGdVB6ems2bmlhNEkwcUZYbl9ZbDdCNzZvQ1pNQ1ZZYlJ2VXp1djVFZjR6ZWdyUlhOZjVyWkREMWFOdHg0enF1aURFRlVCWnVvekxwc3IwdXRBd19yRTJDN09Td0ZlQnYtZmVGX0lEUFM4OTl2MkUxQW5GM2tyUVBiMWRiSndCWTgtY3B2cEZlc1Z0NEZhQ1lEdjJCeXBLM2c=
kind: Secret
metadata:
  annotations:
    kubernetes.io/service-account.name: default
    kubernetes.io/service-account.uid: 2a9bfae2-3983-475b-a806-188fca51da71
  creationTimestamp: "2020-04-07T15:29:54Z"
  name: default-token-8l8vn
  namespace: liqingru
  resourceVersion: "617737"
  selfLink: /api/v1/namespaces/liqingru/secrets/default-token-8l8vn
  uid: 66f92a25-f65d-4f2a-af60-7691bf1fd508
type: kubernetes.io/service-account-token
在新namespace下创建一个Pod
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod-test
  namespace: liqingru
  labels:
    app: nginx-pod-test
spec:
  containers:
  - name: nginx-pod-test
    image: nginx:1.7.9
    ports:
    - containerPort: 80
      name: www

执行命令创建并查看

[root@k8s-master k8s-yaml]# kubectl apply -f nginx-pod.yml 
pod/nginx-pod-test created
[root@k8s-master k8s-yaml]# kubectl get pod -n liqingru
NAME             READY   STATUS    RESTARTS   AGE
nginx-pod-test   1/1     Running   0          15s

[root@k8s-master k8s-yaml]# kubectl get pod nginx-pod-test -n liqingru -o yaml
.....
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: default-token-8l8vn
      readOnly: true
......
  volumes:
  - name: default-token-8l8vn
    secret:
      defaultMode: 420
      secretName: default-token-8l8vn
......

##名称空间新建的pod如果不指定sa,会自动挂载当前名称空间中默认的sa(default)

4.2 创建 Service Account

可以使用命令直接创建:

#kubectl create serviceaccount build-robot

也可以编辑yaml文件

apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-robot

创建成功后,系统会自动为其创建一个secret,为其token认证信息

[root@k8s-master rbac-test]# kubectl get sa
NAME                     SECRETS   AGE
build-robot              1         7s
default                  1         27d

[root@k8s-master rbac-test]# kubectl get sa build-robot -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"name":"build-robot","namespace":"default"}}
  creationTimestamp: "2020-04-09T15:06:38Z"
  name: build-robot
  namespace: default
  resourceVersion: "742272"
  selfLink: /api/v1/namespaces/default/serviceaccounts/build-robot
  uid: fa53e7af-8838-4cb7-a7f0-eb92870348cd
secrets:
- name: build-robot-token-z2zgt

可以查看器token信息

[root@k8s-master rbac-test]# kubectl get secret build-robot-token-z2zgt -o yaml
apiVersion: v1
data:
  ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJd01ETXhNekE1TkRNek1sb1hEVE13TURNeE1UQTVORE16TWxvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTUQ1ClZmeE04ckVMMFdkUnYxaVlrc21ZMGdnSU9qdUVna2U2Sy8yWGFCeVB2TEUwQWZDZ0lYUnhIOUlxSk5PNk42djcKWlVtWXg4bGRBZmF5ZE9weEpmcmhqSlZaWTVqdGdGT0Y1THJnTVl4UjNWZkN5THd3YlcvUmc1NjlkanBsMFlEcApjQlg3Vk1PT3R5RDVNSHZ6eGRrNDhQSzQ0cnhheG5oRkVLV25CNHBXVCttNUFHbkVZcCtqNmc1QnVCdmpxdHFtCnR2MWlqeWRuVlliUVF2QVljMElicVgvUWlxQlY3c2hwbktWejRvRjUzNVRMYS9ORS9VOENwaVk2cmdOUkgwVlgKRm5EYzRuRzFrS080ZEQ2ZHRWaEYySW40NVVhaFpINll3VlRxVloxSURla2JVNUZVcGo1QnJ1YXpaZVdQUVh4Lwo2bnFXQXE1UjRFdVdsbHYyYzZVQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFLZmlZN2tybjdlSXRPZjFZUWRaeTJTczFkb3kKUmh1aS94MHNQQW5kalp0MDkwd1VSY0hlQ1d2bVoyWFRobUgwVWpZblB3SWFRWS9DQlhmc0lpNS9oMi9uU3lJMApTMzA4akhRNU5VWXNwYXh1YS9kYzZJSkJ4V3hyb2NxZmxDS2pGSkFzWkF0OTlCaG5LaHBiLzlMUU8vT0pra2pSClBPcTNndEx5Z0pSaHg0Ukc5TWNLeWl4VHJ4SkE1T0JLdDh5aTlUTkhqSnBIMnE2QlVOYUc3NWVIT1JSdXZRUzMKZGZZZ2ZQa1o4U0RRd3NIU3BabSsweEFoVEpCVEVZM0tXSEZXcEZWY3g3V1FOV3dpbVhjL25PNWRpNlBTeWFCNQpQaUJXeUxKRk9ZQjBSeGM0ZWsrNlZIMktPNTc2SDFYRHdBU1l1VW5weVJXS2c5N1NNdUV0dXFSRVFrND0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
  namespace: ZGVmYXVsdA==
  token: ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNklsRlJiR2hvV1VoVlpsSjBSVWgzVGtodGNrTnJWVVV3YzAxd1dqTmlNek41Tm5oMVkycFdhM2xMVlVraWZRLmV5SnBjM01pT2lKcmRXSmxjbTVsZEdWekwzTmxjblpwWTJWaFkyTnZkVzUwSWl3aWEzVmlaWEp1WlhSbGN5NXBieTl6WlhKMmFXTmxZV05qYjNWdWRDOXVZVzFsYzNCaFkyVWlPaUprWldaaGRXeDBJaXdpYTNWaVpYSnVaWFJsY3k1cGJ5OXpaWEoyYVdObFlXTmpiM1Z1ZEM5elpXTnlaWFF1Ym1GdFpTSTZJbUoxYVd4a0xYSnZZbTkwTFhSdmEyVnVMWG95ZW1kMElpd2lhM1ZpWlhKdVpYUmxjeTVwYnk5elpYSjJhV05sWVdOamIzVnVkQzl6WlhKMmFXTmxMV0ZqWTI5MWJuUXVibUZ0WlNJNkltSjFhV3hrTFhKdlltOTBJaXdpYTNWaVpYSnVaWFJsY3k1cGJ5OXpaWEoyYVdObFlXTmpiM1Z1ZEM5elpYSjJhV05sTFdGalkyOTFiblF1ZFdsa0lqb2labUUxTTJVM1lXWXRPRGd6T0MwMFkySTNMV0UzWmpBdFpXSTVNamczTURNME9HTmtJaXdpYzNWaUlqb2ljM2x6ZEdWdE9uTmxjblpwWTJWaFkyTnZkVzUwT21SbFptRjFiSFE2WW5WcGJHUXRjbTlpYjNRaWZRLk9mdWhJbDV3RkQydWxwQVlkVDVNVXlUSWlNbXdPNUhFTnN5a2pjRFdGM1hzc0tuZFUyeHlvbjAyMlAtTFF4Sm9MU3JqRmQ1RExsRnJHVnpZQ3FLUXhsY1BCWUw0a1U4U1NrYTNRNXNCLXpoNXZJSXQ4SVFETEg0bHpvU3ZwXzhVOVltUldHVmlMSGRPSWNrd2ltWXV3RjlHWS0xcDRnTG5ueGlZNXlvZVpzQ1BTcWtDQWtFOGNJd3QwakxWQzYxVXVvN0FpMHVka0g2MTFhRktfWndoSDBoMTI4RFUtRGVqT0ZXOVE5NG9LVXpUMDFYNDU0eEhMOVBYV2NOeUxLQmtfdkFGVkJZNkN3a3RzbHRDdzJPZnlvdkNFeEtMTVI0Z3cyYnFvRjlvMEFPWVdsSi10NF9qMlVIclNnY3JqQXEydkVuRkpNX0FWYm5vdkQ0UkFEdmpyUQ==
kind: Secret
metadata:
  annotations:
    kubernetes.io/service-account.name: build-robot
    kubernetes.io/service-account.uid: fa53e7af-8838-4cb7-a7f0-eb92870348cd
  creationTimestamp: "2020-04-09T15:06:38Z"
  name: build-robot-token-z2zgt
  namespace: default
  resourceVersion: "742271"
  selfLink: /api/v1/namespaces/default/secrets/build-robot-token-z2zgt
  uid: 1ab67e06-18d7-447f-9076-cdf3652e5cc1
type: kubernetes.io/service-account-token

可以在Pod中的spec.serviceAccountName指定刚创建的sa,而不是使用默认的default。

五. kubeconfig

集群交互的时候需要身份认证,使用kubeconfig和token两种认证方式是最简单也最通用的认证方式,下面我们使用kubeconfing来进行认证。

使用kubeconfig文件来组织关于集群、用户、名称空间和身份验证机制的信息。

使用 kubectl命令行工具对kubeconfig文件来查找选择群集并与群集的API服务器进行通信所需的信息。

默认情况下 kubectl使用的配置文件名称是在$HOME/.kube目录下config文件,可以通过设置环境变量KUBECONFIG或者–kubeconfig指定其他的配置文件。

5.1 查看系统的kubeconfig

我们之所以能够在K8s集群中创建各种资源,做很多能做的事情,是因为我们的用户经过了认证,认证信息存储在$HOME/.kube目录下config文件中,其内容如下:

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: XXXX  ##根证书颁发机构信息,根CA证书
    server: https://192.168.154.200:6443 ##访问集群的API Server完整URL
  name: kubernetes   ##集群的名字
contexts:
- context:
    cluster: kubernetes   ##访问kubernetes这个集群
    user: kubernetes-admin  ##使用 kubernetes-admin账号
  name: kubernetes-admin@kubernetes  ##给定一个名称
current-context: kubernetes-admin@kubernetes  ##当前上下文,表示使用哪个账号访问哪个集群
kind: Config
preferences: {}
users:    ##用户列表
- name: kubernetes-admin   ##用户名称
  user:
    client-certificate-data: XXXXXX  ##客户端证书,用于与apiserver进行认证
    client-key-data: XXXX  ##客户端私钥

clusters模块

  • cluster中包含 kubernetes 集群的端点数据,包括 kubernetes apiserver 的完整 url 以及集群的证书颁发机构。
  • 可以使用kubectl config set-cluster添加或修改 cluster 条目。

users模块

  • user 定义用于向 kubernetes 集群进行身份验证的客户端凭据。
  • 可用凭证有client-certificate、client-key、token 和 username/password。
  • username/password 和 token 是二者只能选择一个,但 client-certificate 和 client-key 可以分别与它们组合。
  • 可以使用kubectl config set-credentials添加或者修改 user 条目。

contexts 模块

  • context 定义了一个命名的cluster、user、namespace元组,用于使用提供的认证信息和命名空间将请求发送到指定的集群。
  • 三个都是可选的,仅使用 cluster、user、namespace 之一指定上下文,或指定none。
  • 未指定的值或在加载的 kubeconfig 中没有相应条目的命名值将被替换为默认值。加载和合并 kubeconfig 文件的规则很简单,但有很多,具体可以查看加载和合并kubeconfig规则。
  • 可以使用kubectl config set-context添加或修改上下文条目。

current-context 模块

  • current-context 是作为cluster、user、namespace元组的 key,当 kubectl 从该文件中加载配置的时候会被默认使用。
  • 可以在 kubectl 命令行里覆盖这些值,通过分别传入–context=CONTEXT、–cluster=CLUSTER、–user=USER 和 –namespace=NAMESPACE。
  • 可以使用kubectl config use-context更改 current-context。
##一个kubeconfig样例
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: xxx
    server: https://xxx:6443
  name: cluster1
- cluster:
    certificate-authority-data: xxx
    server: https://xxx:6443
  name: cluster2
contexts:
- context:
    cluster: cluster1
    user: kubelet
  name: cluster1-context
- context:
    cluster: cluster2
    user: kubelet
  name: cluster2-context
current-context: cluster1-context
kind: Config
preferences: {}
users:
- name: kubelet
  user:
    client-certificate-data: xxx
    client-key-data: xxx

查看kubectl config 命令帮助:

[root@k8s-master ~]# kubectl config --help
Modify kubeconfig files using subcommands like "kubectl config set current-context my-context"

 The loading order follows these rules:

  1.  If the --kubeconfig flag is set, then only that file is loaded. The flag may only be set once and no merging takes
place.
  2.  If $KUBECONFIG environment variable is set, then it is used as a list of paths (normal path delimiting rules for
your system). These paths are merged. When a value is modified, it is modified in the file that defines the stanza. When
a value is created, it is created in the first file that exists. If no files in the chain exist, then it creates the
last file in the list.
  3.  Otherwise, ${HOME}/.kube/config is used and no merging takes place.

Available Commands:
  current-context 显示 current_context
  delete-cluster  删除 kubeconfig 文件中指定的集群
  delete-context  删除 kubeconfig 文件中指定的 context
  get-clusters    显示 kubeconfig 文件中定义的集群
  get-contexts    描述一个或多个 contexts
  rename-context  Renames a context from the kubeconfig file.
  set             设置 kubeconfig 文件中的一个单个值
  set-cluster     设置 kubeconfig 文件中的一个集群条目
  set-context     设置 kubeconfig 文件中的一个 context 条目
  set-credentials 设置 kubeconfig 文件中的一个用户条目
  unset           取消设置 kubeconfig 文件中的一个单个值
  use-context     设置 kubeconfig 文件中的当前上下文
  view            显示合并的 kubeconfig 配置或一个指定的 kubeconfig 文件

Usage:
  kubectl config SUBCOMMAND [options]

Use "kubectl <command> --help" for more information about a given command.
Use "kubectl options" for a list of global command-line options (applies to all commands).

5.2 定义登录dashboard的kubeconfig文件

安装dashboard后,登录时,可以使用token,也可以使用kubeconfig。

TOKEN

找到认证的token,使用此token即可登录dashboard,管理集群。

##找到service account
[root@k8s-master ~]# kubectl get sa -n kubernetes-dashboard
NAME                   SECRETS   AGE
default                1         27d
kubernetes-dashboard   1         27d

##找到service account对应的secret
[root@k8s-master ~]# kubectl get sa kubernetes-dashboard  -n kubernetes-dashboard -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"labels":{"k8s-app":"kubernetes-dashboard"},"name":"kubernetes-dashboard","namespace":"kubernetes-dashboard"}}
  creationTimestamp: "2020-03-13T14:57:27Z"
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kubernetes-dashboard
  resourceVersion: "35485"
  selfLink: /api/v1/namespaces/kubernetes-dashboard/serviceaccounts/kubernetes-dashboard
  uid: 236ebe45-7bdb-4f3a-9832-8b01f5838591
secrets:
- name: kubernetes-dashboard-token-k29x4

##找到token的值
[root@k8s-master ~]# kubectl describe secret kubernetes-dashboard-token-k29x4 -n kubernetes-dashboard
Name:         kubernetes-dashboard-token-k29x4
Namespace:    kubernetes-dashboard
Labels:       <none>
Annotations:  kubernetes.io/service-account.name: kubernetes-dashboard
              kubernetes.io/service-account.uid: 236ebe45-7bdb-4f3a-9832-8b01f5838591

Type:  kubernetes.io/service-account-token

Data
====
ca.crt:     1025 bytes
namespace:  20 bytes
token:      eyJhbGciOiJSUzI1NiIsImtpZCI6IlFRbGhoWUhVZlJ0RUh3TkhtckNrVUUwc01wWjNiMzN5Nnh1Y2pWa3lLVUkifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJrdWJlcm5ldGVzLWRhc2hib2FyZC10b2tlbi1rMjl4NCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjIzNmViZTQ1LTdiZGItNGYzYS05ODMyLThiMDFmNTgzODU5MSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlcm5ldGVzLWRhc2hib2FyZDprdWJlcm5ldGVzLWRhc2hib2FyZCJ9.XC1L3wlB7wIJQmmHIsZHMKtivFs0BBbFOTIjnJY82bgFl5l-yQbdYebftmXB1PvK97d_CG09Hs0JhwrOMbaV_89Ap4SR-xo2V92WTE_-OQfF8qaDTjz7iivqCCjNtL7gOGMgjourOmI6kjv05AvljgTpmpt-q3ELt9rjvbKnGA8S5DfyOjsB0J2zi8t6-AiNR6JwIGs2BOC6TW-HY6-p_-AQDDDgs6d3fJxlt6eXadkkQVSmlPxtdE25mNbYXzpjEQLDA-G2c1Vx7M0beI1LVX7Zp9aqUyUO0CfEzTTm6E26c2JQxeRuuZVaDOQB-rGXZXckOvw9zuNniAGOu6lP4Q

创建kubeconfig

根据上面的token可以创建成kubeconfig文件进行登录。

##找到集群的API Server的入口URL
[root@k8s-master ~]# kubectl cluster-info
Kubernetes master is running at https://192.168.154.200:6443
KubeDNS is running at https://192.168.154.200:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

##将上面的token保存在变量中
[root@k8s-master ~]# TOKEN=$(kubectl describe secret kubernetes-dashboard-token-k29x4 -n kubernetes-dashboard \
> | grep -E '^token' | awk '{print $2}')

创建kubeconfig文件: dashboard.kubeconfig

##定义集群信息
[root@k8s-master ~]#kubectl config set-cluster kubernetes \
> --certificate-authority=/etc/kubernetes/pki/ca.crt \
> --embed-certs=true \
> --server=https://192.168.154.200:6443 \
> --kubeconfig=dashboard.kubeconfig

##设置用户信息
[root@k8s-master ~]#kubectl config set-credentials dashboard_user \
> --token=${TOKEN} \
> --kubeconfig=dashboard.kubeconfig

##设置上下文
[root@k8s-master ~]#kubectl config set-context default \
> --cluster=kubernetes --user=dashboard_user \
> --kubeconfig=dashboard.kubeconfig

##设置当前上下文
[root@k8s-master ~]#kubectl config use-context default \
> --kubeconfig=dashboard.kubeconfig

然后在登录界面输入dashboard.kubeconfig的路径即可登录dashboard。

使用用户证书来创建kubeconfig文件

需要先生成用户证书
##生成私钥(可借助openssl等证书工具)
[root@k8s-master ~]# openssl genrsa -out mike.key 2048

##生成证书请求csr,用户为mike,组为devs
[root@k8s-master rbac-test]# openssl req -new -key mike.key -out mike.csr -subj "/CN=mike/O=devs"
[root@k8s-master rbac-test]# ls
mike.csr  mike.key

##基于csr文件或实例通过集群ca keypari签发证书,下面是openssl签发示例:
[root@k8s-master rbac-test]# openssl x509 -req -in mike.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key \
> -CAcreateserial -out mike.crt -days 365
Signature ok
subject=/CN=mike/O=devs
Getting CA Private Key

[root@k8s-master rbac-test]# ls mike*
mike.crt  mike.csr  mike.key

##查看Mike证书的内容
[root@k8s-master rbac-test]# openssl x509 -in mike.crt -text -noout
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number:
            e4:40:e0:00:84:c1:28:c9
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=kubernetes   
        Validity
            Not Before: Apr  7 14:18:18 2020 GMT
            Not After : Apr  7 14:18:18 2021 GMT
        Subject: CN=mike, O=devs
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:b1:76:d5:02:b2:d5:cd:5e:38:de:f2:38:ae:c9:
                    be:34:22:fe:41:8a:86:21:b9:2b:7c:f7:99:e3:d2:
                    e6:1b:87:ac:a0:2f:f0:00:a0:de:17:23:4c:fa:d8:
                    5c:4d:e4:28:3a:9c:73:91:a5:6f:a2:56:1c:a5:22:
                    d8:d3:c7:5e:b3:81:ba:e2:93:d6:34:52:c9:28:5a:
                    f7:5f:58:bc:3d:4d:cc:7e:98:3a:d9:c3:9a:0f:08:
                    4b:7a:7b:d3:ad:5c:40:62:89:7c:89:82:4e:0b:b1:
                    41:d7:3c:8a:1c:b0:0e:02:78:3e:61:ad:2f:eb:e7:
                    63:e1:c9:a1:ca:83:fe:62:15:db:4f:30:7d:64:a3:
                    a2:3e:55:c4:34:90:70:cc:49:2a:40:f0:54:12:f6:
                    4b:27:21:14:01:64:2d:c2:74:09:32:97:eb:05:1d:
                    75:d5:43:67:3d:85:d8:1b:f9:7e:53:69:a3:6e:92:
                    da:50:3f:04:1e:5e:0c:66:24:07:b3:54:c5:6f:e8:
                    4d:95:c3:5e:9d:67:84:8e:a2:6c:67:fe:d5:74:f6:
                    06:bc:ee:b9:9f:e8:18:40:d8:54:ff:d8:49:a2:ff:
                    8e:9c:cf:1f:6b:42:1d:e4:13:79:dd:1d:54:25:e2:
                    4f:68:41:e6:58:f5:fa:c9:8b:5a:42:27:f4:6c:0f:
                    d4:e3
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha256WithRSAEncryption
         a3:30:76:40:6d:05:cb:86:a5:04:1e:84:c9:f1:d1:40:cb:ab:
         50:32:b2:07:88:94:e5:13:a9:4b:8d:ac:d5:01:f0:f6:6d:8e:
         f2:6f:44:ef:59:36:b6:3b:da:6c:1d:78:25:44:46:63:1c:83:
         78:1a:f3:b0:1b:5f:26:8e:e2:3c:6b:61:b2:ff:69:2e:89:a2:
         d8:7d:18:09:8b:8f:a4:e0:78:db:71:f2:72:3f:4a:16:21:86:
         65:11:42:fd:f6:b0:5b:6c:d7:3a:25:3d:c3:d1:7c:e2:1f:b3:
         08:16:c4:43:f7:86:c2:4f:6d:30:e2:cc:ca:c3:95:ae:5e:17:
         32:bf:65:6c:85:01:06:2b:da:20:21:ce:ad:23:ef:b2:de:66:
         89:61:2e:d6:6f:b5:25:49:2f:6d:57:f0:84:80:1a:67:64:fc:
         02:c3:9c:d1:13:ea:1d:06:f2:19:58:9d:36:43:4e:67:8a:4d:
         fc:86:d9:1e:bc:54:31:e2:cb:2b:27:fb:87:ba:a6:6b:57:ab:
         aa:32:c3:0c:90:42:e3:e7:e3:d0:2b:44:1d:a0:44:61:e5:68:
         9e:94:0a:1c:55:0e:6c:03:26:a1:ac:a4:d3:b3:cc:06:fd:1f:
         c2:3d:0e:5c:55:ae:e7:db:ef:16:cb:1f:a9:ce:2b:2f:de:9d:
         47:de:3b:ad
然后生成kubeconfig文件
[root@k8s-master rbac-test]# kubectl config set-cluster kubernetes \
> --certificate-authority=/etc/kubernetes/pki/ca.crt \
> --server=https://192.168.154.200:6443 \
> --embed-certs=true \
> --kubeconfig=mike.kubeconfig
Cluster "kubernetes" set.

[root@k8s-master rbac-test]# kubectl config set-credentials mike \
> --client-certificate=./mike.crt \
> --client-key=./mike.key \
> --embed-certs=true \
> --kubeconfig=mike.kubeconfig
User "mike" set.

[root@k8s-master rbac-test]# kubectl config set-context default \
> --cluster=kubernetes \
> --user=mike \
> --kubeconfig=mike.kubeconfig 
Context "default" created.

[root@k8s-master rbac-test]# kubectl config use-context default \
> --kubeconfig=mike.kubeconfig
Switched to context "default".
使用mike的kubeconfig文件访问集群
[root@k8s-master rbac-test]# kubectl get pods --kubeconfig=./mike.kubeconfig
Error from server (Forbidden): pods is forbidden: User "mike" cannot list resource "pods" in API group "" in the namespace "default"

六. Kubernetes 鉴权 - RBAC

当一个请求在完成 api-server 认证后,可以认为它是一个合法的用户,那么如何控制该用户在集群中的哪些 namespace 中访问哪些资源,对这些资源又能进行哪些操作呢?

这就由访问控制的第二步 Kubernetes 鉴权来完成。api-server 本身支持多种鉴权方式,我们主要介绍在安全上推荐的鉴权方式 RBAC。

RBAC 使用 rbac.authorization.k8s.io API 组来驱动鉴权操作,允许管理员通过 Kubernetes API 动态配置策略。

要启用 RBAC,在启动 API 服务器时添加 –authorization-mode=RBAC 参数。例如:

kube-apiserver --authorization-mode=Example,RBAC --other-options --more-options

查看集群启动信息:

##通过命令:cat /etc/kubernetes/manifests/kube-apiserver.yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    component: kube-apiserver
    tier: control-plane
  name: kube-apiserver
  namespace: kube-system
spec:
  containers:
  - command:
    - kube-apiserver
    - --advertise-address=192.168.154.200
    - --allow-privileged=true
    - --authorization-mode=Node,RBAC
    - --client-ca-file=/etc/kubernetes/pki/ca.crt
    - --enable-admission-plugins=NodeRestriction
    - --enable-bootstrap-token-auth=true
    - --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
    - --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
    - --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
    - --etcd-servers=https://127.0.0.1:2379
    - --insecure-port=0
    - --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
    - --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
    - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
    - --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
    - --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key
    - --requestheader-allowed-names=front-proxy-client
    - --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
    - --requestheader-extra-headers-prefix=X-Remote-Extra-
    - --requestheader-group-headers=X-Remote-Group
    - --requestheader-username-headers=X-Remote-User
    - --secure-port=6443
    - --service-account-key-file=/etc/kubernetes/pki/sa.pub
    - --service-cluster-ip-range=10.1.0.0/16
    - --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
    - --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
    image: registry.aliyuncs.com/google_containers/kube-apiserver:v1.17.3
    imagePullPolicy: IfNotPresent
    livenessProbe:
      failureThreshold: 8
      httpGet:
        host: 192.168.154.200
        path: /healthz
        port: 6443
        scheme: HTTPS
      initialDelaySeconds: 15
      timeoutSeconds: 15
    name: kube-apiserver
    resources:
      requests:
        cpu: 250m
    volumeMounts:
    - mountPath: /etc/ssl/certs
      name: ca-certs
      readOnly: true
    - mountPath: /etc/pki
      name: etc-pki
      readOnly: true
    - mountPath: /etc/kubernetes/pki
      name: k8s-certs
      readOnly: true
  hostNetwork: true
  priorityClassName: system-cluster-critical
  volumes:
  - hostPath:
      path: /etc/ssl/certs
      type: DirectoryOrCreate
    name: ca-certs
  - hostPath:
      path: /etc/pki
      type: DirectoryOrCreate
    name: etc-pki
  - hostPath:
      path: /etc/kubernetes/pki
      type: DirectoryOrCreate
    name: k8s-certs
status: {}

6.1 RBAC 鉴权三要素

  • 第一要素是 Subjects:也就是主体。可以是开发人员、集群管理员这样的自然人,也可以是系统组件进程,或者是 Pod 中的逻辑进程;主体可以是用户组(Group)、用户(User)或者服务账户(Service Accounts)。
  • 第二个要素是 API Resource:也就是请求对应的访问目标。在 Kubernetes 集群中也就是各类资源;
  • 第三要素是 verbs:对应为请求对象资源可以进行哪些操作,包括增删改查、list、get、watch 等。

这里举个例子,假设有个通过合法认证的用户 Bob,他请求 list 某个 namespace下的 Pods,该请求的鉴权语义记为:Can Bob list pods ?其中 Bob 即为请求中的 Subject,list 为对应的请求动作 Action,而 pods 为对应的请求资源 Resource。

6.2 RBAC 权限粒度

上面介绍了RBAC角色模型的三要素,在整个RBAC策略定义下,还需要将这个角色绑定到一个具体的控制域内。这就是Kubernetes中的命名空间。通过 namespace 可以将 Kubernetes api 资源限定在不同的作用域内。从而在一个多租户集群中,对用户进行逻辑上的隔离。

  • 策略包含主体(subject)、动作(verbs)、资源(resource)和命名空间(namespace)
    • User A can create Pods in namespace B
  • 默认拒绝所有访问:deny all
  • 不能(Cannot):
    • 不能绑定到namespace中的一个具体的object
    • 不能绑定到指定资源的任意一个fields
  • 能(Can):
    • 可以对subresources进行绑定(比如nodes/status)

通常 RBAC 会进行对 api-server 的细粒度访问控制,但是这个细粒度是个相对的概念,RBAC 是面向模型级别的绑定。它不能绑定到 namespace 中的一个具体的 object 实例,更不能绑定到指定资源的任意一个 field。

RBAC 对访问权限的控制粒度上,它可以细化到 Kubernetes api 的 subresources 级别。比如针对一个访问者,我们可以控制其在指定 namespace 下对 nodes/status 模型的访问。

6.3 RBAC - Role

RBAC 具体的绑定权限和对象。

角色(Role):它定义了用户在指定的 Kubernetes 命名空间资源上可以进行哪些操作。比如可以定义一个 namespace 中 pod 的只读权限,同时还可以定义一个 namespace 管理员权限,它具有对这个命名空间下所有对象资源的所有操作权限。

这是“default”名称空间中的一个示例Role,授予其对Pod读取访问权限

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""] # "" 表明这个是 core API group
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
  • resource 字段定义了这个角色可以访问哪些资源;
  • verbs 字段定义了这个角色有哪些操作的权限。
  • apiGroups 中,需要指定目标资源的 apiGroups 名称,这里可以通过官方 API 文档查询,如果指定的 Group 是 core,那么在角色模板中的 apiGroups 可置为空。

为了更容易对API进行扩展,Kubemetes 使用API Groups (API组)进行标识。API Groups 以及REST URL中的路径进行定义。当前支持两类 API groups。

  • Core Groups (核心组),也可以称为Legacy Groups,该组的REST路径位于/api/v1, 作为 Kubernetes 最核心的 API,它是没有“组”的概念,例如 ”vl“,在资源对象的定义中表示为”apiVersion: v1“。
  • 具有分组信息的API,以/apis/GROUP_NAME/GROUPNAME/VERSIONURL路径进行标识,在资源对象的定义中表示为apiVersion: GROUP_NAME/GROUPNAME/VERSION(例如,apiVersion: batch/v1)。已支持的 API 组详细列表请参阅 Kubernetes API reference。

6.4 RBAC - RoleBinding

完成了一个 namespace 下的角色定义之后,还需要建立其与使用这个角色的主体之间在 namespace 下的绑定关系,这里需要一个 RoleBinding 模型。使用 RoleBinding 可以将 Role 对应的权限模型绑定到对应的 Subject 上。

可以将名为 test 的 namespace 中的 pod 只读权限同时绑定给用户 test1 和 test2 以及 proc1。也可以将 namespace test 只读权限绑定组名称为 tech-lead 的 test1 用户,这样用户 test2 和 proc1 是没有 get namespace 权限的。

RoleBinding的示例,该示例将“ pod-reader”角色授予“default”名称空间中的用户“jane”。这允许“jane”读取“default”名称空间中的pod。

apiVersion: rbac.authorization.k8s.io/v1
# This role binding allows "jane" to read pods in the "default" namespace.
# You need to already have a Role named "pod-reader" in that namespace.
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
# You can specify more than one "subject"
- kind: User
  name: jane # "name" is case sensitive 名称区分大小写
  apiGroup: rbac.authorization.k8s.io
roleRef:
  # "roleRef" specifies the binding to a Role / ClusterRole
  kind: Role #this must be Role or ClusterRole
  name: pod-reader # this must match the name of the Role or ClusterRole you wish to bind to
  apiGroup: rbac.authorization.k8s.io
  • subjects 可以是users, groups, 或者 service accounts;
  • 一个RoleBinding只能指定唯一的Role;
  • RoleBinding可以引用同一名称空间中的任何Role;还可以引用ClusterRole并将该ClusterRole绑定到RoleBinding的名称空间;
  • Role和RoleBinding是针对一个名称空间的;

6.5 RBAC - ClusterRole

除了定义指定 namespace 中的权限模型,也可以通过 ClusterRole 定义一个集群维度的权限模型。在一个 Cluster 实例中,可以定义集群维度的权限使用权限,比如像 PV、Nodes 在 namespace 中不可见的资源权限,可以在 ClusterRole 中定义,而操作这些资源的动作同样是之前 Role 中支持的增删改查和 list、watch 等操作。

ClusterRoles是集群范围的,可以定义针对所有命名空间范围内的资源用户可以进行哪些操作:

  • 集群范围的资源(例如 nodes)
  • 非资源endpoint(例如 /healthz)
  • 跨命名空间访问的有命名空间作用域的资源(如 Pods),您可以使用ClusterRole来允许特定用户运行如下命令:
kubectl get pods --all-namespaces

下面的 ClusterRole 示例可用来对某特定命名空间下的 Secrets 的读取操作授权, 或者跨所有命名空间执行授权(取决于它是如何绑定的):

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # "namespace" omitted since ClusterRoles are not namespaced
  name: secret-reader
rules:
- apiGroups: [""]
  #
  # at the HTTP level, the name of the resource for accessing Secret
  # objects is "secrets"
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]

ClusterRole 编排文件几乎和 Role 是一模一样的,唯一不同的地方是 ClusterRole 中是所有集群维度的权限定义,不支持 namespace 字段定义。

6.6 RBAC - ClusterRoleBinding

同样在 ClusterRole 的基础上,可以将其绑定在对应的 Subject 主体上。而 ClusterRoleBinding 模型实例可以帮助我们在集群所有命名空间上将 ClusterRole 绑定到具体的 Subject 对象上。

可以将所有 namespace 的 list 权限绑定给 group 为 sre 或者 devops 的管理员 admin1 和 admin2。

ClusterRoleBinding 可用来在集群级别或对所有命名空间执行授权。 下面的例子允许 “manager” 组中的任何用户读取任意命名空间中 “secrets”。

apiVersion: rbac.authorization.k8s.io/v1
# This cluster role binding allows anyone in the "manager" group to read secrets in any namespace.
kind: ClusterRoleBinding
metadata:
  name: read-secrets-global
subjects:
- kind: Group
  name: manager # Name is case sensitive 名称区分大小写
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

相比较于 RoleBinding,ClusterRoleBinding 模板定义也只是在 namespace 和 roleRef 中的权限对象模型定义上有不同,其他的定义格式是一样的。

注意:创建了一个绑定后,不能修改其引用的Role或者ClusterRole,如果你去修改一个绑定的roleRef,你将得到一个验证错误。如果你想要改变roleRef,需要移除这个绑定对象并重新创建。

6.7 Role示例:

在以下示例中,仅截取展示了rules对应部分。

允许读取在core API Group里的Pods资源
rules:
- apiGroups: [""]
  #
  # at the HTTP level, the name of the resource for accessing Pod
  # objects is "pods"
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
允许读/写在“extensions”和“apps”API组中的“deployments”资源
rules:
- apiGroups: ["extensions", "apps"]
  #
  # at the HTTP level, the name of the resource for accessing Deployment
  # objects is "deployments"
  resources: ["deployments"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
允许读取核心API组中的Pod,以及读取或写入"batch"或"extensions"API组中的Job资源
rules:
- apiGroups: [""]
  #
  # at the HTTP level, the name of the resource for accessing Pod
  # objects is "pods"
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["batch", "extensions"]
  #
  # at the HTTP level, the name of the resource for accessing Job
  # objects is "jobs"
  resources: ["jobs"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
允许读取名为“ my-config”的ConfigMap(必须与RoleBinding绑定以限制单个名称空间中的单个ConfigMap):
rules:
- apiGroups: [""]
  #
  # at the HTTP level, the name of the resource for accessing ConfigMap
  # objects is "configmaps"
  resources: ["configmaps"]
  resourceNames: ["my-config"]
  verbs: ["get"]
允许读取核心API组中nodes资源(由于Node是集群范围的,因此必须位于与ClusterRoleBinding绑定的ClusterRole中才能生效)
rules:
- apiGroups: [""]
  #
  # at the HTTP level, the name of the resource for accessing Node
  # objects is "nodes"
  resources: ["nodes"]
  verbs: ["get", "list", "watch"]
允许对非资源endpoint “/healthz"和所有子路径的GET和POST请求(必须在与ClusterRoleBinding绑定的ClusterRole中有效):
rules:
- nonResourceURLs: ["/healthz", "/healthz/*"] # '*' in a nonResourceURL is a suffix glob match
  verbs: ["get", "post"]

6.8 RoleBinding示例

以下RoleBinding示例仅显示subjects部分。

对于用户名为:alice@example.com
subjects:
- kind: User
  name: "alice@example.com"
  apiGroup: rbac.authorization.k8s.io
对于组名为:frontend-admins
subjects:
- kind: Group
  name: "frontend-admins"
  apiGroup: rbac.authorization.k8s.io
对于“ kube-system”名称空间中的default service account:
subjects:
- kind: ServiceAccount
  name: default
  namespace: kube-system
对于“ qa”命名空间中的所有service accounts帐户:
subjects:
- kind: Group
  name: system:serviceaccounts:qa
  apiGroup: rbac.authorization.k8s.io
对于任何名称空间中的所有服务帐户:
subjects:
- kind: Group
  name: system:serviceaccounts
  apiGroup: rbac.authorization.k8s.io
对于所有经过身份验证的用户:
subjects:
- kind: Group
  name: system:authenticated
  apiGroup: rbac.authorization.k8s.io
对于所有未经身份验证的用户:
subjects:
- kind: Group
  name: system:unauthenticated
  apiGroup: rbac.authorization.k8s.io
对于所有用户:
subjects:
- kind: Group
  name: system:authenticated
  apiGroup: rbac.authorization.k8s.io
- kind: Group
  name: system:unauthenticated
  apiGroup: rbac.authorization.k8s.io

6.9 默认 roles 和 role bindings

通过上面的学习,我们知道在不进行任何权限的绑定下,RABC 会拒绝所有的访问。那么我们系统组件中是如何请求的呢?

其实在集群创建的时候,处理系统各组件的客户端证书,它们各自的角色和环境对象也会被创建出来,以满足组件业务之间交互必须的权限要求。

API servers创建一组默认为ClusterRoleClusterRoleBinding的对象。 其中许多是以system:为前缀的,它表示资源是基础设施 “owned” 的。对于这些资源的修改可能导致集群功能失效。例如,system:node是集群角色,它是定义 kubelets 相关的权限,如果这个角色被修改,它将导致 kubelets 无法正常工作。

所有默认的 ClusterRole 和 ClusterRoleBinding 对象都会被标记为 kubernetes.io/bootstrapping=rbac-defaults

以下简单列举几个:

Default ClusterRole Default ClusterRoleBinding Description
system:basic-user system:authenticated group 允许用户以只读的方式去访问他们自己的基本信息。在1.14版本之前, 这个角色在默认情况下也绑定在 system:unauthenticated 上。
cluster-admin system:masters group 允许超级用户在平台上的任何资源的所有操作。当在 ClusterRoleBinding 中使用时, 可以授权对集群中以及所有命名空间中的全部资源进行完全控制。 当在 RoleBinding 中使用时,可以授权控制 RoleBinding 所在命名空间中的所有资源, 包括命名空间本身。
admin 允许管理员访问权限,旨在使用 RoleBinding 在命名空间内执行授权。 如果在 RoleBinding 中使用,则可授予对命名空间中的大多数资源的读/写权限, 包括创建角色和绑定角色(RoleBinding)的能力。 但是它不允许对资源配额 或者命名空间本身进行写操作。
edit 允许对命名空间的大多数对象进行读/写操作。 它不允许查看或者修改角色(Roles)或者角色绑定(RoleBindings)。
view 允许对命名空间的大多数对象有只读权限。 它不允许查看角色(Roles) 或角色绑定(RoleBindings)。 它不允许查看 Secrets,因为这类操作属于越权。

6.10 角色中的 verbs 如何设置?

通过以上对 RBAC 的学习,应该对 Kubernetes 中 RBAC 中的模型定义有了一定的了解,但是在某些复杂的多租户业务场景下,如何在权限模板中针对各个 API 模型定义相应的动作策略,还是需要一定的理论和实践基础的。而对一个应用开发人员来说,kubectl 可能更为直观和熟悉些,下面是一些 kubectl 操作和 RBAC 中的对应关系。

七. 实际案例

7.1 为用户授权

使用上面的mike用户的kubeconfig文件进行访问

[root@k8s-master rbac-test]# kubectl get pods --kubeconfig=./mike.kubeconfig
Error from server (Forbidden): pods is forbidden: User "mike" cannot list resource "pods" in API group "" in the namespace "default"
创建一个Role
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: mike-role01
  namespace: default
rules:
- apiGroups: ["", "extensions", "apps"]
  resources: ["deployments", "replicasets", "pods"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] # 也可以使用['*']
创建一个RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: mike-rolebinding
  namespace: default
subjects:
- kind: User
  name: mike
  apiGroup: ""
roleRef:
  kind: Role
  name: mike-role01
  apiGroup: ""
测试
##创建Role及RoleBinding
[root@k8s-master rbac-test]# kubectl apply -f mike-role01.yaml 
role.rbac.authorization.k8s.io/mike-role01 unchanged
[root@k8s-master rbac-test]# kubectl apply -f mike-rolebinding.yaml 
rolebinding.rbac.authorization.k8s.io/mike-rolebinding created

##以mike身份进行操作,可以成功,因为默认是default名称空间
[root@k8s-master rbac-test]# kubectl get pods --kubeconfig=./mike.kubeconfig
NAME                                     READY   STATUS    RESTARTS   AGE
curl-69c656fd45-26jv2                    1/1     Running   9          29d
my-service-gb45b                         1/1     Running   4          9d
my-service-sxz97                         1/1     Running   4          9d
nfs-client-provisioner-c7b986bf7-hgs7h   1/1     Running   3          5d12h
nginx-86c57db685-hdwr8                   1/1     Running   8          29d
secret-pod3                              1/1     Running   3          6d6h
test-8656bc94b4-nsk77                    1/1     Running   25         9d

##换成其他名称空间,就又不可以了
[root@k8s-master rbac-test]# kubectl get pods --kubeconfig=./mike.kubeconfig -n kube-system
Error from server (Forbidden): pods is forbidden: User "mike" cannot list resource "pods" in API group "" in the namespace "kube-system"

如何希望Mike在所有名称空间都可以,需要创建ClusterRoleBinding

例如:使用默认的管理员角色admin来创建ClusterRoleBinding

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: mike-rolebinding02
subjects:
- kind: User
  name: mike
  apiGroup: ""
roleRef:
  kind: ClusterRole
  name: admin
  apiGroup: ""

7.2 创建访问dashboard的用户

作为Kubernetes的Web用户界面,用户可以通过Dashboard在Kubernetes集群中部署容器化的应用,对应用进行问题处理和管理,并对集群本身进行管理。通过Dashboard,用户可以查看集群中应用的运行情况,同时也能够基于Dashboard创建或修改部署、任务、服务等Kubernetes的资源。通过部署向导,用户能够对部署进行扩缩容,进行滚动更新、重启Pod和部署新应用。

访问dashboard需要通过config或者token授权才能登陆,但是dashboard本身不提供授权,因为dashborad是一个pod,实际上我们是使用这个pod认证到k8s的集群中去的,我们需要为dashborad的pod提供config或者token认证,所以这里的认证主体应该是service account。

Dashboard 默认只支持 token 认证,所以如果使用 KubeConfig 文件,需要在该文件中指定 token,不支持使用 client 证书认证。

创建具有集群管理权限的用户登录dashborad

## 使用命令创建
# kubectl create sa dashboard-admin -n kube-system
# kubectl create clusterrolebinding dashboard-admin --clusterrole=cluster-admin --serviceaccount=kube-system:dashboard-admin

或者使用yaml文件

apiVersion: v1
kind: ServiceAccount
metadata:
  name: dashboard-admin
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: dashboard-admin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: dashboard-admin
  namespace: kube-system

创建完成后,找到其对应的secret,里面有登录的token

# kubectl get secret -n kube-system | grep dashboard-admin
# kubectl describe secret dashboard-admin-token-hn4c2 -n kube-system | egrep '^token' | awk '{print $2}'
## 复制token,登录dashboard即可

创建具有指定名称空间管理权限的用户登录dashborad

apiVersion: v1
kind: ServiceAccount
metadata:
  name: default-ns-admin
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: default-ns-admin
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: admin
subjects:
- kind: ServiceAccount
  name: default-ns-admin
  namespace: default

找到其token登录后,可以看到只能访问default命名空间资源,其它命名空间是不可以的。 image.png