惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
S
Secure Thoughts
C
Cyber Attacks, Cyber Crime and Cyber Security
SecWiki News
SecWiki News
AWS News Blog
AWS News Blog
P
Proofpoint News Feed
H
Help Net Security
I
InfoQ
T
Tor Project blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Schneier on Security
Schneier on Security
The GitHub Blog
The GitHub Blog
S
Schneier on Security
The Hacker News
The Hacker News
博客园 - Franky
雷峰网
雷峰网
S
Security @ Cisco Blogs
Google Online Security Blog
Google Online Security Blog
Engineering at Meta
Engineering at Meta
G
GRAHAM CLULEY
Security Latest
Security Latest
The Cloudflare Blog
M
MIT News - Artificial intelligence
云风的 BLOG
云风的 BLOG
K
Kaspersky official blog
C
Cisco Blogs
Microsoft Security Blog
Microsoft Security Blog
宝玉的分享
宝玉的分享
Scott Helme
Scott Helme
腾讯CDC
阮一峰的网络日志
阮一峰的网络日志
A
Arctic Wolf
Latest news
Latest news
Last Week in AI
Last Week in AI
The Register - Security
The Register - Security
Google DeepMind News
Google DeepMind News
博客园 - 聂微东
T
Threatpost
G
Google Developers Blog
有赞技术团队
有赞技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
L
LINUX DO - 热门话题
aimingoo的专栏
aimingoo的专栏
S
Securelist
WordPress大学
WordPress大学
N
Netflix TechBlog - Medium
Y
Y Combinator Blog
T
The Exploit Database - CXSecurity.com
月光博客
月光博客
Recent Announcements
Recent Announcements

达拉斯・卢

OpenWRT 使用 udp2raw 对抗 WireGuard 阻断 博客程序架构的思考与展望 如何证明你是原创作者 Nginx 泛域名配置的隐患与对策 WISeID S/MIME 证书 V2EX 刑满释放记 使用 Radicale 在 Ubuntu 24.04 中搭建 vCards CardDav 服务 邮件服务的域名成功从 SURBL 黑名单移除 网站多语言的设计细节 网站评论系统的目前进展和展望 邮件投递平台 Postal 的使用经验 自建 Postal 完美替代 SendGrid 互联网在崩塌吗,然后呢 在 SvelteKit 应用中使用 JSON-LD 网页的打印样式应该怎么写 “茴字的四种写法”之 IP 与域名 怎么伪造 Git 提交的时区 供大众交流的论坛和其它替代产品还是不好用 改编不是照搬 一次排查诡异的网络问题的经历
在公网使用 iptables 转发端口时保留客户端 IP
达拉斯・卢 · 2024-07-12 · via 达拉斯・卢

此前,我曾经使用一个闲置的 IP,做了端口转发,以隐藏自己的 Forgejo 服务器 IP。这种方式简直太棒了。直到我最近搭建了自己邮箱服务,发现使用 iptables 经由公网做的端口转发,在应用端并不能正确获得客户端 IP。在非正式的对外应用中,这一点并无影响。邮箱服务中使用 Greylist 来抵御垃圾邮件,发信者的 IP 是一个重要的参数。因此我需要获得真正的客户端 IP。

flowchart LR C[Client] --> B[Proxy] --> A[Server]

在网关转发

在网关将某端口转发到内网中的一台服务器,是非常简单的,只需要一句话:

iptables -t nat -A PREROUTING -p tcp -d 1.1.1.1 --dport 80 -j DNAT --to-destination 10.0.0.2:80

flowchart LR subgraph Client C[3.3.3.3] end subgraph Subnet subgraph Proxy eth0[1.1.1.1] --> eth1[10.0.0.1] eth1 -.-> eth0 end subgraph Server eth1 --3.3.3.3->10.0.0.2--> A[10.0.0.2] A -.->|10.0.0.2>3.3.3.3| eth1 end end C --3.3.3.3->1.1.1.1--> eth0 eth0 -.->|1.1.1.1->3.3.3.3| C

在内网服务器的应用中,能直接获得正确的客户端 IP。无论是使用 tcpdump 还是 netstat 都能看到来自互联网的 IP 与本机建立了连接。

在公网转发

同样需要指定转发目标:

iptables -t nat -A PREROUTING -p tcp -d 1.1.1.1 --dport 80 -j DNAT --to-destination 2.2.2.2:80

到目前为止,这个转发并不能正常使用。当来自互联网的 3.3.3.3 向 1.1.1.1:80 通讯时,数据包被转发到了 2.2.2.2:80,其 Web 服务在返回握手消息时,其数据包则按自身的路由规则,经过 2.2.2.2 出口返给了 3.3.3.3。这个连接是不能建立的。

flowchart LR subgraph Client C[3.3.3.3] end subgraph Proxy eth0[1.1.1.1] end subgraph Server eth1[2.2.2.2] end eth0 --3.3.3.3>2.2.2.2-->eth1 C --3.3.3.3->1.1.1.1--> eth0 eth1 ~~~|2.2.2.2->3.3.3.3| C

