用UFW来实现端口转发,理解PREROUTING和POSTROUTING链
我不用iptables转发是因为iptables太复杂了,而且现在很多服务器都压根没有iptables-save,而且有些服务器配置生效还不给起作用,网上查了ufw转发,虽然很多命令都是没啥问题的,但是为什么都不顺带提一下要修改 DEFAULT_FORWARD_POLICY
,这个呢?因为默认转发策略是 DROP 爬了多少坑,顺便测下 -A POSTROUTING -j MASQUERADE
具体生效方式。
(English version translate by GPT-3.5)
光速结论
如果对基本的vim都比较熟练的话,就直接贴命令了,10秒讲完的就不长篇大论了
首先编辑
/etc/default/ufw
,并找到 18行左右的DEFAULT_FORWARD_POLICY
,将值改为true1
vi /etc/default/ufw
设置
sysctl.conf
将net.ipv4.ip_forward=1
注释去掉,并确保值为1,然后执行sysctl -p
生效1
2vi /etc/sysctl.conf
sysctl -p编辑
/etc/ufw/before.rules
,并在*filter
上方,一般在最顶部,添加如下命令,33810
端口是坚听端口,200.120.130.140:33800
是转发端口,如果要支持UDP的话就再复制一份-A PREROUTING xxxx
然后将 tcp改成udp即可1
2
3
4
5*nat
:POSTROUTING ACCEPT [0:0]
-A PREROUTING -p tcp --dport 监听端口 -j DNAT --to-destination 目标转发IP地址:目标转发端口
-A POSTROUTING -j MASQUERADE
COMMIT重启ufw即可
1
ufw reload
单步结论
假设我现在有一台服务器A是 200.130.140.160
,另一台服务器B是 200.120.130.140
,那么我想通过服务器A的33810端口,能访问到服务器B的33800端口,那么我可以这么输入
首先编辑
/etc/default/ufw
1
vi /etc/default/ufw
设置 允许转发
1
2
3
4
5
6
7
8
9
10
11
12# Set the default output policy to ACCEPT, DROP, or REJECT. Please note that if
# you change this you will most likely want to adjust your rules.
DEFAULT_OUTPUT_POLICY="ACCEPT"
# Set the default forward policy to ACCEPT, DROP or REJECT. Please note that
# if you change this you will most likely want to adjust your rules
DEFAULT_FORWARD_POLICY="ACCEPT" // 找到这一行,大概在18行这里,将其改成 ACCEPT
# Set the default application policy to ACCEPT, DROP, REJECT or SKIP. Please
# note that setting this to ACCEPT may be a security risk. See 'man ufw' for
# details
DEFAULT_APPLICATION_POLICY="SKIP"设置
sysctl.conf
1
vi /etc/sysctl.conf
将
net.ipv4.ip_forward=1
注释去掉,并确保值为11
2# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1 // 进去后大概在第28行,将注释解除,或者在这个文件底部增加一条这个记录运行
sysctl -p
生效1
sysctl -p
运行后,确保输出的内容中包含
net.ipv4.ip_forward = 1
这一行,就想下面一样1
2root@test-server-1:~# sysctl -p
net.ipv4.ip_forward = 1编辑
/etc/ufw/before.rules
1
vi /etc/ufw/before.rules
在
*filter
之前添加如下命令1
2
3
4
5*nat
:POSTROUTING ACCEPT [0:0]
-A PREROUTING -p tcp --dport 33810 -j DNAT --to-destination 200.120.130.140:33800
-A POSTROUTING -j MASQUERADE
COMMIT修改后的文件看起来就像这样
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26// 这里是这个文件的一开头
#
# rules.before
#
# Rules that should be run before the ufw command line added rules. Custom
# rules should be added to one of these chains:
# ufw-before-input
# ufw-before-output
# ufw-before-forward
#
// 添加的内容开始 #########################################
*nat
:POSTROUTING ACCEPT [0:0]
-A PREROUTING -p tcp --dport 33810 -j DNAT --to-destination 200.120.130.140:33800
-A POSTROUTING -j MASQUERADE
COMMIT
// 添加的内容结束 #########################################
// 这里开始,是文件原来的内容
# Don't delete these required lines, otherwise there will be errors
*filter
:ufw-before-input - [0:0]
:ufw-before-output - [0:0]
:ufw-before-forward - [0:0]
:ufw-not-local - [0:0]
# End required lines重启ufw
1
ufw reload
ufw重启注意!!
重启ufw可能会导致iptables规则重复添加,ufw reload的时候不会删除iptables原有的规则,就会导致iptables有2条一摸一样的nat规则,如果这里出现修改,可能就会导致不生效,此时可以用下面命令进行删除,重复几条删几次
最好不要用 iptables -F,否则可能其他程序,例如Docker,会立马失联
1 | iptables -t nat -D PREROUTING -p tcp --dport 33810 -j DNAT --to-destination 200.120.130.140:33800 |
理解NAT PREROUTING POSTROUTING
在这之前
在理解prerouting链之前,得先知道IP数据包是什么样子的,虽然这个表格并没有完整表达数据包完整结构,但是数据包大致长这样
数据链路层(MAC层) | ||||||||||
源MAC地址(32) | 目标MAC地址(32) | 网络层(IP) 数据 | ||||||||
源IP地址(32) | 目标IP地址(32) | 协议类型(8) | 传输层(TCP/UDP) 数据 | |||||||
源端口号(16) | 目标端口号(16) | 应用层(例如 HTTP数据) 数据 | ||||||||
报文类型(8) | 消息长度(16) | 消息体 |
可以看到数据包是一层层下来的,最底层绿色的,也就是我们应用的数据,例如http header这些信息,而往上会封装一层IP信息,这里表明这个数据包发旺哪一个IP,理论上,只要没有遇到NAT Network Address Translation
网络地址转换协议,那么这个源IP和目标IP都不会变化。
当然实际数据没有没有外面包着里面的说法,它实际是从左到右像如下顺序拼接的
源MAC地址(32) | 目标MAC地址(32) | 源IP地址(32) | 目标IP地址(32) | 协议类型(8) | 源端口号(16) | 目标端口号(16) | 报文类型(8) | 消息长度(16) | 消息体 |
什么是PREROUTING和POSTROUTING
这里可以将其拆分成 pre routing和post routing,routing就是路由嘛(这里别理解成日常例会,就像memory是内存不是记忆。。。)pre表示pre-xxxx,即有在xxx之前,这里就可以理解成,在路由表前,而post,即在路由表后,每一个计算机里面都有自己的路由表(route print),所以这里的prerouting就表示,数据包从网卡进来,到路由表前,postrouting就是数据包在路由表判断后,即将离开计算机从网卡出去的数据,而转发就是在这2个地方做了文章。
详细解释
我们这里拿转发来描述,我(用户)想请求 200.130.140.160:33810
端口,并希望通过ufw转发到我另一台服务器 200.120.130.140:33810
,从而实现功能(很简单的需求)
首先我们数据包从本机出发,本机肯定是访问服务器A的端口,所以数据包如下(我们假设本机连接互联网,有公网IP 1.0.0.1,这里就不纠结是否经过路由器,是否内网IP问题了)
网络层(IP) 数据 1.0.0.1 200.130.140.160 TCP 传输层(TCP/UDP) 数据 31212 33810 应用层(例如 HTTP数据) 数据 数据包进入服务器A的ETH0网卡(此时数据包还没到服务器A路由表),此时PREROUTING规则生效,生效规则是当
33800
端口进来的TCP数据,将目标IP改成200.120.130.140
,目标端口改成33810
,此时数据包就变成如下样子网络层(IP) 数据 1.0.0.1 200.120.130.140 TCP 传输层(TCP/UDP) 数据 31212 33800 应用层(例如 HTTP数据) 数据 数据包进入服务器A路由表,此时路由表判断该数据是发往
200.120.130.140
的,因此数据即将从服务器A的网卡出去,在出去前,POSTROUTING生效了,POSTROUTING发现自己需要对IP进行伪装,然后它会将数据包进行修改,改成如下样子,将原始IP改为自己的IP,同时在自己的NAT表中记录下 稍后发给自己这个数据包,要返回给真实的 1.0.0.1,同时服务器A会开启一个随机端口51241
网络层(IP) 数据 200.130.140.160 200.120.130.140 TCP 传输层(TCP/UDP) 数据 51241 33800 应用层(例如 HTTP数据) 数据 数据包正式从服务器A出发前往服务器B,服务器B进行处理后,准备进行数据回发,此时IP和端口如下,源端口和目标端口调换,数据包回程
网络层(IP) 数据 200.120.130.140 200.130.140.160 TCP 传输层(TCP/UDP) 数据 33800 51241 应用层(例如 HTTP数据) 数据 数据包回到服务器A,进入PREROUTING规则,此时服务器A查询NAT规则,发现这个数据包在POSTROUTING进行了伪装,需要进行还原其真实IP 1.0.0.1 ,此时51241端口完成使命,端口关闭
网络层(IP) 数据 200.120.130.140 1.0.0.1 TCP 传输层(TCP/UDP) 数据 33800 51241(Closed) 应用层(例如 HTTP数据) 数据 数据包经过路由表,在POSTROUTING也就是即将离开网卡之前,它发现之前IP地址经过伪装,要进行反伪装,这下所有的IP都还原成原来的样子了
网络层(IP) 数据 200.130.140.160 1.0.0.1 TCP 传输层(TCP/UDP) 数据 33810 31212 应用层(例如 HTTP数据) 数据 数据回到用户这边,成功得到了想要的数据,这样,应该能看懂了吧。