由于更换了VPS供应商,折腾了一下防火墙的配置。主要解决了Debian 12的防火墙与Docker的兼容问题。
1. 概述
参考:
- nftables - Debian Wiki
- nftables wiki
- iptables - Debian Wiki
- Packet filtering and firewalls | Docker Docs
- 使用iptables管控docker容器
1.1. 主要问题
Debian 12与Docker在防火墙方面的不兼容:
- Debian从10开始默认使用nftables配置和管理防火墙,而Docker还在使用iptables(且不支持nftables)。
- Docker网络在防火墙上有一套自己的规则。
- 防火墙规则,一般是清空再加载,不同的配置会出现被覆盖的问题。
1.2. 网络模型
OSI模型,Open Systems Interconnection model(即开放式系统互联模型),是一个描述网络功能的概念框架。其分为7层。这是大学计算机课程《计算机网络》里的基本知识。
TCP/IP模型,是互联网的基础,它是一系列网络协议的总称。分5层或4层。
OSI模型是学术上和法律上的国际标准,是完整的权威的网络参考模型。而TCP/IP参考模型是事实上的国际标准,即现实生活中被广泛使用的网络参考模型。
两个模型的层次划分如下:
| OSI模型(7层) | TCP/IP模型(5层) | TCP/IP模型(4层) |
|---|---|---|
| 第7层 应用层 | 第5层 应用层 | 第4层 应用层 |
| 第6层 表现层 | ||
| 第5层 会话层 | ||
| 第4层 传输层 | 第4层 传输层 | 第3层 传输层 |
| 第3层 网络层 | 第3层 网络层 | 第2层 网络层 |
| 第2层 数据链路层 | 第2层 数据链路层 | 第1层 网络接口层 |
| 第1层 物理层 | 第1层 物理层 |
1.3. netfilter框架
netfilter是Linux内核中的一个数据包处理框架,其工作在网络模型的上层协议(对应OSI模型第5层及其以上,或TCP/IP模型的应用层)。iptables和nftables是用于管理netfilter。
netfilter提供了5个hook点,数据包经过协议栈时会触发内核模块注册在这里的处理函数。iptables和nftables配置这些处理函数对数据包的管理,实现防火墙的功能。
netfilter提供5个Hook点如下图:
User space 上层协议堆栈(例如HTTP协议的Web服务)
数据终点(收) 数据起点(发)
↑ |
------------------│------------------------------------|---------------
Kernel space │ ⭣
input output
LOCAL_IN ↑ | LOCAL_OUT
| ⭣
目标IP是本机 路由查询2
PRE_ROUTING | ⭣ POST_ROUTING
prerouting ⭢ 路由查询1 -目标IP不是本机⭢ forward ⭢ postrouting -┐
↑ | |
------|------------------------------------------------|-------|------
| Network adapter | |
------|------------------------------------------------|-------|------
User -┘ | |
←-------------------------------------------------┘ └→ 转发给其它主机
- PRE_ROUTING: 是所有接收数据包到达的第一个hook触发点,此处将进行数据包目的地转换 (DNAT), 决定数据包是发给 本地进程、其他机器、其他network namespace
- LOCAL_IN: 经过路由判断后,目标地址是本机的接收数据包到达此hook触发点
- FORWARD: 经过路由判断后,目标地址不是本机地址的数据包到达此hook触发点
- LOCAL_OUT: 所有本地生成的发往其他机器的包, 在进入网络栈后首先到达此hook触发点
- POST_ROUTING: 本机产生准备发出的包或者转发的包,在经过路由判断后到达此hook触发点
参考:
2. 问题
结合上面的原理,针对Debian 12部署Docker所出现的防火墙问题,整理解决方案。
2.1. 管理工具的冲突
- 问题:Docker使用iptbales,Debian 12使用nftables,两个不同的防火墙管理工具。
- 解决:
- Debian 12虽然默认使用nftables,但其提供了iptables-nft,把iptbales的配置转为nftables,实现对iptables的兼容。
- Debian 12安装iptables,默认采用iptables-nft。即Docker执行iptbales命令时,实际是调用了iptables-nft。
- 另外,如果Debian 12上需要使用原来的iptbales,需要安装iptables-legacy。
2.2. 防火墙配置冲突
- 问题:按照一般的教程配置完防火墙,开机后会导致Docker的防火墙规则丢失。
- 原因:
- 一般的防火墙配置教程,会在开机后执行
iptables-restore或nft -f来加载一套自定义的防火墙规则。 - 这个操作一般在Docker服务启动后执行,导致Docker配置好的防火墙规则被清除。
- 一般的防火墙配置教程,会在开机后执行
- 解决:
- 使用netfilter-persistent命令保存防火墙规则。
- netfilter-persistent会在网络启动后,Docker服务启动前,加载用户的防火墙规则。然后Docker服务启动后再加载自己的防火墙规则,实现不冲突。
- 另外,Docker在FORWARD链里创建了子链DOKCER-USER,提供给用户配置针对Docker的防火墙规则。Docker服务重启后,不会清除或修改DOKCER-USER链的规则。
2.3. Docker容器映射到宿主机端口的配置
- 问题:Docker容器映射到宿主机端口,在INPUT链的规则对其无效。
- 原因:
- 防火墙的INPUT链,只针对本机运行服务所开启的端口。
- 访问Docker容器映射到宿主机端口时,没有走INPUT链,而是走FORWARD链。
- 解决:
- Docker容器映射到宿主机端口的防火墙规则,配置在FORWARD链里的DOKCER-USER链。
2.4. DOKCER-USER链的配置
- 问题:如何把DOKCER-USER链配置为“白名单”?
- 解决:
- 由于DOKCER-USER链是FORWARD链的子链,要先理解FORWARD链。FORWARD链就像混合了INPUT和OUTPUT的规则。
- DOKCER-USER链配置为“白名单”时,先允许(ACCEPT)指定宿主机端口流入对应Docker容器端口,再禁止(REJECT)从宿主机网口流入到Docker容器,最后把其它数据包交给(RETURN)DOCKER-FORWARD链处理(例如从Docker容器流出)。
3. 配置
通常Debian 12预装了iptables,但需要安装netfilter-persistent命令,对应的安装包为iptables-persistent。
sudo apt update && sudo apt -y install iptables-persistent
查看当前iptables配置
# 列出所有链的规则,简单信息
sudo iptables -L
# 列出所有链的规则,详细信息。`-n`端口、协议显示为数字,`-v`详细模式,`--line-numbers`各个链配置显示行号
sudo iptables -L -n -v --line-numbers
# 查看`DOCKER-USER`链的规则。`DOCKER-USER`是链的名称,可以替换为其它链。
sudo iptables -L DOCKER-USER -n -v --line-numbers
iptables配置参考
# 设置默认允许所有连接,方便配置
sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT
# 配置INPUT链
# INPUT链允许本地回环
sudo iptables -A INPUT -i lo -j ACCEPT
# INPUT链允许已建立的连接
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# INPUT链允许`ping`命令访问
sudo iptables -A INPUT -p icmp -j ACCEPT
# INPUT链开启SSH端口(22)
sudo iptables -A INPUT -p tcp -m conntrack --ctstate NEW --dport 22 -j ACCEPT
# INPUT链开启HTTP端口(80),如果是Docker容器提供HTTP服务,此配置可以不执行
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
# INPUT链开启HTTPS端口(443),如果是Docker容器提供HTTPS服务,此配置可以不执行
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# 配置DOCKER-USER链
# DOCKER-USER链清空配置
sudo iptables -F DOCKER-USER
# DOCKER-USER链允许已建立的连接
sudo iptables -A DOCKER-USER -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# DOCKER-USER链允许HTTP服务(端口80)访问,且只允许数据包从宿主机80端口(参数`--ctorigdstport`的值)流入任意Docker容器80端口(参数`--dport`的值)
sudo iptables -A DOCKER-USER -p tcp --dport 80 -m conntrack --ctorigsrc 0.0.0.0/0 --ctorigdstport 80 -j ACCEPT
# DOCKER-USER链允许HTTP服务(端口443)访问,且只允许数据包从宿主机443端口(参数`--ctorigdstport`的值)流入任意Docker容器443端口(参数`--dport`的值)
sudo iptables -A DOCKER-USER -p tcp --dport 443 -m conntrack --ctorigsrc 0.0.0.0/0 --ctorigdstport 443 -j ACCEPT
# DOCKER-USER链禁止数据包从宿主机网口`enp1s0`(需按实际情况修改)流入,实现DOCKER-USER链“白名单”功能
sudo iptables -A DOCKER-USER -i enp1s0 -j REJECT
# DOCKER-USER链允许其它数据包,例如从Docker容器流出的数据包
sudo iptables -A DOCKER-USER -j RETURN
# INPUT链和FORWARD链默认禁止,即配置为“白名单”模式
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
# 保存配置,保存在`/etc/iptables/rules.v4`和`/etc/iptables/rules.v6`
sudo netfilter-persistent save
注意:
iptables命令是即时生效。最好确认能直接操作实体机或者VPS,避免防火墙配置失败而不能访问。- 保证配置正确后,才执行“保存配置”的命令。即使配置错误,可以通过重启系统进行还原防火墙。
- Docker的防火墙配置,会在Docker服务器重启后自动配置,所以这里保存配置时不用理会是否包含了Docker的防火墙配置。另一层意思是,即使搞坏了Docker的防火墙配置,重启Docker服务即可。