使用 AppArmor 保护容器安全

AppArmor 是一个 Linux 内核安全模块,可用于限制主机操作系统上运行的进程的功能。每个进程都可以拥有自己的安全配置文件。安全配置文件用来允许或禁止特定功能,例如网络访问或文件读/写/执行权限。

您可以将 AppArmor 与在 Container-Optimized OS 实例上运行的 Docker 容器配合使用。对于任何给定容器,您可以应用 Docker 附带的默认 AppArmor 安全配置文件,也可以应用自己提供的自定义安全配置文件。

使用默认的 Docker AppArmor 安全配置文件

在 Container-Optimized OS 实例上启动容器时,系统会自动应用 docker-default 安全配置文件。以下命令演示了使用 docker-default 安全配置文件运行容器:

docker run --rm -it debian:jessie bash -i

要测试 docker-default 安全配置文件,可以尝试使用 cat 命令读取 /proc/sysrq-trigger 文件,如下所示:

root@88cef496c1a5:/# cat /proc/sysrq-trigger

输出应包含“没有权限”错误,如下所示:

cat: /proc/sysrq-trigger: Permission denied

应用自定义安全配置文件

要应用其他安全配置文件,请在运行容器时使用 apparmor=<profile-name> 命令行选项。以下命令演示了使用安全配置文件 no-ping 运行容器:

docker run --rm -i --security-opt apparmor=no-ping debian:jessie bash -i

有关创建示例中指定的 no-ping 配置文件的详细信息,请参阅本主题后文中的创建自定义安全配置文件

您还可以将 apparmor 选项指定为 unconfined,以表示要在没有安全配置文件的情况下运行容器,如下例所示:

docker run --rm -it --security-opt apparmor=unconfined debian:jessie bash -i

查看活跃的 AppArmor 安全配置文件

您可以通过检查 /proc/<pid>/attr/current 文件来查看系统将哪个 AppArmor 配置文件(如果有)应用于 Container-Optimized OS 实例上的进程,其中 <pid> 是进程 ID。

假设实例上运行有以下进程(通过 ps -ef | grep '[b]ash -i' 命令显示):

root      1903  1897  0 21:58 pts/3    00:00:00 docker run --rm -it debian:jessie bash -i
root      1927  1913  0 21:58 pts/4    00:00:00 bash -i
root      1978  1001  0 22:01 pts/0    00:00:00 docker run --rm -it --security-opt apparmor=unconfined debian:jessie bash -i
root      2001  1988  0 22:01 pts/2    00:00:00 bash -i

如果检查 /proc/1927/attr/current,您将看到以下输出,表示该进程 (pid 1927) 使用默认 Docker 安全配置文件运行:

# cat /proc/1927/attr/current
docker-default (enforce)

如果检查 /proc/2001/attr/current,您将看到以下输出,表示在没有安全配置文件的情况下(使用选项 apparmor=unconfined)运行进程 (pid 2001):

# cat /proc/2001/attr/current
unconfined

创建自定义安全配置文件

如果进程需要 docker-default 外的其他安全配置文件,您可以编写自己的自定义配置文件。要使用自定义配置文件,您必须创建配置文件,然后将该文件加载到 AppArmor 中。

例如,假设您需要一个禁止所有原始网络流量的安全配置文件。以下脚本演示了在 /etc/apparmor.d/no_raw_net 下创建名为 no-ping 的安全配置文件:

cat > /etc/apparmor.d/no_raw_net <<EOF
#include <tunables/global>

profile no-ping flags=(attach_disconnected,mediate_deleted) {
  #include <abstractions/base>

  network inet tcp,
  network inet udp,
  network inet icmp,

  deny network raw,
  deny network packet,
  file,
  mount,
}
EOF

创建安全配置文件后,可以使用 apparmor_parser 将配置文件加载到 AppArmor 中:

/sbin/apparmor_parser --replace --write-cache /etc/apparmor.d/no_raw_net

加载 no-ping 配置文件后,您可以使用以下命令对配置文件进行测试:

$ docker run --rm -i --security-opt apparmor=no-ping debian:jessie ping -c3 8.8.8.8

该命令创建了使用 no-ping 安全配置文件的容器,并尝试在容器中运行 ping。安全配置文件会禁止该流量,从而导致如下错误:

ping: Lacking privilege for raw socket.

要确保启动 Container-Optimized OS 实例时存在自定义安全配置文件,且配置文件在重新启动后仍然存在,可以使用 cloud-init/etc/apparmor.d 中安装该配置文件。要执行此操作,请将 cloud-config 脚本添加到实例的元数据中,作为 user-data 键的值。

以下 cloud-config 脚本演示了将 no-ping 配置文件添加到 /etc/apparmor.d 中:

#cloud-configs

write_files:
- path: /etc/apparmor.d/no_raw_net
  permissions: 0644
  owner: root
  content: |
    #include <tunables/global>

    profile no-ping flags=(attach_disconnected,mediate_deleted) {
      #include <abstractions/base>

      network inet tcp,
      network inet udp,
      network inet icmp,

      deny network raw,
      deny network packet,
      file,
      mount,
    }

要确保您的服务文件能将自定义配置文件加载到 AppArmor 并告知 Docker 使用该配置文件,请在您的实例上运行以下命令:

ExecStartPre=/sbin/apparmor_parser -r -W /etc/apparmor.d/no_raw_net
ExecStart=/usr/bin/docker run --security-opt apparmor=no-ping ...

运行命令后,重新启动实例,然后便可以运行受自定义 AppArmor 配置文件约束的容器。