# 权限

  • 客户端通发送 HTTP 请求到 apiserver 时,需要依次通过以下检查,请求才会生效。
    • 认证(Authentication)
      • :客户端需要采用某种身份认证方式,证明自己是 k8s 中的一个合法用户。
      • 如果客户端未进行身份认证,则会被 apiserver 视作 system:anonymous 用户,也就是匿名用户,属于 system:unauthenticated 用户组。
        • apiserver 默认会拒绝匿名用户的请求,返回 HTTP 403 Forbidden 响应报文。
      • 如果客户端进行了身份认证,但认证失败。比如使用一个无效的 token 。则 apiserver 会返回 HTTP 401 Unauthorized 响应报文。
    • 限流(Rate Limit)
      • 例如启动 apiserver 时,可添加命令行参数 --max-requests-inflight=xx --max-mutating-requests-inflight=xx ,限制 apiserver 接收读、写请求的最大并发数。
    • 鉴权(Authorization)
      • :客户端通过身份认证之后,apiserver 会根据鉴权模块,确定客户端有权进行哪些操作。
    • 准入控制(Admission Control)
      • :客户端通过认证、鉴权环节之后,会进入准入控制环节,进行更多方面的审核。
      • 如果客户端发出的 HTTP 请求属于写请求,则 apiserver 会执行所有准入控制插件。全部通过之后,才会将写请求持久化到 etcd 。任何一个不通过,都会拒绝请求。
      • 如果属于读请求,则跳过准入控制环节。

# 认证

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

    • SSL 客户端证书
      • :用于验证客户端的身份,证书中的 CN 字段记录了用户名。
      • 部署 k8s 集群时,默认会让 apiserver、kubelet、etcd 等组件分别使用自签名的 SSL 证书,从而保障 k8s 组件之间的通信安全。
    • ServiceAccount token
      • :创建 ServiceAccount 时默认会绑定一个 token ,供 Pod 内程序使用。
    • bootstrap token
      • :用于部署 k8s 集群、新增节点。
    • static token file
      • :可以在 apiserver 的启动命令中加入 --token-auth-file=tokens.csv ,启用一个静态的 bearer token 列表。
      • tokens.csv 中每行记录一个 token 以及对应的用户,格式为 <token>,<username>,<uid>,<group>
      • 通过 tokens.csv 可以添加多个用户。这些用户还需要绑定 RBAC 角色,才有权访问 k8s 资源。
    • 此外,还支持集群外的身份认证服务,比如 LDAP、Kerberos、OIDC 。
    • k8s 不支持账号密码的认证方式,比如 HTTP Basic Auth 。
  • k8s 的账户分为两类:

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

    • 客户端可以向 apiserver 发送 HTTP 请求,并在请求头中加入 Authorization: Bearer <token>
    • 可以将 token 填入 kubeconfig 文件,然后供 kubectl 命令调用。

# 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 的资源
    

# 客户端示例

  • 客户端通发送 HTTP 请求到 apiserver 时,首先要通过身份认证。

    • 例如使用 kubecbtl 作为客户端时,会从 kubeconfig 配置文件中获取 CA 证书、token 。然后 kubectl 发送 HTTP 请求到 apiserver 时,会携带这些信息,从而通过身份认证。
    • 用户也可以用 curl 命令作为客户端,访问 apiserver :
      TOKEN=`kubectl config view --raw | yq '.users[0].user.token'` # 从 kubeconfig 中提取 token
      curl https://apiserver -H "Authorization: Bearer $TOKEN" -k   # 访问 apiserver
      
      apiserver 本身采用的 SSL 证书通常是自签名的,不被 curl 命令认可。因此上述命令添加了 -k 选项,表示不检查 apiserver 的 SSL 证书是否有效。
      也可以让 curl 命令认可自签名证书,如下:
      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
      
  • 可以用 kubectl 反向代理 apiserver :

    kubectl proxy &
    
    • kubectl proxy 默认监听 8001 端口。用户可以向 kubectl proxy 发送未经认证的 HTTP 请求,比如执行 curl 127.0.0.1:8001/
    • kubectl proxy 会将收到的 HTTP 请求,转发给 apiserver ,并通过 apiserver 的身份认证。
    • 这种做法不安全,常用于临时调试 apiserver 的接口。
  • apiserver 的接口示例:

    # 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
    

# 准入控制

  • 一个准入控制插件,可能提供两种功能:

    • 验证(Validating)
      • :检查客户端的写请求,如果满足某种条件,则放行。
    • 变更(Mutating)
      • :修改对象的 YAML 配置。
      • 例如客户端请求创建一个 Pod 时,自动修改该 Pod 。
      • 有的插件只提供 Validating 功能,有的插件只提供 Mutating 功能,有的插件提供了两种功能。
  • 启动 apiserver 时,默认会通过命令行参数,启用一些准入控制插件:

    kube-apiserver --enable-admission-plugins=LimitRanger,PodSecurity,...
    
  • 举例几种插件:

    AlwaysPullImages    # 新建 Pod 时,将 imagePullPolicy 设置为 Always
    DefaultStorageClass # 新建 PVC 时,如果未声明 StorageClass ,则设置默认值
    EventRateLimit      # 限制写请求的 qps
    LimitRanger         # 新建 Pod 时,检查它是否符合 LimitRange 限制
    PodSecurity         # 新建 Pod 时,检查其 securityContext 是否符合安全标准 https://kubernetes.io/zh-cn/docs/concepts/security/pod-security-standards/