在Linux宿主机审计docker进程和网络连接

2019-03-28 61825人围观 ,发现 7 个不明物体 系统安全

*本文作者:zhouqiao,本文属 FreeBuf 原创奖励计划,未经许可禁止转载。

一、引言

docker容器已经被广泛应用到各大公司线上、测试等各种环境,在宿主机如何识别出docker进程、docker网络连接就成为一个困扰的问题,如果容器内部署相同的crond或ssh服务,在宿主机上执行ps命令发现一大堆相同名字进程,根本无法区分属于宿主机还是具体某个container;不过docker提供docker top container_id命令能看到具体某个container在宿主机进程pid相关信息,但没有办法直接列出全部container进程。同时在网络连接方面,在宿主机执行netstat命令, 看不到container连接状态。

本文开发的docker_util工具能够在宿主机上全部审计和展示当前docker容器全部存活进程与网络连接信息。在上文通过Linux连接器能够实时审计宿主机进程,有兴趣可以看下:基于Linux连接器的审计进程事件实现方案

二、测试环境

在一台centos(ip 10.89.93.11)部署redis和tomcat两个container,其中reids开放默认6379, tomcat 开放默认80,8080,8085,同时宿主机部署nginx服务。

root@mytest:/home/zhouqiaozhouqiao$ docker ps

CONTAINER ID        IMAGE                                      COMMAND                  CREATED             STATUS              PORTS                                                                NAMES

51ec548386ef        docker.io/redis:latest                     "docker-entrypoint..."   3 days ago          Up 3 days           0.0.0.0:6379->6379/tcp                                               relaxed_bell

23f07e13e192        docker.io/tomcat                           "catalina.sh run"        7 weeks ago         Up 7 weeks          8080/tcp, 0.0.0.0:8089->80/tcp                                       hungry_bassi

在另一台centos(ip 10.89.93.12),使用redis-cli连接redis-server,构造一条真实连接。

root@mytest:/home/zhouqiaozhouqiao$ redis-cli -h 10.89.93.11

10.89.93.11:6379>

三、docker进程审计

docker进程是通过pid namespace技术,在不同pid namespace,进程pid是独立的,但docker是共享主机内核,在宿主机上都有相应的进程pid映射,所以在宿主机能够获取全部docker进程和宿主机进程信息。

ps命令能够扫描出全部宿主机进程, 遍历/proc/目录,结合进程目录下exe,cmdline等信息输出,但没有办法区分出docker进程。

下面分析docker进程与宿主机进程区分方式。

3.1 进程区分方式

对比宿主机进程nginx与docker进程redis各进程cgroup(/proc/pid/cgroup)信息文件,都协带container id信息。

非docker进程(nginx进程 pid:22982)

root@mytest:/home/zhouqiaozhouqiao$ cat /proc/22982/cgroup

10:freezer:/

9:perf_event:/

8:memory:/user.slice

7:cpuset:/

6:devices:/user.slice

5:net_cls:/

4:blkio:/user.slice

3:cpuacct,cpu:/user.slice

2:hugetlb:/

1:name=systemd:/user.slice/user-85001637.slice/session-3822015.scope

docker进程(redis进程 pid:17147)

root@mytest:/home/zhouqiaozhouqiao$ cat /proc/17147/cgroup

10:freezer:/system.slice/docker-51ec548386efd801fe28b3ce6b1905305841c997a3f7325ae6961a2cc7e88d14.scope

9:perf_event:/system.slice/docker-51ec548386efd801fe28b3ce6b1905305841c997a3f7325ae6961a2cc7e88d14.scope

8:memory:/system.slice/docker-51ec548386efd801fe28b3ce6b1905305841c997a3f7325ae6961a2cc7e88d14.scope

7:cpuset:/system.slice/docker-51ec548386efd801fe28b3ce6b1905305841c997a3f7325ae6961a2cc7e88d14.scope

6:devices:/system.slice/docker-51ec548386efd801fe28b3ce6b1905305841c997a3f7325ae6961a2cc7e88d14.scope

5:net_cls:/system.slice/docker-51ec548386efd801fe28b3ce6b1905305841c997a3f7325ae6961a2cc7e88d14.scope

4:blkio:/system.slice/docker-51ec548386efd801fe28b3ce6b1905305841c997a3f7325ae6961a2cc7e88d14.scope

3:cpuacct,cpu:/system.slice/docker-51ec548386efd801fe28b3ce6b1905305841c997a3f7325ae6961a2cc7e88d14.scope

2:hugetlb:/system.slice/docker-51ec548386efd801fe28b3ce6b1905305841c997a3f7325ae6961a2cc7e88d14.scope

1:name=systemd:/system.slice/docker-51ec548386efd801fe28b3ce6b1905305841c997a3f7325ae6961a2cc7e88d14.scope

