In my Docker container, why can I still bind the port 1 without `NET_BIND_SERVICE` capability?(在我的Docker容器中,为什么还可以绑定没有`NET_BIND_SERVICE`能力的端口1?)
问题描述
我正在使用Ubuntu 18.04 Desktop
。以下是有关我的问题的更多详细信息。
最近,我正在编写一些想要执行此操作的测试代码:当它以非特权用户身份运行时,测试代码会尝试绑定一个特权端口(在我的示例中是端口1),并期望绑定失败。
在我的主机上,我的当前非特权用户有以下capsh --print
输出:
Current: =
Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read
Securebits: 00/0x0/1'b0
secure-noroot: no (unlocked)
secure-no-suid-fixup: no (unlocked)
secure-keep-caps: no (unlocked)
uid=1000(ywen)
gid=1000(ywen)
groups=4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),116(lpadmin),126(sambashare),999(docker),1000(ywen)
因此,在尝试使用当前非特权用户绑定端口1时,可以如期收到拒绝权限错误:
Python 3.6.9 (default, Oct 8 2020, 12:12:24)
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket as s
>>> o = s.socket(s.AF_INET)
>>> o.bind(("127.0.0.1", 1))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
PermissionError: [Errno 13] Permission denied
>>> exit()
因为我的测试代码最终将在Docker容器内运行,所以我使用以下Dockerfile
构建了一个镜像:
ARG UBUNTU_VERSION=18.04
FROM ubuntu:${UBUNTU_VERSION}
ARG USER_NAME=ywen
ARG USER_ID=1000
ARG GROUP_ID=1000
RUN apt-get update
# Install the needed packages.
RUN DEBIAN_FRONTEND=noninteractive apt-get -y install
bash-completion
libcap2-bin
openssh-server
openssh-client
sudo
tree
vim
# Add a non-privileged user.
RUN groupadd -g ${GROUP_ID} ${USER_NAME} &&
useradd -r --create-home -u ${USER_ID} -g ${USER_NAME} ${USER_NAME}
# Give the non-privileged user the privilege to run `sudo` without a password.
RUN echo "${USER_NAME} ALL=(ALL:ALL) NOPASSWD: ALL" > /etc/sudoers.d/${USER_NAME}
# Switch to the non-root user.
USER ${USER_NAME}
# The default command when the container is run.
CMD ["/bin/sleep", "infinity"]
通过运行以下docker build
命令:
docker build -f ./Dockerfile.ubuntu --tag port-binding .
生成的图像名为port-binding:latest
。
然后运行,首先with the default capabilities as listed here:
docker run --rm -it --name binding port-binding /bin/bash
然后我登录到容器并运行capsh --print
。我收到:
Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap+i
Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
Securebits: 00/0x0/1'b0
secure-noroot: no (unlocked)
secure-no-suid-fixup: no (unlocked)
secure-keep-caps: no (unlocked)
uid=1000(ywen)
gid=1000(ywen)
groups=
目前,我有cap_net_bind_service
能力。因此,当我在本文开头运行测试代码时,端口绑定可以成功,并且没有收到任何错误:
Python 3.6.9 (default, Oct 8 2020, 12:12:24)
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket as s
>>> o = s.socket(s.AF_INET)
>>> o.bind(("127.0.0.1", 1)) # Succeeded here.
>>>
我认为成功是意料之中的,因为容器具有cap_net_bind_service
功能。所以我停止了容器并启动了一个新的容器,它丢弃了cap_net_bind_service
:
docker run --rm -it --cap-drop=NET_BIND_SERVICE --name binding port-binding /bin/bash
在新容器内,capsh --print
未显示cap_net_bind_service
:
Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap+i
Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
Securebits: 00/0x0/1'b0
secure-noroot: no (unlocked)
secure-no-suid-fixup: no (unlocked)
secure-keep-caps: no (unlocked)
uid=1000(ywen)
gid=1000(ywen)
groups=
但是当我运行测试代码时,我发现仍然可以成功绑定端口1:
Python 3.6.9 (default, Oct 8 2020, 12:12:24)
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket as s
>>> o = s.socket(s.AF_INET)
>>> o.bind(("127.0.0.1", 1)) # Didn't raise an error. Still succeeded here.
>>>
但是,通过阅读以下帖子,我认为删除NET_BIND_SERVICE
应该是正确的做法。很明显,我在什么地方弄错了。有人能告诉我我做错了什么吗?- capabilities(7)
- https://superuser.com/a/892391/224429
- https://serverfault.com/a/112798/125167
推荐答案
我遇到了相反的问题-想要绑定到端口80,但无法绑定。两天的调试导致了这样的结果:https://github.com/moby/moby/pull/41030-由于docker 20.03.0容器的默认sysctl net.ipv4.ip_unPriviled_port_start设置为0,这与CAP_NET_BIND_SERVICE具有相同的效果-容器内的所有进程现在都可以绑定到(容器的)任何端口,即使作为无特权用户也是如此。它可以通过docker run --sysctl net.ipv4.ip_unprivileged_port_start=0 ...
或docker-compose.yml设置进行外部设置
sysctls:
- net.ipv4.ip_unprivileged_port_start=0
将其设置为1024可获得与docker 20.03.0之前版本相同的行为
这篇关于在我的Docker容器中,为什么还可以绑定没有`NET_BIND_SERVICE`能力的端口1?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:在我的Docker容器中,为什么还可以绑定没有`NET_BIND_SERVICE`能力的端口1?
基础教程推荐
- 如何在 Python 中检测文件是否为二进制(非文本)文 2022-01-01
- 将 YAML 文件转换为 python dict 2022-01-01
- 合并具有多索引的两个数据帧 2022-01-01
- 使 Python 脚本在 Windows 上运行而不指定“.py";延期 2022-01-01
- 哪些 Python 包提供独立的事件系统? 2022-01-01
- Python 的 List 是如何实现的? 2022-01-01
- 如何在Python中绘制多元函数? 2022-01-01
- 使用Python匹配Stata加权xtil命令的确定方法? 2022-01-01
- 使用 Google App Engine (Python) 将文件上传到 Google Cloud Storage 2022-01-01
- 症状类型错误:无法确定关系的真值 2022-01-01