# 使用AppArmor限制容器对资源的访问
AppArmor（Application Armor）是Linux内核的一个安全模块，通常在Ubuntu等操作系统中使用，AppArmor允许系统管理员将每个程序与一个安全配置文件关联，从而限制程序的功能。简单来说，AppArmor是与SELinux类似的一个访问控制系统，通过它可以指定程序可以读、写或运行哪些文件，是否可以打开网络端口等。作为对传统Unix的自主访问控制模块的补充，AppArmor提供了强制访问控制机制，它已经被整合到2.6版本的Linux内核中。
本文为您介绍如何在CCE集群的Ubuntu节点上使用AppArmor限制容器对资源的访问。
#### 约束与限制
- 此特性仅在v1.31.6-r0及以上版本的集群中支持，且节点需使用Ubuntu操作系统，请确保您的集群版本和节点操作系统符合要求。
- 集群中的Everest插件要求2.4.158版本及以上。
- 在开启AppArmor之后，即使Pod不配置appArmorProfile参数，Docker或Containerd都会对调度到本节点的Pod应用默认AppArmor规则，除非显式指定Pod的appArmorProfile.type参数为Unconfined，配置详情请参见[使用AppArmor规则保护Pod]。
- 执行[重置节点](https://support.huaweicloud.com/usermanual-cce/cce_10_0003.html)或[同步节点池](https://support.huaweicloud.com/usermanual-cce/cce_10_0654.html)等触发节点重置的操作会导致AppArmor功能被关闭，请谨慎操作。如果您确实需要关闭AppArmor功能，请通过重置节点的方式进行，暂不支持手动关闭。
 
#### 节点启用AppArmor
1. 登录Ubuntu节点，安装AppArmor模块。 
   ```
   sudo DEBIAN_FRONTEND=noninteractive apt-get -y install apparmor
   ```
   ![](https://support.huaweicloud.com/usermanual-cce/public_sys-resources/caution_3.0-zh-cn.png)
   如果使用私有Ubuntu镜像，请提前在系统镜像中预装apparmor。
   
   
2. 查看AppArmor模块状态。 
   ```
   sudo aa-status
   ```
   回显如下，表示AppArmor模块已经装载。
   ```
   apparmor module is loaded.
   ```
   
   
3. 启动AppArmor服务，并设置开机自动。重启节点时，将自动重新加载AppArmor规则。 
   ```
   sudo systemctl enable apparmor
   sudo systemctl start apparmor
   ```
   
   
4. 执行以下命令确认系统已经开启，返回Y表示已开启。 
   ```
   sudo cat /sys/module/apparmor/parameters/enabled
   ```
   以上参数会被kubelet读取，用于节点启用AppArmor的校验。当节点没有开启AppArmor时，设置了AppArmor的Pod调度到此节点会被拦截。
   重启kubelet读取该参数（kubelet 1.31版本GA，默认开启AppArmor）：
   ```
   sudo systemctl restart kubelet
   ```
   
   
 
#### 容器运行时开启AppArmor
**容器运行时为containerd**
1. 执行以下命令修改containerd配置。
   ```
   vim /etc/containerd/config.toml
   ```
   
2. 修改以下内容并保存。
   ```
   [plugins."io.containerd.grpc.v1.cri"]
     disable_apparmor = false
   ```
   
3. 重启containerd。
   ```
   sudo systemctl restart containerd
   ```
   
**容器运行时为docker**
1. 执行以下命令修改docker配置。
   ```
   vim /etc/default/docker
   ```
   
2. 使用#注释以下内容：
   ```
   # container='disable apparmor'
   ```
   
3. 重启docker。
   ```
   sudo systemctl restart docker
   ```
   
 
 #### 使用AppArmor规则保护Pod
AppArmor配置文件可以在Pod级别或容器级别指定。如果在Pod级别设置配置文件，该配置将被用作Pod中所有容器（包括Init、Sidecar和临时容器）的默认配置文件。如果同时设置了Pod和容器AppArmor配置文件，则优先使用容器级别的AppArmor配置文件。配置示例如下：
```
...
securityContext:
  appArmorProfile:
    type: <profile_type>
...
```
其中\<profile_type\>的取值如下：
- Localhost：使用节点上加载的AppArmor配置文件，详情请参见[示例：使用自定义AppArmor规则对Pod进行限制]。
- RuntimeDefault：使用运行时的默认配置文件。
- Unconfined：不强制执行AppArmor。
有关AppArmor配置文件API的完整详细信息，请参见[指定AppArmor限制](https://kubernetes.io/zh-cn/docs/tutorials/security/apparmor/#specifying-apparmor-confinement)。
![](https://support.huaweicloud.com/usermanual-cce/public_sys-resources/caution_3.0-zh-cn.png)
当Pod同时开启特权容器（设置privileged: true）时，AppArmor配置生效规则请参见[特权容器开启AppArmor后的生效策略]。
您可以通过检查容器根进程的进程属性来检查该进程是否正在使用正确的配置文件运行：
```
kubectl exec <pod_name> -- cat /proc/1/attr/current
```
输出应如下所示：
```
cri-containerd.apparmor.d (enforce)
```
 #### 示例：使用自定义AppArmor规则对Pod进行限制
1. 将要使用的自定义AppArmor规则配置文件加载到节点上，配置文件名称可自定义。 
   ```
   vim /etc/apparmor.d/k8s-apparmor-example-deny-write
   ```
   示例如下，该配置文件阻止所有文件写入操作，更多关于配置文件写作语法的说明请参见[AppArmor配置文件快速指南](https://gitlab.com/apparmor/apparmor/-/wikis/QuickProfileLanguage)。
   ```
   #include <tunables/global>
   profile k8s-apparmor-example-deny-write flags=(attach_disconnected) {
     #include <abstractions/base>
     file,
     # 拒绝所有文件写入
     deny /** w,
   }
   ```
   加载配置文件：
   ```
   apparmor_parser -r /etc/apparmor.d/k8s-apparmor-example-deny-write
   ```
   ![](https://support.huaweicloud.com/usermanual-cce/public_sys-resources/caution_3.0-zh-cn.png)
   AppArmor规则配置文件需要加载到每个需要使用该功能的节点上。
   
   
2. 创建一个简单的Pod，并为该Pod添加上一步拒绝写入文件的AppArmor规则。 
   创建hello-apparmor.yaml文件，内容如下：
   ```
   apiVersion: v1
   kind: Pod
   metadata:
     name: hello-apparmor
   spec:
     securityContext:
       appArmorProfile:
         type: Localhost  # 使用节点上加载的AppArmor配置文件
         localhostProfile: k8s-apparmor-example-deny-write  # 指定节点上加载的AppArmor配置文件名称
     containers:
     - name: hello
       image: busybox:1.28
       command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]
   ```
   ![](https://support.huaweicloud.com/usermanual-cce/public_sys-resources/caution_3.0-zh-cn.png)
   如果集群中包含不支持AppArmor的节点，则还需要使用亲和性调度将Pod指定调度到支持AppArmor的节点上，具体操作请参见[设置节点亲和调度（nodeAffinity）](https://support.huaweicloud.com/usermanual-cce/cce_10_0892.html)。
   
   
3. 创建示例Pod。 
   ```
   kubectl create -f hello-apparmor.yaml
   ```
   
   
4. 当Pod运行后，通过检查容器的/proc/1/attr/current文件来验证容器是否确实使用AppArmor规则配置文件运行： 
   ```
   kubectl exec hello-apparmor -- cat /proc/1/attr/current
   ```
   输出以下内容表示AppArmor规则已经应用到容器：
   ```
   k8s-apparmor-example-deny-write (enforce)
   ```
   
   
5. 通过写入文件来违反AppArmor规则，测试配置是否生效。 
   ```
   kubectl exec hello-apparmor -- touch /tmp/test
   ```
   回显如下，表示写入操作被拒绝，AppArmor规则生效：
   ```
   touch: /tmp/test: Permission denied
   command terminated with exit code 1
   ```
   
   
 
 #### 特权容器开启AppArmor后的生效策略
如果Pod同时开启特权容器（设置privileged: true），则AppArmor配置生效规则如下：
| Pod AppArmor配置 | 未使用特权容器              | 使用特权容器（privileged: true） |
|:---|:---|:---|
| 未开启AppArmor    | 使用容器运行时默认策略          | 不强制执行AppArmor               |
| RuntimeDefault  | 使用容器运行时默认策略         | 不强制执行AppArmor              |
| Unconfined   | 不强制执行AppArmor       | 不强制执行AppArmor             |
| Localhost     | 使用主机上加载的AppArmor配置文件 | 使用主机上加载的AppArmor配置文件     |
   
#### 相关文档
- [AppArmor官方网站](https://apparmor.net/)
- [在Ubuntu中使用AppArmor](https://documentation.ubuntu.com/server/how-to/security/apparmor/index.html)
- [使用AppArmor限制容器对资源的访问](https://kubernetes.io/zh-cn/docs/tutorials/security/apparmor/)
- [AppArmor配置文件快速指南](https://gitlab.com/apparmor/apparmor/-/wikis/QuickProfileLanguage)
 