可以看到两类进程cgroup信息文件有明显不同,其中docker进程明显多出51ec548386efd801fe28b3ce6b1905305841c997a3f7325ae6961a2cc7e88d14,通过docker inspect可以确定这是redis container的id。

3.2 cgroup文件验证

对于常用container,有systemd Docker和Non-systemd Docker两种管理方式,都可以验证出/proc/pid/cgroup文件都协带64位container id信息。

// systemd Docker

container id为51ec548386efd801fe28b3ce6b1905305841c997a3f7325ae6961a2cc7e88d14

8:memory:/system.slice/docker-51ec548386efd801fe28b3ce6b1905305841c997a3f7325ae6961a2cc7e88d14.scope

// Non-systemd Docker

container id为de630f22746b9c06c412858f26ca286c6cdfed086d3b302998aa403d9dcedc42

8:memory:/docker/de630f22746b9c06c412858f26ca286c6cdfed086d3b302998aa403d9dcedc42

//k8s

container id为9170559b8aadd07d99978d9460cf8d1c71552f3c64fefc7e9906ab3fb7e18f69

4:memory:/kubepods/burstable/pod5f399c1a-f9fc-11e8-bf65-246e9659ebfc/9170559b8aadd07d99978d9460cf8d1c71552f3c64fefc7e9906ab3fb7e18f69

cgroup文件信息可以作为判断进程是否属于宿主机进程还是某个container.

3.3 docker进程审计

开发docker_util工具审计docker进程首先是遍历/proc目录扫描,识别出进程pid号,再读取进程/proc/pid/cgroup文件信息确定属于宿主机进程或某个docker container进程。

下面是执行docker_util获取的结果,输出pid 17147和40618的进程信息,与部署的redis,tomcat docker一致。

Pid:17147, Exe:/usr/local/bin/redis-server, Status:LISTEN, Laddr:Ip:0.0.0.0,Port:6379, Raddr:Ip:0.0.0.0,Port:0

Pid:17147, Exe:/usr/local/bin/redis-server, Status:ESTABLISHED, Laddr:Ip:172.17.0.7,Port:6379, Raddr:Ip:10.89.93.12,Port:59358

Pid:40618, Exe:/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, Status:LISTEN, Laddr:Ip:127.0.0.1,Port:8005, Raddr:Ip:0.0.0.0,Port:0

Pid:40618, Exe:/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, Status:LISTEN, Laddr:Ip:0.0.0.0,Port:8009, Raddr:Ip:0.0.0.0,Port:0

Pid:40618, Exe:/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, Status:LISTEN, Laddr:Ip:0.0.0.0,Port:8080, Raddr:Ip:0.0.0.0,Port:0

四、docker网络连接审计

4.1 netstat失效原因

常用的获取主机网络连接命令netstat为什么不能扫描出docker网络连接,分析源码,netstat的实现原理,主要包括如下三个步骤:

1)遍历进程fd(/proc/pid/fd)目录获取文件类型为socket的inode号(inode->pid)

2)遍历/proc/net/tcp和/proc/net/udp 网络状态文件,按行解析网络连接信息(inode->ip port四元组、连接状态)

3)通过网络连接中的inode信息与第一步获取的inode关联,将进程与网络连接对应起来

该原理与获取linux进程级网络流量统计实现一致,有兴趣可以看下:一种linux进程网络流量统计方法和实现

回到正题,那是(/proc/pid/fd,/proc/net/tcp)这两文件中是哪个信息缺失docker进程网络连接呢?

查看docker redis进程(/proc/17147/fd) fd目录, 可以看到socket有两个inode,分别是 1927741637, 1927719349

root@mytest:/home/zhouqiaozhouqiao$ ls -l /proc/17147/fd

total 0

lr-x------ 1 systemd-bus-proxy ssh_keys 64 Dec 14 10:05 0 -> pipe:[1927741605]

l-wx------ 1 systemd-bus-proxy ssh_keys 64 Dec 14 10:05 1 -> pipe:[1927741606]

l-wx------ 1 systemd-bus-proxy ssh_keys 64 Dec 14 10:05 2 -> pipe:[1927741607]

lr-x------ 1 systemd-bus-proxy ssh_keys 64 Dec 14 10:05 3 -> pipe:[1927741635]

l-wx------ 1 systemd-bus-proxy ssh_keys 64 Dec 14 10:05 4 -> pipe:[1927741635]

lrwx------ 1 systemd-bus-proxy ssh_keys 64 Dec 14 10:05 5 -> anon_inode:[eventpoll]

lrwx------ 1 systemd-bus-proxy ssh_keys 64 Dec 14 10:05 6 -> socket:[1927741637]

lrwx------ 1 systemd-bus-proxy ssh_keys 64 Dec 14 10:05 7 -> socket:[1927719349]

