带有 iptables 端口转发的源地址

带有 iptables 端口转发的源地址

我使用 iptables 设置了 80 端口的端口转发:

iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 10080  

此外,我已启用 IP 转发来路由来自不同子网的连接。

我在同一台机器的端口 10080 上运行我自己的标准套接字服务器,当我收到端口 80 的传入连接时,它会按预期转发到我的服务器。

我的问题是,当我使用 getsockname 和 getpeername 打印出套接字信息时,源地址始终是服务器的。如果我通过不受 iptables 规则影响的端口直接连接到服务器,我会看到预期的客户端地址。为什么端口转发会影响源地址?

答案1

我认为在这种情况下重定向目标不是正确的选择,动态 NAT 目标更合适。

摘录自https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/4/html/Security_Guide/s1-firewall-ipt-fwd.html

如果您的内部网络上有一台服务器,并且希望将其对外开放,则可以使用-j DNATNAT 中的 PREROUTING 链的目标来指定目标 IP 地址和端口,以便转发请求连接到内部服务的传入数据包。例如,如果您想将传入的 HTTP 请求转发到位于 172.31.0.23 的专用 Apache HTTP Server 服务器系统,请运行以下命令:

iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT \
  --to 172.31.0.23:80

答案2

我的问题是,当我使用 getsockname 和 getpeername 打印出套接字信息时,源地址始终是服务器的。如果我通过不受 iptables 规则影响的端口直接连接到服务器,我会看到预期的客户端地址。为什么端口转发会影响源地址?

我会解释,但请注意,这将是一个详细解释

首先让我们了解一下什么是TCP 连接(IP,Port):它是‘发起元组’和‘终止元组’之间的有状态的面向连接的数据交换(IP,port)

换句话说,TCP 连接由四重(Client_IP,Client_Port,Server_IP,Server_Port)

这意味着,该 TCP 连接的所有信息传输都需要满足四重定义。

现在,我们假设您的服务器位于 1.1.1.1,而客户端位于 2.2.2.2。客户端打开到您服务器端口 80 的连接。假设客户端的临时端口为 34567,则我们有四个端口 (2.2.2.2,34567,1.1.1.1,80)。

在您的服务器上,数据包被重定向到 1.1.1.1:10080。但这会导致不同的四重连接!例如,(2.2.2.2,34567,1.1.1.1,10080)。

那么实际的监听器会做什么呢?为什么它会直接响应 2.2.2.2:34567,导致数据包被客户端丢弃,因为客户端有 (2.2.2.2,34567,1.1.1.1,80) 四个字节,而没有 (2.2.2.2,34567,1.1.1.1,10080)。

因此,在这种情况下,REDIRECT目标必须创建一个临时的四重映射:

(2.2.2.2,34567,1.1.1.1,80) --> (1.1.1.1,x,1.1.1.1,10080)

(x 是一个临时的、短暂的端口,用于建立 TCP 连接)。

通过这种临时映射,10080 处的服务器响应netfilter,并且发生反向映射:

(1.1.1.1,x,1.1.1.1,10080) --> (2.2.2.2,34567,1.1.1.1,80)

netfilter将已经反向映射的数据包发送给客户端,一切正常。

因此,如果您需要知道客户端的 IP 地址,您可以不能使用REDIRECT目标。相反,将实际服务器放在不同的子网中,并使用DNAT目标,让 Linux 服务器充当完整路由器。假设实际服务器的本地 LAN 地址为 192.168.1.51,则映射将是:

(2.2.2.2,34567,1.1.1.1,80) --> (2.2.2.2,34567,192.168.1.51,80)

192.168.1.51的服务器会回复2.2.2.2,路由器会进行反向映射:

(2.2.2.2,34567,192.168.1.51,80) --> (2.2.2.2,34567,1.1.1.1,80)

一切都好。

相关内容