在我的 Docker 容器中,为什么我仍然可以在没有“NET_BIND_SERVICE”功能的情况下绑定端口 1?

问题描述

我正在使用 Hex: 2A D7 B1 83 0F 42 40 Integers: 42 215 177 131 15 66 64 。以下是有关我的问题的更多详细信息。

最近,我正在编写一些想要执行此操作的测试代码:当它作为非特权用户运行时,测试代码尝试绑定特权端口(在我的情况下为端口 1)并期望绑定到失败。

在我的主机上,我当前的非特权用户具有以下 1000000 输出:

Ubuntu 18.04 Desktop

因此,在尝试使用当前非特权用户绑定端口 1 时,我可以按预期获得权限拒绝错误:

capsh --print

因为我的测试代码最终会在 Docker 容器中运行,所以我使用以下 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) 构建了一个映像:

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()

通过运行以下 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

然后我运行它,首先with the default capabilities as listed here

docker build -f ./Dockerfile.ubuntu --tag port-binding .

然后我登录到容器并运行 port-binding:latest。我得到:

docker run --rm -it --name binding port-binding /bin/bash

目前,我拥有 capsh --print 能力。因此,当我运行本文开头的测试代码时,端口绑定可以成功并且没有出现任何错误:

Current: = cap_chown,cap_setfcap+i
Bounding set =cap_chown,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,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

但是当我运行测试代码时,我发现我仍然可以成功绑定端口1:

cap_net_bind_service

但是,通过阅读以下帖子,我认为删除 Current: = cap_chown,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= 应该是正确的做法。显然,我在某个地方犯了一个错误。 谁能告诉我我做错了什么?

解决方法

我遇到了相反的问题 - 想绑定到端口 80 但不能。两天的调试导致这个:https://github.com/moby/moby/pull/41030 - 从 docker 20.03.0 开始,容器的默认 sysctl net.ipv4.ip_unprivileged_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 之前相同的行为

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...