再看网络状态文件cat /proc/net/tcp |grep 1927741637 或 cat /proc/net/tcp |grep 1927719349, 都没有检索出docker 进程inode号,也就是/proc/net/tcp没有docker进程连接信息。

cat /proc/net/tcp |grep 1927741637

cat /proc/net/tcp |grep 1927719349

查看内核代码, 可以分析出/proc/net/tcp只会输出宿主机net namespace的进程连接, 也就是说网络状态文件 /proc/net/tcp与某宿主机进程(/proc/net/pid/tcp)内容是一致的。

再次对比宿主机nginx进程ns信息,与redis ns信息,可以发现两者ns namespace都完全不一样,与内核代码解释一致。

//宿主机进程ns

root@mytest:/home/zhouqiaozhouqiao$ ls -l /proc/22982/ns

total 0

lrwxrwxrwx 1 root root 0 Nov 30 14:25 ipc -> ipc:[4026531839]

lrwxrwxrwx 1 root root 0 Nov 30 14:25 mnt -> mnt:[4026531840]

lrwxrwxrwx 1 root root 0 Nov 30 14:25 net -> net:[4026531956]

lrwxrwxrwx 1 root root 0 Nov 30 14:25 pid -> pid:[4026531836]

lrwxrwxrwx 1 root root 0 Nov 30 14:25 user -> user:[4026531837]

lrwxrwxrwx 1 root root 0 Nov 30 14:25 uts -> uts:[4026531838]

//docker进程ns

root@mytest:/home/zhouqiaozhouqiao$ ls -l  /proc/17147/ns

total 0

lrwxrwxrwx 1 systemd-bus-proxy ssh_keys 0 Dec 14 10:05 ipc -> ipc:[4026533327]

lrwxrwxrwx 1 systemd-bus-proxy ssh_keys 0 Dec 14 10:05 mnt -> mnt:[4026533325]

lrwxrwxrwx 1 systemd-bus-proxy ssh_keys 0 Dec 14 10:05 net -> net:[4026533330]

lrwxrwxrwx 1 systemd-bus-proxy ssh_keys 0 Dec 14 10:05 pid -> pid:[4026533328]

lrwxrwxrwx 1 systemd-bus-proxy ssh_keys 0 Dec 14 10:05 user -> user:[4026531837]

lrwxrwxrwx 1 systemd-bus-proxy ssh_keys 0 Dec 14 10:05 uts -> uts:[4026533326]

netstat 无法采集到docker网络连接根本原因是docker进程与宿主机进程namespace不一致,而网络状态文件/proc/net/tcp只有宿主机进程网络连接信息,没有docker进程网络连接信息。

4.2 docker网络连接审计

docker进程的网络连接信息全部在/proc/pid/net/tcp。

//redis tcp

root@mytest:/home/zhouqiaozhouqiao$ cat /proc/17147/net/tcp

  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode

   0: 00000000:18EB 00000000:0000 0A 00000000:00000000 00:00000000 00000000   999        0 1927741637 1 ffff882024a73c00 100 0 0 10 0

   1: 070011AC:18EB 0C5D590A:E7DE 01 00000000:00000000 02:00000690 00000000   999        0 1927719349 2 ffff882014e41680 20 4 1 22 21

可以看到inode号1927741637, 1927719349,与/proc/17147/fd目录socket连接inode号一致。

开发docker_util工具审计docker网络连接,在netstat原理上增加docker进程判断和增加进程网络状态文件(/proc/pid/net/tcp等)扫描。

1)遍历进程fd(/proc/pid/fd)目录获取文件类型为socket的inode号(inode->pid)

2)判断进程是docker进程,遍历/proc/pid/net/tcp和/proc/pid/net/udp 网络状态文件,按行解析网络连接信息(inode->ip port四元组、连接状态)

3)通过网络连接中的inode信息与第一步获取的inode关联,将进程与网络连接对应起来

下面是执行docker_util获取的结果,输出pid 17147 redis进程网络连接结果:

Pid:17147, Exe:/usr/local/bin/redis-server, Status:LISTEN, Laddr:Ip:0.0.0.0,Port:6379, Raddr:Ip:0.0.0.0,Port:0

Pid:17147, Exe:/usr/local/bin/redis-server, Status:ESTABLISHED, Laddr:Ip:172.17.0.7,Port:6379, Raddr:Ip:10.89.93.12,Port:59358

可以看出redis有两条连接,inode号为1927741637号是reids-server LINSTEN连接,1927719349号是10.89.93.12客户连接redis-server的tcp连接,源端口是59358。

五、结论

通过进程cgroup(/proc/pid/cgroup)文件信息和进程网络状态(/proc/pid/net/tcp或udp)等信息结合能够在Linux宿主机上审计docker容器进程和网络连接。

*本文作者:zhouqiao,本文属 FreeBuf 原创奖励计划,未经许可禁止转载。

发表评论

已有 7 条评论

取消
Loading...
css.php