40

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

 5 years ago
source link: https://www.freebuf.com/articles/system/197650.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

*本文作者: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 原创奖励计划,未经许可禁止转载。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK