# 权限

  • apiserver 等 k8s 组件提供了 Restful API 供外部访问,并且启用了以下安全措施:
    • 身份认证策略
    • 鉴权策略

# 身份认证

  • 客户端发送 HTTP 请求到 k8s 时,需要进行身份认证。

    • 如果一个 HTTP 请求未通过身份认证,则会被 k8s 视作 system:anonymous 用户,属于 system:unauthenticated 用户组。
      • 默认禁止匿名用户的请求,会返回响应 403: Forbidden
  • k8s 支持多种身份认证方式:

    • SSL 客户端证书:用于验证客户端的身份,证书中的 CN 字段记录了用户名。
      • 部署 k8s 集群时,默认会让 apiserver、kubelet、etcd 等组件分别使用自签名的 SSL 证书,从而保障 k8s 组件之间的通信安全。
    • ServiceAccount Token :需要客户端在 HTTP 请求头中加入 Authorization: Bearer <token>
    • Bootstrap Token :用于部署 k8s 集群、新增节点。
    • 支持集群外的身份认证服务,比如 LDAP、Kerberos、OIDC 。
  • k8s 将账户分为两类:

    • User :供自然人使用,比如 kubectl 。
      • 不能通过 k8s API 创建,要通过 SSL 证书等方式创建,比较麻烦。
    • ServiceAccount :供应用程序使用。
      • User 作用于集群全局,而 ServiceAccount 会被 namespace 隔离。
      • 创建一个 ServiceAccount 时,会自动创建并关联一个 secret ,包含了身份凭证,命名格式为 <ServiceAccount>-token-<random_id>
        • 如果删除该 secret ,则会自动创建一个新 id 的 secret 。
        • 如果删除该 ServiceAccount ,则会自动删除相应的 secret 。

# 示例

  • 创建 Pod 时可以配置 ServiceAccount :

    spec:
      serviceAccountName: default         # 该 Pod 采用的 ServiceAccount ,如果不存在则不能创建 Pod 。默认为 default
      automountServiceAccountToken: true  # 是否自动将 ServiceAccount 关联的 secret 挂载到 Pod 的 /var/run/secrets/kubernetes.io/serviceaccount/ 目录下。默认为 true
    
    • k8s 默认会在每个 namespace 下创建一个名为 default 的 ServiceAccount 。
  • ServiceAccount 的配置示例:

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: promethues
      namespace: default  # 该 ServiceAccount 所属的命名空间,默认为 default
    # secrets:            # 创建 ServiceAccount 之后会自动关联 secret
    # - name: promethues-token-zqltx
    
  • ServiceAccount 的 secret 示例:

    apiVersion: v1
    data:
      ca.crt: ******      # CA 证书,用于验证服务器的身份
      namespace: ******
      token: ******       # 用于验证 ServiceAccount 的身份
    kind: Secret
    metadata:
      annotations:
        kubernetes.io/service-account.name: default
        kubernetes.io/service-account.uid: ******
      name: promethues-token-zqltx
      namespace: default
    type: kubernetes.io/service-account-token
    

# 鉴权

  • 客户端通过身份认证之后,k8s 会根据鉴权模块分配权限。

  • k8s 支持多种鉴权模块:

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

    • 启动 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:               # 主体内容,声明要绑定的用户
    - kind: User
      name: leo
      apiGroup: rbac.authorization.k8s.io
    - kind: Group
      name: system:authenticated    # 一个内置的用户组,表示所有通过身份认证的用户
      apiGroup: rbac.authorization.k8s.io
    - kind: ServiceAccount
      name: default
      namespace: kube-system
    

# 客户端示例

  • 使用 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