**k8s集群安全控制包括API访问控制、Pod安全策略、网络安全策略。**



## **API访问控制**

### **用户类别**

**k8s有两类用户：人类用户和service account**

- **k8s没有User对象维护人类用户信息，API-server是通过一个文件保存用户的token相关信息。**
- **service account是集群内的进程程序使用的。Service account被绑定到特定的命名空间，通过API server创建，保存在secrets中并挂载到pod里，pod进程会用其中的token与API-serve交互。Pod定义中未指定service account时，会使用命名空间中default的service account。**



### **访问控制**

**API serve访问过程中的控制分为三个阶段。**

![](https://figure-bed-1304788733.cos.ap-guangzhou.myqcloud.com/typora/202207041103083.png)



### <font color='blue'>Authentication</font>

Authentication的认证有多种方式，可以组合使用。组合情况下，有验证结果的作为该环节的结果。service account会使用token方式验证，普通用户则需要另外再选择一种方式。验证通过的情况下，会得到用户的信息。用户信息中username被后续环节使用。

- X509：客户端和服务端从CA机构获取证书，验证对方证书的有效性，从而确认对方身份。
- HTTP Bearer Token：API server用csv文件保存用户信息，验证HTTP header中的token，获取用户名，并与csv文件对照判断用户是否合法。ServiceAccount本质使用的就是该方法。
- OpenID第三方认证
- Webhook Token：API server从需要认证的请求Header中获取token，发送到Webhook服务进行认证，根据返回到结果决定认证有效性。
- Authenticating Proxy （认证代理）



### <font color='blue'>Authorization</font>

API Server通过启动参数设置多种授权策略；可以组合使用。组合情况下，有验证结果的作为该环节的结果。授权策略选项包括：

- AlwaysAllow：允许所有
- AlwaysDeny：拒绝所有
- Node：针对kubelet发起的请求特殊授权模式，限制每个kubelet只能访问自身所在Node运行的pod，包括读取、写入、授权相关操作的限制
- Webhook：通过webhook模式获取授权结果
- RBAC：基于角色进行授权



#### RBAC授权

**主要分为两个步骤为用户设置相关权限，首先创建包含一组权限集合的Role/ClusterRole，再创建RoleBinding将权限配置到具体用户身上。Role和RoleBinding只能作用在具体命名空间内。通常会创建若干RoleBinding，再根据需要在各个命名空间引用ClusterRole，从而作用在具体命名空间中。用户无法修改与之绑定的Role或ClusterRole，需要变更时应先删除再重新绑定。**

![](https://figure-bed-1304788733.cos.ap-guangzhou.myqcloud.com/typora/202207041129899.png)



**Role、ClusterRole定义**

在 RBAC API 中，角色包含了一组授权规则。此处的授权规则是纯粹的“授予”规则（没有“否定”规则）。角色可以用以下两种形式定义：

- 名称空间中的 `Role`
- 集群范围内的 `ClusterRole`

`Role` 只能用来授权访问单个名称空间内部的资源。下面的例子中定义的 `Role` 授权读取 `default` 名称空间中的 Pod

`ClusterRole` 可以用来授予与 `Role` 相同的权限，但是由于 `ClusterRole` 是集群范围内的，也可以用来授权访问如下资源：

- 集群范围内的资源（例如节点）
- 非资源性质的端口（例如 "/healthz"）
- 所有名称空间内的资源（例如 Pod，授权后可以使用这类语句 `kubectl get pods --all-namespaces`）

下面的 `ClusterRole` 可以授权读取任意特定名称空间的 secrets，或所有名称空间的 secrets（取决于如何 [绑定](https://kuboard.cn/learning/k8s-advanced/sec/rbac/api.html#RoleBinding和ClusterRoleBinding)）：

```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""] # "" indicates the core API group
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
---
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"]
```





**RoleBinding和ClusterRoleBinding的聚合定义**

角色绑定将角色中定义的权限授予给一个用户或者一组用户。角色绑定包含了一个被授权对象的列表（user、group、service account），以及一个被授予的角色的引用。角色绑定有如下两种定义方式：

- 名称空间内的 `RoleBinding`
- 集群范围内的 `ClusterRoleBinding`

`RoleBinding` 可以引用同名称空间下的 `Role`。下面的 `RoleBinding` 将 “default” 名称空间中的角色 “pod-reader” 授予给用户 “jane”，此时 “jane” 可以读取 “default” 名称空间中的 pod。

- `roleRef`：引用被授予的角色

  - `kind` 可以是 `Role` 或者 `ClusterRole`
  - `name` 是被引用的 `Role` 或者 `ClusterRole` 的名称

此例中的 RoleBinding 使用 `roleRef` 将用户 “jane” 绑定到上面创建的 `pod-reader` 这个 `Role`

`ClusterRoleBinding` 可以被用来授权访问集群级别的资源，以及所有名称空间中的资源。下面的 `ClusterRoleBinding` 允许 “manager” 用户组中的用户读取任何名称空间中的 secrets：

```yaml
apiVersion: rbac.authorization.k8s.io/v1
# This role binding allows "jane" to read pods in the "default" namespace.
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
# You can specify more than one "subjects"
subjects:
- 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
---
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
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring
aggregationRule:  # 多个roleBinding的聚合
  clusterRoleSelectors:
  - matchLabels:
      rbac.example.com/aggregate-to-monitoring: "true"
rules: [] # The control plane automatically fills in the rules
```

**<font color='blue'>系统默认设置的Role和用户</font>**

`RoleBinding`、`ClusterRoleBinding` 创建后，其 `roleRef` 字段不可修改，如果尝试修改则会报错。如果需要修改 `roleRef` 字段，必须将 `RoleBinding` 或 `ClusterRoleBinding` 删除后重新创建。这样做的主要原因有如下两点：

1. `roleRef` 不同的话，本质上是完全不同的绑定关系。要求删除并重建 `RoleBinding` 或 `ClusterRoleBinding` 以修改 `roleRef` 字段，可以确保列表中的被授权的对象（用户、用户组、Service Account）都是经过考虑的（如果直接修改 `roleRef` 字段，用户将不会核对列表中的用户、用户组、Service Account是否应该被授予新的角色）
2. 不允许修改 `roleRef` 字段的情况下，可以将修改 `RoleBinding`、`ClusterRoleBinding` 的权限授予给某个用户，使其可以管理其中的被授权对象列表（用户、用户组、Service Account），但是不能够修改对应的角色（权限）。

`kubectl auth reconcile` 命令行工具可以创建或更新包含 RBAC 对象的描述文件，删除、重新创建 `RoleBinding`、`ClusterRoleBinding`。更多信息请参考 [command usage and examples](https://kuboard.cn/learning/k8s-advanced/sec/rbac/cmd.html#kubectl-auth-reconcile)



#### 聚合ClusterRole

当需要同时使用多个ClusterRole的授权规则时，可以创建一个新的ClusterRole，通过aggregationRule字段设置需要包含的ClusterRole

```yaml
# 创建一个聚合ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring
aggregationRule:
  clusterRoleSelectors:
  - matchLabels:
      rbac.example.com/aggregate-to-monitoring: "true"
rules: [] # 控制面自动填充这里的规则
```



### Admission Controllers

在通过认证和授权后，需要进行准入控制和校验。有两种方式配置要使用的准入控制规则：

- API Server在启动时，通过设置--enable-admission-plugins来启用使用的插件。该方式只针对k8s自带的插件
- 通过webhook的方式，有两种admission webhook：
  - ValidatingAdmissionWebhook：用于验证创建对象，是否允许资源创建
  - MutatingAdmissionWebhook：在开始创建对象时，请求会先发到编写的controller中，做一些操作。如：注入操作、优化



## Pod安全策略

Pod的安全策略是通过设置security context 限制不可信的容器行为，从而保护系统和其他容器不受影响。Security context的设置有三种方式：

- 容器级别配置：只影响该容器本身，且会覆盖pod级别的设置，不影响Volume
- pod级别配置：影响pod中的容器及Volume
- Pod Security Policies（PSP）：应用到集群内所有Pod和Volume

### pod和容器中的security context配置

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: hello-world
spec:
  securityContext:
    privileged: false  #会被container定义的覆盖
    fsGroup: 1234
    supplementalGroups: [5678]
    seLinuxOptions:
      level: "s0:c123,cd456"
  containers:
  - name: hello-world-container
    # The container definition
    # ...
    securityContext:
      privileged: true
```



### Pod Security Policy

PSP是通过admission插件定义的创建合规性检查

1. 启动PSP准入控制器

   kube-apiserver启动时，参数设置为：--enable-admission-plugins=· · ·，PodSecurityPolicy

2. 创建一个PSP对象

   ```yaml
   apiVersion: policy/v1beta1
   kind: PodSecurityPolicy
   metadata:
     name: no-privilege
   spec:
     privileged: false
     allowPrivilegeEscalation: true
     volumes:
     - '*'
     runAsUser:
       rule: 'RunAsAny'
     seLinux:
       rule: 'RunAsAny'
     supplementalGroups:
       rule: 'RunAsAny'
     fsGroup:
       rule: 'RunAsAny'
   ```

3. 借助RBAC设置PSP

   ```yaml
   kind: ClusterRole
   apiVersion: rbac.authorization.k8s.io/v1
   metadata:
     name: no-privilege:no-privilege
   rules:
   - apiGroups:
     - policy   #psp的组，也可以写成个列表
     - extensions
     resources:
     - podsecuritypolicies   #api资源名称
     resourceNames:
     - restrictive   #psp的名称
     verbs:
     - use   #操作名称  
   ---
   kind: ClusterRoleBinding
   apiVersion: rbac.authorization.k8s.io/v1
   metadata:
     name: no-privilege:no-privilege
   subjects:
   - kind: Group  # 授权给 kube-system 下面的 serviceaccount，group表示一组用户，可以是普通用户也可以是serviceaccount
     name: system:serviceaccounts   #该group的名称
     namespace: kube-system
     apiGroup: rbac.authorization.k8s.io
   roleRef:
     kind: ClusterRole
     name: no-privilege:no-privilege
     apiGroup: rbac.authorization.k8s.io
   ```



### 支持的安全策略

|           分类           |               控制项                |                 说明                 |
| :----------------------: | :---------------------------------: | :----------------------------------: |
|                          |           **Privileged**            |           **运行特权容器**           |
|    **Linux能力相关**     |     **DefaultAddCapabilities**      |   **可添加到容器的 Capabilities**    |
|    **Linux能力相关**     |    **RequiredDropCapabilities**     |  **会从容器中删除的 Capabilities**   |
|    **Linux能力相关**     |       **AllowedCapabilities**       |   **允许使用的 Capabilities 列表**   |
|  **宿主机命名空间相关**  |           **HostNetwork**           |        **允许使用 host 网络**        |
|  **宿主机命名空间相关**  |            **HostPorts**            |       **允许的 host 端口列表**       |
|  **宿主机命名空间相关**  |             **HostPID**             |     **使用 host PID namespace**      |
|  **宿主机命名空间相关**  |             **HostIPC**             |     **使用 host IPC namespace**      |
|                          |             **SELinux**             |         **SELinux Context**          |
|     **用户和组相关**     |            **RunAsUser**            |      **运行容器的user ID范围**       |
|     **用户和组相关**     |           **RunAsGroup**            |      **运行容器的user ID范围**       |
|     **用户和组相关**     |       **SupplementalGroups**        |         **允许的补充用户组**         |
| **存储卷和文件系统相关** |             **FSGroup**             |          **volume FSGroup**          |
| **存储卷和文件系统相关** |             **Volumes**             |   **控制容器可以使用哪些 volume**    |
| **存储卷和文件系统相关** |     **ReadOnlyRootFilesystem**      |          **只读根文件系统**          |
| **存储卷和文件系统相关** |        **AllowedHostPaths**         | **允许 hostpath 插件使用的路径列表** |
|                          |        **AllowedFlexVolume**        |  **允许使用的 flexVolume 插件列表**  |
|                          |                                     |                                      |
|     **提升权限相关**     |    **AllowPrivilegeEscalation**     |  **允许容器进程设置 no_new_privs**   |
|     **提升权限相关**     | **DefaultAllowPrivilegeEscalation** |       **默认是否允许特权升级**       |

**[官方列表](https://kubernetes.io/zh-cn/docs/concepts/security/pod-security-policy/#policy-reference)**



## 网络安全策略

通过定义Network Policy对象实现Pod之间网络的访问策略。可以限制流量的进和出两个方向。

```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: access-nginx
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: nginx
  policyTypes:
    - Ingress
    - Egress  # 如果下面没有具体指出哪些pod，则是禁止所有。如果没有本行，表示不对egress做限制，都可以出；但是有本行就是做限制，但下面没有定义就是禁止出，想不禁止的话就删掉或者写好白名单。详细看第二个
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              project: myproject
        - podSelector:
            matchLabels:
              role: frontend
      ports:
        - protocol: TCP
          port: 6379
  egress:
    - to:
        - ipBlock:
            cidr: 10.0.0.0/24
      ports:
        - protocol: TCP
          port: 5978
```



```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - ipBlock:
            cidr: 172.17.0.0/16
            except:
              - 172.17.1.0/24
        - namespaceSelector:
            matchLabels:
              project: myproject
        - podSelector:
            matchLabels:
              role: frontend
      ports:
        - protocol: TCP
          port: 6379
  egress:
    - to:
        - ipBlock:
            cidr: 10.0.0.0/24
      ports:
        - protocol: TCP
          port: 5978
```

**必需字段**：与所有其他的 Kubernetes 配置一样，NetworkPolicy 需要 `apiVersion`、 `kind` 和 `metadata` 字段。关于配置文件操作的一般信息，请参考 [配置 Pod 以使用 ConfigMap](https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-pod-configmap/), 和[对象管理](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/object-management)。

**spec**：NetworkPolicy [规约](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status) 中包含了在一个名字空间中定义特定网络策略所需的所有信息。

**podSelector**：每个 NetworkPolicy 都包括一个 `podSelector`，它对该策略所 适用的一组 Pod 进行选择。示例中的策略选择带有 "role=db" 标签的 Pod。 空的 `podSelector` 选择名字空间下的所有 Pod。

**policyTypes**: 每个 NetworkPolicy 都包含一个 `policyTypes` 列表，其中包含 `Ingress` 或 `Egress` 或两者兼具。`policyTypes` 字段表示给定的策略是应用于 进入所选 Pod 的入站流量还是来自所选 Pod 的出站流量，或两者兼有。 如果 NetworkPolicy 未指定 `policyTypes` 则默认情况下始终设置 `Ingress`； 如果 NetworkPolicy 有任何出口规则的话则设置 `Egress`。

**ingress**: 每个 NetworkPolicy 可包含一个 `ingress` 规则的白名单列表。 每个规则都允许同时匹配 `from` 和 `ports` 部分的流量。示例策略中包含一条 简单的规则：它匹配某个特定端口，来自三个来源中的一个，第一个通过 `ipBlock` 指定，第二个通过 `namespaceSelector` 指定，第三个通过 `podSelector` 指定。

**egress**: 每个 NetworkPolicy 可包含一个 `egress` 规则的白名单列表。 每个规则都允许匹配 `to` 和 `port` 部分的流量。该示例策略包含一条规则， 该规则将指定端口上的流量匹配到 `10.0.0.0/24` 中的任何目的地。

所以，该网络策略示例:

1. 隔离 "default" 名字空间下 "role=db" 的 Pod （如果它们不是已经被隔离的话）。
2. （Ingress 规则）允许以下 Pod 连接到 "default" 名字空间下的带有 "role=db" 标签的所有 Pod 的 6379 TCP 端口：
   - "default" 名字空间下带有 "role=frontend" 标签的所有 Pod
   - 带有 "project=myproject" 标签的所有名字空间中的 Pod
   - IP 地址范围为 172.17.0.0–172.17.0.255 和 172.17.2.0–172.17.255.255 （即，除了 172.17.1.0/24 之外的所有 172.17.0.0/16）
3. （Egress 规则）允许 “default” 命名空间中任何带有标签 “role=db” 的 Pod 到 CIDR 10.0.0.0/24 下 5978 TCP 端口的连接。

**[官方教程案例](https://kubernetes.io/zh-cn/docs/concepts/services-networking/network-policies/)**



## RBAC总结

### Role

用于定义某个命名空间的角色的权限。

### ClusterRole

用于定义整个集群的角色的权限。

### RoleBinding

将角色中定义的权限赋予一个或者一组用户，针对命名空间执行授权。

### ClusterRoleBinding

将角色中定义的权限赋予一个或者一组用户，针对集群范围内的命名空间执行授权。



在 RBAC API 中，一个角色包含一组相关权限的规则。权限是纯粹累加的（不存在拒绝某操作的规则）。 角色可以用 Role 来定义到某个命名空间上， 或者用 ClusterRole 来定义到整个集群作用域。