Docker 和 UFW 默认情况下不兼容,因为它们都会尝试修改相同的 iptables 规则集。当 Docker 映射容器端口到宿主机时,它会自动修改 iptables 规则以允许流量通过,而 UFW 的规则则不会影响这些由 Docker 管理的端口,导致 UFW 的规则看起来没有生效。

为了解决这个问题,可以通过以下步骤让 UFW 控制 Docker 的端口:

  1. 禁用 Docker 修改 iptables 的能力:在 Docker 的配置文件中设置 iptablesfalse。对于使用 systemd 的系统,可以创建一个 docker.service.d 目录下的 override.conf 文件来覆盖默认的 Docker 启动配置,而不是修改 /etc/default/docker 文件。例如:

    mkdir -p /lib/systemd/system/docker.service.d
    cat << EOF > /lib/systemd/system/docker.service.d/override.conf
    [Service]
    ExecStart=
    ExecStart=/usr/bin/dockerd
    EOF
    cat << EOF > /etc/docker/daemon.json
    {
      "hosts": ["fd://"],
      "dns": ["8.8.8.8", "8.8.4.4"],
      "iptables": false
    }
    EOF
    systemctl daemon-reload && systemctl restart docker

    这样,Docker 就不会尝试管理 iptables 规则了,而是由 UFW 接管。

  2. 配置 UFW 以允许 Docker 流量:修改 UFW 的配置文件 /etc/ufw/after.rules,在文件末尾添加以下规则,以确保 Docker 容器的流量可以通过 UFW:

    # BEGIN UFW AND DOCKER
    *filter
    :ufw-user-forward - [0:0]
    :ufw-docker-logging-deny - [0:0]
    :DOCKER-USER - [0:0]
    -A DOCKER-USER -j ufw-user-forward
    -A DOCKER-USER -j RETURN -s 10.0.0.0/8
    -A DOCKER-USER -j RETURN -s 172.16.0.0/12
    -A DOCKER-USER -j RETURN -s 192.168.0.0/16
    -A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN
    -A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
    -A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
    -A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
    -A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
    -A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
    -A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 172.16.0.0/12
    -A DOCKER-USER -j RETURN
    -A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "
    -A ufw-docker-logging-deny -j DROP
    COMMIT
    # END UFW AND DOCKER

    然后重启 UFW 以应用更改:

    sudo systemctl restart ufw

    现在,UFW 应该能够控制 Docker 容器的端口了。

  3. 允许特定端口的访问:如果需要允许外部访问某个容器的特定端口,可以使用 ufw route 命令,例如:

    ufw route allow proto tcp from any to any port 80

    这将允许外部网络访问所有 Docker 容器内部服务端口为 80 的服务。如果只想允许访问特定容器,可以指定容器的内部 IP 地址:

    ufw route allow proto tcp from any to 172.17.0.2 port 80

    其中 172.17.0.2 是容器的内部 IP 地址。

通过这些步骤,你应该能够使 UFW 控制 Docker 容器的端口,同时保持系统的安全性。

ufw相关命令

ufw status numbered
ufw delete 2
ufw allow from 192.168.1.100 to any port 22 proto tcp
ufw allow from 192.168.1.0/24 to any port 21 proto tcp
ufw allow 80/tcp
ufw reload