我已经设置好了。最复杂的部分是——即使你是 root 用户——在默认的 Discourse Docker 容器内尝试查看或编辑 iptables 规则时,仍然会收到“权限被拒绝”的错误。
root@24a1f9f4c038:/# iptables -L
# Warning: iptables-legacy tables present, use iptables-legacy to see them
iptables: Permission denied (you must be root).
root@24a1f9f4c038:/#
这是因为 Discourse 的 launcher 脚本(用于包装 Docker 命令)在开箱即用的情况下,运行 Discourse Docker 容器时未启用“NET_ADMIN”能力。
为 Discourse Docker 容器添加 NET_ADMIN 能力最稳健的方法是更新容器的 yaml 文件,通过 docker_args yaml 字符串,向 docker run ... /sbin/boot 命令添加必要的参数:
docker_args: "--cap-add NET_ADMIN"
例如:
[root@osestaging1 discourse]# head -n15 containers/app.yml
## this is the all-in-one, standalone Discourse Docker container template
##
## After making changes to this file, you MUST rebuild
## /var/discourse/launcher rebuild app
##
## BE *VERY* CAREFUL WHEN EDITING!
## YAML FILES ARE SUPER SUPER SENSITIVE TO MISTAKES IN WHITESPACE OR ALIGNMENT!
## visit http://www.yamllint.com/ to validate this file as needed
docker_args: "--cap-add NET_ADMIN"
templates:
- "templates/postgres.template.yml"
- "templates/redis.template.yml"
- "templates/web.template.yml"
[root@osestaging1 discourse]#
这些参数将通过 $user_args 变量被添加到由 launcher 执行的 docker run ... /sbin/boot 命令中:
[root@osestaging1 discourse]# grep -A2 -E '^\s*\$docker_path run' launcher
$docker_path run --shm-size=512m $links $attach_on_run $restart_policy "${env[@]}" "${labels[@]}" -h "$hostname" \
-e DOCKER_HOST_IP="$docker_ip" --name $config -t "${ports[@]}" $volumes $mac_address $user_args \
$run_image $boot_command
[root@osestaging1 discourse]#
由于在 docker pull 之后、docker run ... /sbin/boot 之前对 Discourse Docker 镜像进行内嵌修改非常复杂,我决定通过一个由 runit 在容器启动时执行的简单脚本来安装和配置 iptables。
以下命令将创建必要的模板 yaml 文件,该文件将在下一次 ./launcher rebuild app 时生成 runit 脚本:
cat << EOF > /var/discourse/templates/iptables.template.yml
run:
- file:
path: /etc/runit/1.d/01-iptables
chmod: "+x"
contents: |
#!/bin/bash
################################################################################
# File: /etc/runit/1.d/01-iptables
# Version: 0.2
# Purpose: installs & locks-down iptables
# Author: Michael Altfield <michael@opensourceecology.org>
# Created: 2019-11-26
# Updated: 2019-12-17
################################################################################
sudo apt-get update
sudo apt-get install -y iptables
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A INPUT -s 127.0.0.1/32 -j DROP
sudo iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A INPUT -j DROP
sudo iptables -A OUTPUT -s 127.0.0.1/32 -d 127.0.0.1/32 -j ACCEPT
sudo iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A OUTPUT -m owner --uid-owner 0 -j ACCEPT
sudo iptables -A OUTPUT -m owner --uid-owner 100 -j ACCEPT
sudo iptables -A OUTPUT -j DROP
sudo ip6tables -A INPUT -i lo -j ACCEPT
sudo ip6tables -A INPUT -s ::1/128 -j DROP
sudo ip6tables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo ip6tables -A INPUT -j DROP
sudo ip6tables -A OUTPUT -s ::1/128 -d ::1/128 -j ACCEPT
sudo ip6tables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo ip6tables -A OUTPUT -m owner --uid-owner 0 -j ACCEPT
sudo ip6tables -A OUTPUT -m owner --uid-owner 100 -j ACCEPT
sudo ip6tables -A OUTPUT -j DROP
EOF
请注意,这是一个非常基础的 iptables 配置。使用 web.socketed.template.yml 文件(我使用的配置)时,Docker 容器实际上根本不需要互联网访问权限,因为 Docker 主机上的 nginx 反向代理通过 Unix 域套接字与 Docker 容器内的 Discourse nginx 进行通信。我允许互联网访问的主要原因是为了让 Docker 容器的基础操作系统能够通过 unattended-upgrades 自动更新关键安全补丁。因此,我为用户 100(即 apt 用户)开放了访问权限。
最后,将上述 templates/iptables.template.yml 文件添加到容器的 app.yaml 文件中。
[root@osestaging1 discourse]# head -n15 /var/discourse/containers/discourse_ose.yml
## this is the all-in-one, standalone Discourse Docker container template
##
## After making changes to this file, you MUST rebuild
## /var/discourse/launcher rebuild app
##
## BE *VERY* CAREFUL WHEN EDITING!
## YAML FILES ARE SUPER SUPER SENSITIVE TO MISTAKES IN WHITESPACE OR ALIGNMENT!
## visit http://www.yamllint.com/ to validate this file as needed
docker_args: "--cap-add NET_ADMIN"
templates:
- "templates/iptables.template.yml"
- "templates/postgres.template.yml"
- "templates/redis.template.yml"
[root@osestaging1 discourse]#
现在你应该可以执行:
./launcher rebuild app
等待大约 10 分钟后,你的 Discourse Docker 容器中的 iptables 将完全正常运行 