​Podman:一个比docker更安全的容器

在上一篇文章中我们介绍了用来取代传统docker容器的另一种选择其中podman是最重要的组件之一。我们也说了其主要优势是无需docker守护进程,以普通用户形式启动,是一种更安全的容器管理器。本文虫虫就来介绍下podman的安全性,通过linux安全审计机制实例介绍podman和docker的安全性。

linux安全审计

Linux系统自带一个安全升级功能audit,有一个审计守护进程auditd和一套审计工具,比如auditctl,aureport,autrace,auvirt和ausearch等组成。它允许管理员在系统上监视安全事件,并将它们记录到/var/log/audit/audit.log中,该文件可以本地存储或异地保存在另一台机器上,以保证审计日志被人恶意篡改。

关于linux审计的文档和介绍有大量文档可以参考,本文此处不在赘述,如有需要以后可以专门写文章说明,此处我们仅仅以文件追踪为例子说明一下其流程和审计日志中的关键字段等。

我们知道linux下所有用户的密码信息是保存在/etc/shadow文件中的,这是一个最常用的安全文件,所有用户的变化情况都会反应在该文件中。如果我们想知道该文件的变化情况。我们可以auditctl将给文件添加到审计追踪中:

auditctl -w /etc/shadow

auditctl -l ,-l可以列出目前的审计追踪列表:

现在让我们看看如果修改/etc/shadow文件会发生什么:

touch /etc/shadow

ausearch -f /etc/shadow

审计记录中有很多信息,我摘了其中一条root通过touch修改了/etc/shadow文件,并且进程审计Uid(auid)的所有者是1001 ,test。

日志如下:

time->Wed Jul 31 14:59:21 2019

type=PROCTITLE msg=audit(1564556361.681:8062): proctitle=7375646F006175736561726368002D66002F6574632F736861646F77002D69002D747300726563656E74

type=PATH msg=audit(1564556361.681:8062): item=0 name=”/etc/shadow”inode=68873511 dev=fd:00 mode=0100000 ouid=0 ogid=0 rdev=00:00 objtype=NORMAL cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0 cap_fver=0

type=CWD msg=audit(1564556361.681:8062): cwd=”/home/test”

type=SYSCALL msg=audit(1564556361.681:8062): arch=c000003e syscall=2success=yes exit=7 a0=7f480a5514f3 a1=80000 a2=1b6 a3=24 items=1 ppid=32003 pid=2387 auid=1001 uid=1001 gid=1001 euid=0 suid=0 fsuid=0 egid=1001 sgid=1001 fsgid=1001 tty=pts0 ses=998 comm=”sudo” exe=”/usr/bin/sudo” key=(null)

上面日志中的关键字段有:

追踪的事件发生时间(time)和最终对象的名称(name),本例子中是/etc/shadow,事件发生时候的工作路径(cwd),相关的系统调用(syscall)审计用户ID(auid)和事件发生执行的命令(exe)

关于系统调用id我们可以通过ausyscall转换为实际操作:

ausyscall x86_64 2

auid等xxuid都可以通过getent passwd命令(或者直接查询/etc/passwd)获得系统用户名:

getent passwd 1001

audid在linux安全审计中是一个很有用的字段,即loginuid,下面我来介绍下loginuid。

loginuid

Linux内核空间中有一个名为loginuid的字段,它的值存储在/proc/self/ loginuid中,该字段是系统上每个进程的proc结构组成之一。该字段只能设置一次。此后,内核设置其为只读,不允许任何进程重置它。

当test用户登录系统时,登录认证程序就将loginuid设置为test,其UID为1001

cat /proc/self/loginuid

1001

就算是通过sudu,甚至是su切换为root用户其值还是不变:

sudo cat /proc/self/loginuid

1001

从初始登录进程fork和execute的所有进程都会自动继承loginuid。这就是内核知道登录的人是test的方式。

容器中的loginuid

我们再来看看容器中的loginuid的情况。

podman

首先使用podman起一个最小的linux alpine

sudo podman run alpine cat /proc/self/loginuid

1001

该容器中的进程也保留loginuid。那么Docker呢?

Docker

sudo docker run alpine cat /proc/self/loginuid

Unable to find image ‘alpine:latest’ locally

latest: Pulling from library/alpine

050382585609: Pull complete

Digest: sha256:6a92cd1fcdc8d8cdec60f33dda4db2cb1fcdcacf3410a8e05b3741f44a9b5998

Status: Downloaded newer image for alpine:latest

4294967295

值变成了4294967295,而不是test的uid 1001

区别

