防止GitHub Actions中断iptables规则(链默认为DROP)

78次阅读
没有评论

问题描述

假设我的iptables规则在INPUT和OUTPUT链上默认为DROP,那么我必须向我的链中添加的最少规则是什么,以防止在GitHub Actions中运行的脚本无限期地停滞不前?
我正在使用(免费的)GitHub Actions 作为我的开源应用程序的CI/CD基础设施。当我推送更改到github.com时,它会自动在Microsoft的云中旋转一个Ubuntu 18.04 Linux服务器,检出我的仓库并执行一个BASH脚本来构建我的应用程序。
出于安全原因,在我的构建脚本中,我早期安装和设置了一些非常严格的iptables规则,它们在INPUT和OUTPUT链上默认为DROP。我在防火墙中为127.0.0.1、INPUT上的RELATED/ESTABLISHED,以及只允许_apt用户通过OUTPUT发送流量打了一个洞。
当我在本地系统上的docker容器中运行构建脚本时,这个方法非常好用。但是,正如我刚刚了解到的那样,当它在GitHub Actions中运行时,它会无限期地停滞不前。显然,实例本身需要能够与GitHub的服务器通信才能完成。而我似乎破坏了这一点。
所以问题是:我应该向我的iptables INPUT和OUTPUT链中添加什么-j ACCEPT规则,以只允许GitHub Actions执行正常进行所需的最低限度?
以下是我的构建脚本中设置防火墙的片段的参考:

################### SETUP IPTABLES #################### We setup iptables so that only the apt user (and therefore the apt command)# can access the internet. We don't want insecure tools like `pip` to download# unsafe code from the internet.${SUDO} iptables-save > /tmp/iptables-save.`date "+%Y%m%d_%H%M%S"`${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 100 -j ACCEPT # apt uid = 100${SUDO} iptables -A OUTPUT -j DROP${SUDO} ip6tables-save > /tmp/ip6tables-save.`date "+%Y%m%d_%H%M%S"`${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 100 -j ACCEPT${SUDO} ip6tables -A OUTPUT -j DROP# attempt to access the internet as root. If it works, exit 1curl -s 1.1.1.1if [ $? -eq 0 ]; then        echo "ERROR: iptables isn't blocking internet access to unsafe tools. You may need to run this as root (and you should do it inside a VM)"        exit 1fi

解决方案

请注意以下操作注意版本差异及修改前做好备份。

方案1

可以通过在docker容器中运行构建脚本并在容器内应用iptables规则来实现。这不会影响主机runner的连接。
例如,如果在GitHub Actions作业中执行以下脚本(在Ubuntu 18.04 GitHub共享runner中),它将在一个没有互联网连接的debian docker容器中运行构建脚本(docker_script.sh),除了_apt用户之外。

#!/bin/bashset -x#################### INSTALL DEPENDS ####################apt-get -y install docker.io################### DOWNLOAD IMAGE #################### At the time of writing, Docker Content Trust is 100% security theater without# explicitly adding the root public keys to the $HOME/.docker/trust/ directory##  * https://github.com/BusKill/buskill-app/issues/6#issuecomment-700050760#  * https://security.stackexchange.com/questions/238529/how-to-list-all-of-the-known-root-keys-in-docker-docker-content-trust#  * https://github.com/docker/cli/issues/2752docker -D pull debian:stable-slim################## CREATE SCRIPT ##################tmpDir=`mktemp -d`pushd "${tmpDir}"cat << EOF > docker_script.sh#!/bin/bashset -x# SETTINGS #SUDO=/usr/bin/sudo# DEPENDS #${SUDO} apt-get update${SUDO} apt-get install iptables curl# IPTABLES ## We setup iptables so that only the apt user (and therefore the apt command)# can access the internet. We don't want insecure tools like `pip` to download# unsafe code from the internet.${SUDO} iptables-save > /tmp/iptables-save.`date "+%Y%m%d_%H%M%S"`${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 100 -j ACCEPT # apt uid = 100${SUDO} iptables -A OUTPUT -j DROP${SUDO} ip6tables-save > /tmp/ip6tables-save.`date "+%Y%m%d_%H%M%S"`${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 100 -j ACCEPT${SUDO} ip6tables -A OUTPUT -j DROP# attempt to access the internet as root. If it works, exit 1curl 1.1.1.1if [ $? -eq 0 ]; then        echo "ERROR: iptables isn't blocking internet access to unsafe tools. You may need to run this as root (and you should do it inside a VM)"        exit 1fi# BUILD ## ...# <DO BUILD HERE># ...exit 0EOFchmod +x docker_script.sh############### DOCKER RUN ###############docker run --rm --cap-add "NET_ADMIN" -v "${tmpDir}:/root/shared_volume" debian:stable-slim /bin/bash -c "cd /root/shared_volume && docker_script.sh"# exit cleanlyexit 0

注意:
1. 您必须手动执行docker run命令,而不仅仅在GitHub Actions yaml文件中指定容器:,以添加NET_ADMIN功能。参见https://stackoverflow.com/questions/63959783/how-to-run-script-in-docker-container-with-additional-capabilities-docker-exec/64045054#64045054
2. 这是一个安全风险,除非在调用docker pull之前固定根签名密钥。参见https://security.stackexchange.com/questions/238529/how-to-list-all-of-the-known-root-keys-in-docker-docker-content-trust
3. 上述脚本应以root身份执行。例如,在GitHub Actions工作流程的步骤中的run:键之前添加sudo。

方案2

使用脚本或工具来管理容器的启动顺序可能会增加复杂性,并且需要确保容器A和容器B之间的依赖关系正确设置。
另一种方法是编写脚本或使用工具来控制容器的运行顺序。您可以使用docker run命令来手动控制容器的启动顺序,或者使用一些第三方工具来管理容器的依赖关系。

示例:

以下是一个简单的bash脚本示例,可以在容器A启动后启动容器B:

#!/bin/bash
# 启动容器A
docker run -d --name container_a your_image_a
# 等待容器A完全启动
while ! docker exec container_a echo "Container A is ready"; do
  sleep 1
done
# 启动容器B
docker run -d --name container_b your_image_b

在这个示例中,我们首先使用docker run命令启动容器A,并将其命名为container_a。然后,使用一个循环来等待容器A完全启动(这里是通过在容器内运行echo命令来测试)。一旦容器A就绪,我们再使用docker run命令启动容器B,并将其命名为container_b。

正文完