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

推荐订阅源

D
DataBreaches.Net
N
Netflix TechBlog - Medium
P
Proofpoint News Feed
D
Docker
J
Java Code Geeks
L
LangChain Blog
Microsoft Security Blog
Microsoft Security Blog
The GitHub Blog
The GitHub Blog
I
InfoQ
Stack Overflow Blog
Stack Overflow Blog
云风的 BLOG
云风的 BLOG
Engineering at Meta
Engineering at Meta
MongoDB | Blog
MongoDB | Blog
月光博客
月光博客
T
Tailwind CSS Blog
M
MIT News - Artificial intelligence
Blog — PlanetScale
Blog — PlanetScale
Google DeepMind News
Google DeepMind News
腾讯CDC
罗磊的独立博客
U
Unit 42
爱范儿
爱范儿
Vercel News
Vercel News
MyScale Blog
MyScale Blog

Dallas Lu

一些没有意义的事情 博客程序的一次大重构 OpenWRT 使用 udp2raw 对抗 WireGuard 阻断 如何证明你是原创作者 Nginx 泛域名配置的隐患与对策 WISeID S/MIME 证书 V2EX 刑满释放记 使用 Radicale 在 Ubuntu 24.04 中搭建 vCards CardDav 服务 邮件服务的域名成功从 SURBL 黑名单移除 The Domain of Mail successfully removed from SURBL blacklist 网站多语言的设计细节 Website Multilingual Design Details 网站评论系统的目前进展和展望 Current Progress and Outlook of the Website Comment System 在公网使用 iptables 转发端口时保留客户端 IP 邮件投递平台 Postal 的使用经验 Experience with Using Postal, the Mail Delivery Platform 自建 Postal 完美替代 SendGrid Build Your Own SendGrid: Postal SMTP Server 互联网在崩塌吗,然后呢 Is the Internet collapsing and then what 在 SvelteKit 应用中使用 JSON-LD Using JSON-LD in SvelteKit Applications 网页的打印样式应该怎么写 How to write print styles for web pages “茴字的四种写法”之 IP 与域名 Trivia: Special ways of writing IP and domain names 怎么伪造 Git 提交的时区 How to fake the timezone of a git commit 供大众交流的论坛和其它替代产品还是不好用
Retaining Client IP with iptables Port Forwarding on Publ...
Dallas Lu · 2024-07-12 · via Dallas Lu

Previously, I used an idle IP to set up port forwarding to hide my Forgejo server’s IP, which worked excellently. However, when I recently set up my email service, I discovered that using iptables for port forwarding over the public network does not allow the application to correctly obtain the client’s IP. This was not an issue for informal external applications. However, since my email service uses Greylist to filter spam, the sender’s IP is an important parameter. Therefore, I need to obtain the actual client IP.

ClientProxyServer

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

Forwarding at the Gateway

Forwarding a port from the gateway to a server within the internal network is very simple and can be done with a single command:

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

SubnetClientProxyServer3.3.3.3->10.0.0.210.0.0.2>3.3.3.33.3.3.3->1.1.1.11.1.1.1->3.3.3.310.0.0.210.0.0.11.1.1.13.3.3.3

Mermaid source
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

In the internal network server application, the correct client IP can be directly obtained. Whether using tcpdump or netstat, you can see that the IP from the internet has established a connection with the local machine.

Public Network Forwarding

Similarly, you need to specify the forwarding target:

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

So far, this forwarding does not work correctly. When 3.3.3.3 from the internet communicates with 1.1.1.1:80, the packet is forwarded to 2.2.2.2:80. When the web server returns the handshake message, its packet should follow its own routing rules and is returned to 3.3.3.3 via 2.2.2.2. This connection cannot be established.

ServerProxyClient3.3.3.3>2.2.2.23.3.3.3->1.1.1.12.2.2.2->3.3.3.32.2.2.21.1.1.13.3.3.3

Mermaid source
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

Therefore, we need to change the source of the packet on 1.1.1.1 to 1.1.1.1 to forward the response from 2.2.2.2, ensuring normal communication and connection with 3.3.3.3.

ServerProxyClient1.1.1.1>2.2.2.22.2.2.2>1.1.1.13.3.3.3->1.1.1.11.1.1.1->3.3.3.32.2.2.21.1.1.13.3.3.3

Mermaid source
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

using SNAT:

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

Using restricted MASQUERADE

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

Using broad MASQUERADE

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

This also makes 2.2.2.2 always think that 1.1.1.1 is communicating with it, completely unaware of the existence of 3.3.3.3.

Retaining Client IP with Public Network Forwarding

If a packet sent from 1.1.1.1 to 2.2.2.2 has the source IP as the original client IP, say 3.3.3.3, and the destination as 2.2.2.2. When 2.2.2.2 receives the data, if there are some filtering rules, it may not respond because the route from 2.2.2.2 to 3.3.3.3 is different from where the packet came from.

If we disable these filtering rules, 2.2.2.2 may be more vulnerable to attacks, as it becomes hard to differentiate. If we configure an extra IP on 2.2.2.2 specifically to accept forwarding from 1.1.1.1, it would be wasteful. Changing 2.2.2.2’s routing to make all its traffic go through 1.1.1.1 would make it unable to handle direct requests from the internet normally.

Establishing a Dedicated Tunnel

Some proxy implementations, such as Haproxy or Transport Proxy, require 2.2.2.2 to have application-level support. For example, in nginx, you need to explicitly specify proxy_protocol or transport. If the application does not support it, it cannot be used.

Moreover, this scenario only involves direct connection between two nodes, without needing load balancing or high availability. Using iptables is simpler than using proxy software.

Therefore, a better approach is to establish a dedicated tunnel between 1.1.1.1 and 2.2.2.2. There are many ways to establish a tunnel. Similarly, there are many other choices for establishing a tunnel, such as OpenVPN / WireGuard. Given the requirements of this scenario, I chose the simpler GRE.

GRE

Establish the tunnel on 1.1.1.1 and configure the IP address.

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

Forward the local public network traffic to the other end of the tunnel.

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

Establish the tunnel on 2.2.2.2 and configure the IP address.

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

There is no need to disable rp_filter. Add the routing table:

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

Set routing table rules and routing information to let the traffic at this tunnel endpoint return to its origin.

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

ServerProxyClient2.2.2.21.1.1.13.3.3.3->10.0.0.210.0.0.2->3.3.3.3GRE3.3.3.3->1.1.1.11.1.1.1->3.3.3.310.0.0.210.0.0.13.3.3.3

Mermaid source
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

Keep alive

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

Conclusion

Using iptables port forwarding and GRE tunnels, you can easily hide the real IP address while still obtaining the actual client IP. This process does not require installing additional software and is applicable to various applications without needing further adaptation. It’s an excellent solution for using an idle VPS as a front-end server, fully utilizing a VPS with a good connection to provide multiple IP entry points for your application, making it faster and more secure.