哪个 PID 在没有网络工具的情况下使用 k8s pod 内的端口
Which PID is using a PORT inside a k8s pod without net tools
抱歉这个长问题 post,但我认为了解它的工作原理对其他人很有用。
我知道的:
在任何 linux host 上(不使用 docker 容器), 我可以查看 /proc/net/tcp
来提取与 tcp 套接字相关的信息。
因此,我可以检测处于 LISTEN
状态的端口:
cat /proc/net/tcp |
grep " 0A " |
sed 's/^[^:]*: \(..\)\(..\)\(..\)\(..\):\(....\).*/echo $((0x)).$((0x)).$((0x)).$((0x)):$((0x))/g' |
bash
结果:
0.0.0.0:111
10.174.109.1:53
127.0.0.53:53
0.0.0.0:22
127.0.0.1:631
0.0.0.0:8000
/proc/net/tcp
给出了UID
,GID
,可惜没有提供PID
。但是returns inode
。我可以用它来发现 PID
并将其用作文件描述符。
所以一种方法是搜索 /proc
寻找 inode
套接字。它很慢,但适用于主机:
cat /proc/net/tcp |
grep " 0A " |
sed 's/^[^:]*: \(..\)\(..\)\(..\)\(..\):\(....\).\{72\}\([^ ]*\).*/echo $((0x)).$((0x)).$((0x)).$((0x)):$((0x))\\t$(find \/proc\/ -type d -name fd 2>\/dev\/null \| while read f\; do ls -l $f 2>\/dev\/null \| grep -q \&\& echo $f; done)/g' |
bash
输出:
0.0.0.0:111 /proc/1/task/1/fd /proc/1/fd /proc/924/task/924/fd /proc/924/fd
10.174.109.1:53 /proc/23189/task/23189/fd /proc/23189/fd
127.0.0.53:53 /proc/923/task/923/fd /proc/923/fd
0.0.0.0:22 /proc/1194/task/1194/fd /proc/1194/fd
127.0.0.1:631 /proc/13921/task/13921/fd /proc/13921/fd
0.0.0.0:8000 /proc/23122/task/23122/fd /proc/23122/fd
权限提示1:您只会看到您有权查看的内容。
权限提示 2:容器中使用的假 root
无法访问 /proc/*/fd
中的所有文件描述符。您需要为每个用户查询它。
如果您 运行 作为普通用户,结果是:
0.0.0.0:111
10.174.109.1:53
127.0.0.53:53
0.0.0.0:22
127.0.0.1:631
0.0.0.0:8000 /proc/23122/task/23122/fd /proc/23122/fd
使用 unshare
隔离环境,它按预期工作:
$ unshare -r --fork --pid unshare -r --fork --pid --mount-proc -n bash
# ps -fe
UID PID PPID C STIME TTY TIME CMD
root 1 0 2 07:19 pts/6 00:00:00 bash
root 100 1 0 07:19 pts/6 00:00:00 ps -fe
# netstat -ntpl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
# python -m SimpleHTTPServer &
[1] 152
# Serving HTTP on 0.0.0.0 port 8000 ...
netstat -ntpl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 152/python
# cat /proc/net/tcp |
> grep " 0A " |
> sed 's/^[^:]*: \(..\)\(..\)\(..\)\(..\):\(....\).\{72\}\([^ ]*\).*/echo $((0x)).$((0x)).$((0x)).$((0x)):$((0x))\\t$(find \/proc\/ -type d -name fd 2>\/dev\/null \| while read f\; do ls -l $f 2>\/dev\/null \| grep -q \&\& echo $f; done)/g' |
> bash
0.0.0.0:8000 /proc/152/task/152/fd /proc/152/fd
# ls -l /proc/152/fd
total 0
lrwx------ 1 root root 64 mai 25 07:20 0 -> /dev/pts/6
lrwx------ 1 root root 64 mai 25 07:20 1 -> /dev/pts/6
lrwx------ 1 root root 64 mai 25 07:20 2 -> /dev/pts/6
lrwx------ 1 root root 64 mai 25 07:20 3 -> 'socket:[52409024]'
lr-x------ 1 root root 64 mai 25 07:20 7 -> /dev/urandom
# cat /proc/net/tcp
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
0: 00000000:1F40 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 52409024 1 0000000000000000 100 0 0 10 0
在我主机的 docker 容器中,它似乎以同样的方式工作。
问题:
我在 kubernetes pod 中有一个容器 运行ning jitsi。在这个容器中,我无法获取监听端口的服务的 PID。
安装 netstat 后也没有:
root@jitsi-586cb55594-kfz6m:/# netstat -ntpl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:5222 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:5269 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:5280 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:5347 0.0.0.0:* LISTEN -
tcp6 0 0 :::5222 :::* LISTEN -
tcp6 0 0 :::5269 :::* LISTEN -
tcp6 0 0 :::5280 :::* LISTEN -
# ps -fe
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 May22 ? 00:00:00 s6-svscan -t0 /var/run/s6/services
root 32 1 0 May22 ? 00:00:00 s6-supervise s6-fdholderd
root 199 1 0 May22 ? 00:00:00 s6-supervise jicofo
jicofo 203 199 0 May22 ? 00:04:17 java -Xmx3072m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp -Dnet.java.sip.communicator.SC_HOME_DIR_LOCATION=/ -Dnet.java.sip.communicator.SC_HOME_DIR_NAME=config -Djava
root 5990 0 0 09:48 pts/2 00:00:00 bash
root 10926 5990 0 09:57 pts/2 00:00:00 ps -fe
最后是问题:
a) 为什么我无法读取进程监听端口 5222 的文件描述符?
root@jitsi-586cb55594-kfz6m:/# cat /proc/net/tcp | grep " 0A "
0: 00000000:1466 00000000:0000 0A 00000000:00000000 00:00000000 00000000 101 0 244887827 1 ffff9bd749145800 100 0 0 10 0
...
root@jitsi-586cb55594-kfz6m:/# echo $(( 0x1466 ))
5222
root@jitsi-586cb55594-kfz6m:/# ls -l /proc/*/fd/* 2>/dev/null | grep 244887827
root@jitsi-586cb55594-kfz6m:/# echo $?
1
root@jitsi-586cb55594-kfz6m:/# su - svc
svc@jitsi-586cb55594-kfz6m:~$ id -u
101
svc@jitsi-586cb55594-kfz6m:~$ ls -l /proc/*/fd/* 2>/dev/null | grep 244887827
svc@jitsi-586cb55594-kfz6m:~$ echo $?
1
b) 还有另一种方法可以将 inode
和 link 列出到 pid
而无需搜索 /proc/*/fd
?
更新 1:
根据 Anton Kostenko 提示,我查看了 AppArmor。不是这样的,因为服务器没有使用AppArmor,但是搜索,把我带到了SELinux。
在 AppArmor 为 运行ning 的 ubuntu 机器上,我得到:
$ sudo apparmor_status | grep dock
docker-default
在 OKE(Oracle Kubernetes Engine,我的案例)节点中没有 AppArmor。我改用了 SELinux:
$ man selinuxenabled | grep EXIT -A1
EXIT STATUS
It exits with status 0 if SELinux is enabled and 1 if it is not enabled.
$ selinuxenabled && echo $?
0
现在,我相信 SELinux
正在阻止容器内根目录的 /proc/*/fd
列表。但是我还不知道怎么解锁。
参考文献:
问题已通过添加 POSIX 功能解决:CAP_SYS_PTRACE
我的情况是容器在 kubernetes 编排下。
this reference 解释了 kubectl
和 POSIX Capabilities
所以我有
root@jitsi-55584f98bf-6cwpn:/# cat /proc/1/status | grep Cap
CapInh: 00000000a80425fb
CapPrm: 00000000a80425fb
CapEff: 00000000a80425fb
CapBnd: 00000000a80425fb
CapAmb: 0000000000000000
于是我仔细看了POSIX Capabilities Manual。但即使添加 CAP_SYS_ADMIN
,PID
也不会出现在 netstat
上。所以我测试了所有功能。 CAP_SYS_PTRACE
是天选者
root@jitsi-65c6b5d4f7-r546h:/# cat /proc/1/status | grep Cap
CapInh: 00000000a80c25fb
CapPrm: 00000000a80c25fb
CapEff: 00000000a80c25fb
CapBnd: 00000000a80c25fb
CapAmb: 0000000000000000
所以这里我的部署规范更改:
...
spec:
...
template:
...
spec:
...
containers:
...
securityContext:
capabilities:
add:
- SYS_PTRACE
...
但我不知道 selinux
出于什么安全原因才这样做。但是现在对我来说已经足够了。
参考文献:
抱歉这个长问题 post,但我认为了解它的工作原理对其他人很有用。
我知道的:
在任何 linux host 上(不使用 docker 容器), 我可以查看 /proc/net/tcp
来提取与 tcp 套接字相关的信息。
因此,我可以检测处于 LISTEN
状态的端口:
cat /proc/net/tcp |
grep " 0A " |
sed 's/^[^:]*: \(..\)\(..\)\(..\)\(..\):\(....\).*/echo $((0x)).$((0x)).$((0x)).$((0x)):$((0x))/g' |
bash
结果:
0.0.0.0:111
10.174.109.1:53
127.0.0.53:53
0.0.0.0:22
127.0.0.1:631
0.0.0.0:8000
/proc/net/tcp
给出了UID
,GID
,可惜没有提供PID
。但是returns inode
。我可以用它来发现 PID
并将其用作文件描述符。
所以一种方法是搜索 /proc
寻找 inode
套接字。它很慢,但适用于主机:
cat /proc/net/tcp |
grep " 0A " |
sed 's/^[^:]*: \(..\)\(..\)\(..\)\(..\):\(....\).\{72\}\([^ ]*\).*/echo $((0x)).$((0x)).$((0x)).$((0x)):$((0x))\\t$(find \/proc\/ -type d -name fd 2>\/dev\/null \| while read f\; do ls -l $f 2>\/dev\/null \| grep -q \&\& echo $f; done)/g' |
bash
输出:
0.0.0.0:111 /proc/1/task/1/fd /proc/1/fd /proc/924/task/924/fd /proc/924/fd
10.174.109.1:53 /proc/23189/task/23189/fd /proc/23189/fd
127.0.0.53:53 /proc/923/task/923/fd /proc/923/fd
0.0.0.0:22 /proc/1194/task/1194/fd /proc/1194/fd
127.0.0.1:631 /proc/13921/task/13921/fd /proc/13921/fd
0.0.0.0:8000 /proc/23122/task/23122/fd /proc/23122/fd
权限提示1:您只会看到您有权查看的内容。
权限提示 2:容器中使用的假 root
无法访问 /proc/*/fd
中的所有文件描述符。您需要为每个用户查询它。
如果您 运行 作为普通用户,结果是:
0.0.0.0:111
10.174.109.1:53
127.0.0.53:53
0.0.0.0:22
127.0.0.1:631
0.0.0.0:8000 /proc/23122/task/23122/fd /proc/23122/fd
使用 unshare
隔离环境,它按预期工作:
$ unshare -r --fork --pid unshare -r --fork --pid --mount-proc -n bash
# ps -fe
UID PID PPID C STIME TTY TIME CMD
root 1 0 2 07:19 pts/6 00:00:00 bash
root 100 1 0 07:19 pts/6 00:00:00 ps -fe
# netstat -ntpl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
# python -m SimpleHTTPServer &
[1] 152
# Serving HTTP on 0.0.0.0 port 8000 ...
netstat -ntpl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 152/python
# cat /proc/net/tcp |
> grep " 0A " |
> sed 's/^[^:]*: \(..\)\(..\)\(..\)\(..\):\(....\).\{72\}\([^ ]*\).*/echo $((0x)).$((0x)).$((0x)).$((0x)):$((0x))\\t$(find \/proc\/ -type d -name fd 2>\/dev\/null \| while read f\; do ls -l $f 2>\/dev\/null \| grep -q \&\& echo $f; done)/g' |
> bash
0.0.0.0:8000 /proc/152/task/152/fd /proc/152/fd
# ls -l /proc/152/fd
total 0
lrwx------ 1 root root 64 mai 25 07:20 0 -> /dev/pts/6
lrwx------ 1 root root 64 mai 25 07:20 1 -> /dev/pts/6
lrwx------ 1 root root 64 mai 25 07:20 2 -> /dev/pts/6
lrwx------ 1 root root 64 mai 25 07:20 3 -> 'socket:[52409024]'
lr-x------ 1 root root 64 mai 25 07:20 7 -> /dev/urandom
# cat /proc/net/tcp
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
0: 00000000:1F40 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 52409024 1 0000000000000000 100 0 0 10 0
在我主机的 docker 容器中,它似乎以同样的方式工作。
问题:
我在 kubernetes pod 中有一个容器 运行ning jitsi。在这个容器中,我无法获取监听端口的服务的 PID。
安装 netstat 后也没有:
root@jitsi-586cb55594-kfz6m:/# netstat -ntpl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:5222 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:5269 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:5280 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:5347 0.0.0.0:* LISTEN -
tcp6 0 0 :::5222 :::* LISTEN -
tcp6 0 0 :::5269 :::* LISTEN -
tcp6 0 0 :::5280 :::* LISTEN -
# ps -fe
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 May22 ? 00:00:00 s6-svscan -t0 /var/run/s6/services
root 32 1 0 May22 ? 00:00:00 s6-supervise s6-fdholderd
root 199 1 0 May22 ? 00:00:00 s6-supervise jicofo
jicofo 203 199 0 May22 ? 00:04:17 java -Xmx3072m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp -Dnet.java.sip.communicator.SC_HOME_DIR_LOCATION=/ -Dnet.java.sip.communicator.SC_HOME_DIR_NAME=config -Djava
root 5990 0 0 09:48 pts/2 00:00:00 bash
root 10926 5990 0 09:57 pts/2 00:00:00 ps -fe
最后是问题:
a) 为什么我无法读取进程监听端口 5222 的文件描述符?
root@jitsi-586cb55594-kfz6m:/# cat /proc/net/tcp | grep " 0A "
0: 00000000:1466 00000000:0000 0A 00000000:00000000 00:00000000 00000000 101 0 244887827 1 ffff9bd749145800 100 0 0 10 0
...
root@jitsi-586cb55594-kfz6m:/# echo $(( 0x1466 ))
5222
root@jitsi-586cb55594-kfz6m:/# ls -l /proc/*/fd/* 2>/dev/null | grep 244887827
root@jitsi-586cb55594-kfz6m:/# echo $?
1
root@jitsi-586cb55594-kfz6m:/# su - svc
svc@jitsi-586cb55594-kfz6m:~$ id -u
101
svc@jitsi-586cb55594-kfz6m:~$ ls -l /proc/*/fd/* 2>/dev/null | grep 244887827
svc@jitsi-586cb55594-kfz6m:~$ echo $?
1
b) 还有另一种方法可以将 inode
和 link 列出到 pid
而无需搜索 /proc/*/fd
?
更新 1:
根据 Anton Kostenko 提示,我查看了 AppArmor。不是这样的,因为服务器没有使用AppArmor,但是搜索,把我带到了SELinux。
在 AppArmor 为 运行ning 的 ubuntu 机器上,我得到:
$ sudo apparmor_status | grep dock
docker-default
在 OKE(Oracle Kubernetes Engine,我的案例)节点中没有 AppArmor。我改用了 SELinux:
$ man selinuxenabled | grep EXIT -A1
EXIT STATUS
It exits with status 0 if SELinux is enabled and 1 if it is not enabled.
$ selinuxenabled && echo $?
0
现在,我相信 SELinux
正在阻止容器内根目录的 /proc/*/fd
列表。但是我还不知道怎么解锁。
参考文献:
问题已通过添加 POSIX 功能解决:CAP_SYS_PTRACE
我的情况是容器在 kubernetes 编排下。
this reference 解释了 kubectl
和 POSIX Capabilities
所以我有
root@jitsi-55584f98bf-6cwpn:/# cat /proc/1/status | grep Cap
CapInh: 00000000a80425fb
CapPrm: 00000000a80425fb
CapEff: 00000000a80425fb
CapBnd: 00000000a80425fb
CapAmb: 0000000000000000
于是我仔细看了POSIX Capabilities Manual。但即使添加 CAP_SYS_ADMIN
,PID
也不会出现在 netstat
上。所以我测试了所有功能。 CAP_SYS_PTRACE
是天选者
root@jitsi-65c6b5d4f7-r546h:/# cat /proc/1/status | grep Cap
CapInh: 00000000a80c25fb
CapPrm: 00000000a80c25fb
CapEff: 00000000a80c25fb
CapBnd: 00000000a80c25fb
CapAmb: 0000000000000000
所以这里我的部署规范更改:
...
spec:
...
template:
...
spec:
...
containers:
...
securityContext:
capabilities:
add:
- SYS_PTRACE
...
但我不知道 selinux
出于什么安全原因才这样做。但是现在对我来说已经足够了。
参考文献: