# 权限

  • 客户端通发送 HTTP 请求到 apiserver 时,需要依次通过以下检查,请求才会生效。
    • 认证(Authentication)
      • :客户端需要采用某种身份认证方式,证明自己是 k8s 中的一个合法用户。
      • 如果客户端未通过身份认证,则会被 apiserver 视作匿名用户,记作 system:anonymous ,属于 system:unauthenticated 用户组。
      • apiserver 默认会拒绝匿名用户的请求,返回 HTTP 403 报文。
    • 限流(Rate Limit)
      • 例如启动 apiserver 时,可添加命令行参数 --max-requests-inflight=xx --max-mutating-requests-inflight=xx ,限制 apiserver 接收读、写请求的最大并发数。
    • 鉴权(Authorization)
      • :客户端通过身份认证之后,apiserver 会根据鉴权模块,确定客户端有权进行哪些操作。比如有权创建 Pod 。
    • 准入控制(Admission Control)
      • :除了一般软件常见的认证、鉴权环节,apiserver 还可对客户端操作进行更多审核。比如创建 Pod 时是否符合 LimitRange 限制。
      • 启动 apiserver 时,默认会通过命令行参数,启用一些准入控制插件:
        kube-apiserver --enable-admission-plugins=LimitRanger,PodSecurity,...
        

# 认证

  • k8s 支持多种身份认证方式:

    • SSL 客户端证书
      • :用于验证客户端的身份,证书中的 CN 字段记录了用户名。
      • 部署 k8s 集群时,默认会让 apiserver、kubelet、etcd 等组件分别使用自签名的 SSL 证书,从而保障 k8s 组件之间的通信安全。
    • ServiceAccount Token
      • :需要客户端在 HTTP 请求头中加入 Authorization: Bearer <token>
    • bootstrap Token
      • :用于部署 k8s 集群、新增节点。
    • static token file
      • :可以在 apiserver 的启动命令中,以 --token-auth-file=tokens.csv 的方式启用一个静态的 token 列表。
    • 此外,还支持集群外的身份认证服务,比如 LDAP、Kerberos、OIDC 。
  • k8s 的账户分为两类:

    • User
      • :供自然人使用,比如 kubectl 。
      • 不能通过 k8s API 创建,要通过 SSL 证书等方式创建,比较麻烦。
    • ServiceAccount
      • :供应用程序使用。
      • User 作用于集群全局,而 ServiceAccount 会被 namespace 隔离。

# ServiceAccount

  • 创建一个 k8s namespace 时,会自动创建一个名为 default 的 ServiceAccount ,并关联一个 secret 。配置示例:

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: default
      namespace: default  # 该 ServiceAccount 所属的命名空间,默认为 default
    secrets:              # 该 ServiceAccount 关联的一组 secret
    - name: default-token-zqltx
    
    ---
    
    apiVersion: v1
    data:
      ca.crt: ******      # CA 证书,用于验证 apiserver 的身份
      namespace: ******
      token: ******       # 客户端访问 apiserver 时,可以在 HTTP 请求头中加入 token ,从而证明自己的身份是某个 ServiceAccount
    kind: Secret
    metadata:
      annotations:
        kubernetes.io/service-account.name: default # 表示该 secret 所属的 ServiceAccount ,必须已存在,否则不能创建该 secret
        kubernetes.io/service-account.uid: ******
      name: default-token-zqltx
      namespace: default
    type: kubernetes.io/service-account-token
    
  • 创建 Pod 时,可以采用当前 namespace 的任一 ServiceAccount :

    apiVersion: v1
    kind: Pod
    spec:
      serviceAccountName: default         # 该 Pod 采用的 ServiceAccount ,如果不存在则不能创建 Pod 。默认为 default
      automountServiceAccountToken: true  # 是否自动将 ServiceAccount 关联的 secret 挂载到 Pod 的 /var/run/secrets/kubernetes.io/serviceaccount/ 目录下。默认为 true
    
    • Pod 内的程序,可以从挂载目录读取 token ,从而在访问 apiserver 时验证身份。
  • k8s v1.24 之前,创建一个 ServiceAccount 时,会自动创建并关联一个 secret ,命名格式为 <ServiceAccount>-token-<random_id>

    • 删除一个 ServiceAccount 时,会自动删除其关联的所有 secret 。
  • k8s v1.24 之后,创建 ServiceAccount 不会自动创建 secret 。

    • 创建一个 Pod 时,会根据当前采用的 ServiceAccount ,自动从 apiserver 获取一个临时的 token ,挂载到 Pod 中。当 Pod 被删除时,该 token 就会失效。
    • 用户可以按以下配置,手动创建一个 secret 并关联到 ServiceAccount 。这样的 secret 会长期有效,可以给 k8s 外部的程序使用。
      apiVersion: v1
      kind: Secret
      metadata:
        annotations:
          kubernetes.io/service-account.name: <str>
        name: <str>
        namespace: <str>
      type: kubernetes.io/service-account-token
      

# 鉴权

  • k8s 支持多种鉴权模块:

    • Node :用于控制 kubelet 对已调度的 Pod 的权限,比如读取 ConfigMap、修改 Pod 状态。
    • ABAC :基于属性的访问控制,根据用户的属性,决定其权限。
    • RBAC :基于角色的访问控制,根据用户所属的角色,决定其权限。
    • Webhook :发送 HTTP 请求给第三方,根据响应报文决定权限。
  • k8s 支持同时启用多个鉴权模块。

    • 启动 apiserver 时,默认会通过命令行参数,启用一些鉴权模块:
      kube-apiserver --authorization-mode=Node,RBAC
      
    • k8s 处理一个客户端请求时,会依次调用各个鉴权模块。
      • 如果某个鉴权模块批准或拒绝该请求,则立即结束鉴权。
      • 如果所有鉴权模块都未决策,则默认拒绝该请求。
  • 可用以下命令,测试客户端是否有权执行某个操作:

    kubectl auth can-i \
        create deployments
    
    • 输出为 yes 或 no 。

# RBAC

  • RBAC 鉴权模块定义了四种对象:

    • Role :角色,作用于某个 namespace 。
    • RoleBinding :在某个 namespace 中,将一个 Role 或 ClusterRole 角色,绑定到一些用户。
    • ClusterRole :集群角色,作用于集群全局。
    • ClusterRoleBinding :将角色绑定到用户,作用于集群全局。
  • Role 的配置示例:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: pod-reader
      namespace: default
    rules:                  # 角色的权限,可声明多条规则
    - apiGroups: [""]       # 第一条规则,允许通过 API 组对哪些 resources 执行哪些 verbs 操作
      resources:
        - pods
        - pods/log          # 允许访问 pods 的子资源 log
      # resourceNames:      # 只允许访问指定名称的资源。默认不限制名称
      #   - nginx
      verbs:
        - get
        - list
        - watch
    # aggregationRule: ...  # 定义 ClusterRole 时,可通过聚合功能,继承其它多个 ClusterRole
    
  • RoleBinding 的配置示例:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: pod-reader
      namespace: default
    roleRef:                # 要绑定的角色。创建 Binding 之后不允许修改该配置
      apiGroup: rbac.authorization.k8s.io
      kind: Role            # 可以为 Role 或 ClusterRole
      name: pod-reader
    subjects:               # 主体内容,声明要绑定的用户
    - apiGroup: rbac.authorization.k8s.io
      kind: User
      name: leo
    - apiGroup: rbac.authorization.k8s.io
      kind: Group
      name: system:authenticated    # 一个内置的用户组,表示所有通过身份认证的用户
    - kind: ServiceAccount
      name: default
    
  • ClusterRoleBinding 的配置示例:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: admin
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: admin
    subjects:
      - apiGroup: rbac.authorization.k8s.io
        kind: User
        name: leo
      - kind: ServiceAccount
        name: admin
        namespace: kube-system
        # 这里必须声明 namespace ,才能定位该 ServiceAccount ,因为 ClusterRoleBinding 本身不关联 namespace
        # ServiceAccount 虽然来自某个 namespace ,但绑定 ClusterRole 之后,有权访问所有 namespace 的资源
    

# 客户端示例

  • 使用 kubecbtl 作为客户端时,会从 kubeconfig 配置文件中获取 CA 证书、token ,从而连接 apiserver 。

  • 可以用 kubectl 反向代理 apiserver ,此时发向 proxy 的 HTTP 请求不必采用 SSL、不需要 token 。

    kubectl proxy &
    curl 127.0.0.1:8001/
    
  • 可以用 curl 命令访问 k8s :

    TOKEN=`kubectl config view --raw | yq '.users[0].user.token'` # 获取 token
    curl https://apiserver -H "Authorization: Bearer $TOKEN" -k   # 用 -k 选项跳过 SSL 认证
    
    kubectl config view --raw | grep certificate-authority-data | awk '{print $2}' | base64 -d > ca.crt       # 获取 k8s 的 ca 证书
    kubectl config view --raw | grep client-certificate-data    | awk '{print $2}' | base64 -d > client.pem   # 获取客户端的证书
    kubectl config view --raw | grep client-key-data            | awk '{print $2}' | base64 -d > client-key.pem
    curl https://apiserver -H "Authorization: Bearer $TOKEN" --cacert ca.crt --cert client.pem --key client-key.pem
    
  • URL 示例:

    # k8s 核心资源的 apiVersion 只包含版本号,比如 v1 ,因此 URL 开头为 /api/${apiVersion}/
    GET /api/v1/namespaces                # 查询所有命名空间
    GET /api/v1/pods                      # 查询所有 pod ,不限命名空间
    GET /api/v1/namespaces/default/pods   # 查询指定命名空间的 pod
    GET /api/v1/services
    
    # k8s 普通资源的 apiVersion 由 API Group 和版本号组成,比如 apps/v1 ,因此 URL 开头为 /apis/${apiVersion}/
    GET /apis/apps/v1/namespaces/default/deployments        # 查询指定命名空间下,所有 deployment
    GET /apis/apps/v1/namespaces/default/deployments/$name  # 查询指定名称的 deployment