于是我们需要在 1.1.1.1 中,把数据包的来源也修改成 1.1.1.1,以转发 2.2.2.2 给出的回应,以保证与 3.3.3.3 的正常通讯和链接。

flowchart LR subgraph Client C[3.3.3.3] end subgraph Proxy eth0[1.1.1.1] end subgraph Server eth1[2.2.2.2] end eth0 --1.1.1.1>2.2.2.2-->eth1 eth1 -.->|2.2.2.2>1.1.1.1|eth0 C --3.3.3.3->1.1.1.1--> eth0 eth0 -.->|1.1.1.1->3.3.3.3| C

使用 SNAT:

iptables -t nat -A POSTROUTING -p tcp -d 2.2.2.2 --dport 80 -j SNAT --to-source 1.1.1.1

使用有限制条件的 MASQUERADE

iptables -t nat -A POSTROUTING -p tcp -d 2.2.2.2 --dport 80 -j MASQUERADE

使用宽泛条件的 MASQUERADE

iptables -t nat -A POSTROUTING ! -o lo -j MASQUERADE

这样也造成了 2.2.2.2 一直以为是 1.1.1.1 与自己通讯,完全不知 3.3.3.3 的存在。

公网转发保留客户端 IP

如果从 1.1.1.1 向 2.2.2.2 发送的数据包,其来源为原始客户端 IP,假设为 3.3.3.3,其目标为 2.2.2.2。那么当 2.2.2.2 收到数据后,如果有一些过滤规则存在,可能根本不会作出响应。因为 2.2.2.2 通往 3.3.3.3 的路由,与这个数据包的来路并不相同。

如果我们关闭这些过滤规则,则可能导致 2.2.2.2 这个 IP 会更容易受到攻击,因为很难区分。如果在 2.2.2.2 上,额外配置一个 IP,专门接受来自 1.1.1.1 的转发,又比较浪费。如果修改 2.2.2.2 的路由,使其全部流量都经过 1.1.1.1,又会使得 2.2.2.2 无法正常处理来自互联网的直接请求。

建立专用隧道

一些代理实现的方式,比如 Haproxy 或者 Transport Proxy,都需要 2.2.2.2 在应用层面做支持。如在 nginx中,需要明确指定 proxy_protocol 或者 transport。如果应用不支持,那么就无法使用。

此外,这个场景只有两个节点直连,并不需要负载均衡高可用之类,使用 iptables 比使用代理软件更简单。

所以,一个更好的办法是,给 1.1.1.1 和 2.2.2.2 建立一个专用的隧道。建立隧道有很多种方式。同样地,建立隧道也有很多其他选择,比如 OpenVPN / WireGuard,鉴于这个场景需求,我选择了更简单的 GRE。

GRE

在 1.1.1.1 上 建立隧道,并配置 IP 地址。

ip tunnel add gre1 mode gre remote 2.2.2.2 local 1.1.1.1 ttl 255
ip addr add 10.0.0.1/24 dev gre1
ip link set gre1 up

将本地公网的流量,转发到隧道的另一端。

iptables -t nat -A PREROUTING -p tcp -d 1.1.1.1 --dport 80 -j DNAT --to-destination 10.0.0.2:80

在 2.2.2.2 上建立隧道,并配置 IP 地址。

ip tunnel add gre1 mode gre remote 1.1.1.1 local 2.2.2.2 ttl 255
ip addr add 10.0.0.2/24 dev gre1
ip link set gre1 up

甚至已经不需要关闭 rp_filter 了。添加路由表:

echo "200 custom" >> /etc/iproute2/rt_tables

设定路由表规则和路由信息,让这个隧道端点的流量,哪里来就哪里去。

ip rule add from 10.0.0.2 table custom
ip route add default via 10.0.0.1 dev gre1 table custom

flowchart LR subgraph Client C[3.3.3.3] end subgraph Proxy subgraph b[1.1.1.1] eth1[10.0.0.1] end end subgraph Server subgraph a[2.2.2.2] eth11[10.0.0.2] end end b o--o|GRE| a eth1 -->|3.3.3.3->10.0.0.2| eth11 eth11 -.->|10.0.0.2->3.3.3.3| eth1 C --3.3.3.3->1.1.1.1--> b b -.->|1.1.1.1->3.3.3.3| C

保持活跃

crontab -e
*/1 * * * * ping -c 60 10.0.0.2

结语

使用 iptables 端口转发、GRE隧道,可轻松实现隐藏真实 IP 的同时,还能获取到真实的客户端 IP。这个过程,并不需要安装额外的软件,同时也适用于各种应用软件而无需另做适配。非常适合使用闲置 VPS 做前置机,充分利用线路好的 VPS,为你的应用增加多 IP 入口,更快更安全。