Podman使用传统的fork/exec模型创建容器,因此容器进程是Podman进程的子进程。而Docker使用C/S(客户端/服务器)模型。执行docker命令的是Docker客户端工具,它通过C/S操作与Docker守护进程通信。Docker守护进程创建容器并处理stdin/stdout和Docker客户端工具的通信。

init默认loginuid是4294967295。由于容器是Docker守护程序的子进程,而Docker守护进程是init系统的子进程,所以 Docker守护进程和容器进程全部具有相同的loginuid为4294967295,在审计日志中都为unset,包括crond等

cat /proc/1/loginuid

4294967295

安全问题

我们来看看如果通过Docker启动的容器进程修改/etc/shadow文件会发生什么。

sudo docker run –privileged -v /:/host alpine touch /host/etc/shadow

sudo ausearch -f / etc / shadow –i

type=PROCTITLE msg=audit(07/31/2019 14:25:37.654:7990) : proctitle=sudo docker run –privileged -v /:/host alpine touch /host/etc/shadow

type=PATH msg=audit(07/31/2019 14:25:37.654:7990) : item=0 name=/etc/shadow inode=68873511 dev=fd:00 mode=file,000 ouid=root ogid=root rdev=00:00 objtype=NORMAL cap_fp=none cap_fi=none cap_fe=0 cap_fver=0

type=CWD msg=audit(07/31/2019 14:25:37.654:7990) : cwd=/home/test

type=SYSCALL msg=audit(07/31/2019 14:25:37.654:7990) : arch=x86_64 syscall=open success=yes exit=7 a0=0x7f442f2354f3 a1=O_RDONLY|O_CLOEXEC a2=0x1b6 a3=0x24 items=1 ppid=32003 pid=1773 auid=unset uid=test gid=test euid=root suid=root fsuid=root egid=test sgid=test fsgid=test tty=pts0 ses=998 comm=sudo exe=/usr/bin/sudo key=(null)

上面Docker审计日志中auid为unset(4294967295)。这样审计时候可虽然知道知道进程修改了/etc/shadow文件,但是谁就无法确定了。

如果该攻击者随后删除了Docker容器,那么修改/etc/shadow文件的系统上将没有任何跟踪。

现在让我们看看与Podman完全相同的场景。

sudo podman run –privileged -v /:/host alpine touch /host/etc/shadow

sudo ausearch -f /etc/shadow –i

type=PROCTITLE msg=audit(07/31/2019 14:30:01.061:8007) : proctitle=sudo podman run –privileged -v /:/host alpine touch /host/etc/shadow

type=PATH msg=audit(07/31/2019 14:30:01.061:8007) : item=0 name=/etc/shadow inode=68873511 dev=fd:00 mode=file,000 ouid=root ogid=root rdev=00:00 objtype=NORMAL cap_fp=none cap_fi=none cap_fe=0 cap_fver=0

type=CWD msg=audit(07/31/2019 14:30:01.061:8007) : cwd=/home/test

type=SYSCALL msg=audit(07/31/2019 14:30:01.061:8007) : arch=x86_64 syscall=open success=yes exit=7 a0=0x7fb84435e4f3 a1=O_RDONLY|O_CLOEXEC a2=0x1b6 a3=0x24 items=1 ppid=32003 pid=1916 auid=test uid=test gid=test euid=root suid=root fsuid=root egid=test sgid=test fsgid=test tty=pts0 ses=998 comm=sudo exe=/usr/bin/sudo key=(null)

由于它使用传统的fork/exec,因此Podman正确记录了所有内容。

通过审计追踪/etc/shadow文件的一个简单示例,但审计系统对于审计系统上的进程非常有用。使用fork /exec容器运行时启动容器(而不是客户端/服务器容器运行时)允许安全人员通过审计日志记录保持更好的安全性。

在启动容器时,fork /exec模型与C/S架构先比较还有许多其他不错的功能。例如,systemd有关功能:

SD_NOTIFY:如果将Podman命令放入systemd单元文件中,容器进程可以通过Podman向堆栈返回通知,表明服务已准备好接收任务。这是基于C/S架构的docker无法完成的事情。

套接字激活:可以将连接的套接字通过systemd传递到Podman并传递到容器进程中使用它们。这在C/S架构的docker中也是不可能的。

总结

总之,最好的功能是以非root用户身份运行Podman和容器。这样着永远不会在主机上授予用户root权限,而在C/S架构的docker中,必须以root身份运行的特权守护程序的套接字来启动容器。因此,将受到守护程序中实现的安全机制与主机操作系统中实现的安全机制的支配,无法真正做到特权的隔离,不是很安全的方法。

未经允许不得转载:大自然的搬运工 » ​Podman:一个比docker更安全的容器

赞 (0)

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址