Preserving Original Client IP Addresses with OpenWrt Port Forwarding: A Fail2Ban Savior

If you want to read the Chinese version, move down to the Chinese Part, 本文包含中文版本,请移到后面的中文版本。

I run a self-hosted environment at home, including an Immich photo server and an SSH server, both happily residing on my local network (LAN). To access these services remotely, I configured port forwarding on my OpenWrt router. Initially, everything seemed to function flawlessly – I could access my services from anywhere.

However, a peculiar issue soon surfaced: my SSH server began refusing connections. A dive into the auth.log and Nginx access logs revealed something unexpected. All incoming internet traffic, whether to SSH or Immich, appeared to originate from 192.168.x.x – the LAN IP address of my OpenWrt router itself! This became a critical problem because persistent SSH scanning attempts from the internet were, understandably, being flagged by Fail2Ban. The consequence? Fail2Ban banned the “offending” IP, which was my router, effectively severing access to all my forwarded services for everyone, including myself.

My troubleshooting journey involved extensive Googling and even consulting AI assistants, but these efforts proved fruitless. One AI suggestion was to install Nginx on the OpenWrt router itself to act as a reverse proxy. While a reverse proxy can preserve client IPs (using headers like X-Forwarded-For), installing a full-fledged Nginx instance on a resource-constrained router solely for this felt like overkill and not the most direct solution.

The breakthrough came from an unexpected source: an iStoreOS issue tracker. (For the uninitiated, iStoreOS is a popular OpenWrt fork). While the issue thread didn’t contain a step-by-step solution, the original poster mentioned resolving a similar problem by tweaking NAT rules to prevent source address translation. Given the shared lineage between OpenWrt and iStoreOS, I suspected this approach might be applicable.

Armed with this clue, I navigated to my OpenWrt LuCI interface: Network -> Firewall -> NAT Rules.

Here, I added a new rule with the following key parameters:

  • Action: ACCEPT
  • Crucially: I enabled an option typically labeled something like “Disable Source NAT” or “Disable Address Rewrite” (the exact wording might vary slightly depending on your OpenWrt version/theme, but the intent is to prevent the router from rewriting the source IP address of the incoming packets).

(Self-correction: More accurately, the typical OpenWrt port forward is a DNAT rule. The issue is that an overly broad SNAT/Masquerade rule might be applying to this forwarded traffic. The key is to ensure that for these specific port-forwarded connections, SNAT is not performed, allowing the original source IP to pass through to the internal server. The “Disable Source NAT” option is often available directly within the port forward (DNAT) rule definition itself, or a specific “NO SNAT” rule needs to be crafted).

The result? Success!

Immediately after applying and saving this NAT rule, my server logs began showing the actual public IP addresses of external visitors. Most importantly, Fail2Ban could now accurately identify and ban malicious external IPs without inadvertently blacklisting my entire home network via the router’s IP.

Why this works (The Geeky Bit):

Standard port forwarding involves Destination NAT (DNAT), where the router changes the destination IP of an incoming packet (from its public WAN IP to your internal server’s LAN IP). However, many default OpenWrt setups also apply Source NAT (SNAT) or Masquerading for traffic passing through it, especially from LAN to WAN. In some configurations, this SNAT can also incorrectly get applied to WAN-to-LAN forwarded traffic, replacing the original client’s IP with the router’s LAN IP before the packet reaches your internal server.

The “Disable Source NAT” or “Disable Address Rewrite” option for the relevant traffic flow instructs OpenWrt’s netfilter firewall to not perform this source IP alteration for packets destined for your forwarded ports. This allows your internal services to see the true originating IP, essential for accurate logging, geolocation, and security tools like Fail2Ban.

If you’re facing a similar issue where your internal services behind an OpenWrt router only see the router’s IP for external connections, diving into your NAT rules and looking for an option to prevent source address translation on your port forwards might just be the elegant solution you need.


Following is the Chinese part(中文版本)

OpenWrt 端口转发:保留原始客户端 IP,解决 Fail2Ban 困境

我在家中搭建了一套自托管服务环境,包括一个 Immich 照片服务器和一个 SSH 服务器,它们都安稳地运行在我的本地网络(LAN)中。为了能够远程访问这些服务,我在我的 OpenWrt 路由器上配置了端口转发。起初,一切似乎都完美无缺——我可以从任何地方访问我的服务。

然而,一个奇怪的问题很快浮出水面:我的 SSH 服务器开始拒绝连接。深入查看 auth.log 和 Nginx 的访问日志后,我发现了一些出乎意料的情况。所有来自互联网的访问流量,无论是到 SSH还是 Immich,都显示其源 IP 地址为 192.168.x.x——这正是我 OpenWrt 路由器的局域网 IP 地址!这是一个严重的问题,因为来自互联网的持续性 SSH 扫描尝试,理所当然地被 Fail2Ban 标记了。结果呢?Fail2Ban 封禁了这个“违规”的 IP,也就是我的路由器,从而有效地切断了所有人(包括我自己)对我所有转发服务的访问。

我的故障排除过程包括了大量的谷歌搜索,甚至咨询了 AI 助手,但这些努力都未能奏效。一个 AI 给出的建议是在 OpenWrt 路由器上安装 Nginx 作为反向代理。虽然反向代理确实可以通过 X-Forwarded-For 之类的头部信息来保留客户端 IP,但在资源受限的路由器上仅仅为此目的安装一个完整的 Nginx 实例,感觉有些小题大做,也不是最直接的解决方案。

转机来自一个意想不到的地方:一个 iStoreOS 的问题追踪器。(对于不熟悉的人来说,iStoreOS 是一个流行的 OpenWrt 分支)。虽然那个问题帖子里没有包含手把手的解决方案,但原帖主提到他通过调整 NAT 规则,阻止了源地址转换,从而解决了类似问题。考虑到 OpenWrt 和 iStoreOS 之间的同源关系,我猜测这种方法可能也适用。

带着这条线索,我进入了我的 OpenWrt LuCI 管理界面:网络 (Network) -> 防火墙 (Firewall) -> NAT 规则 (NAT Rules)

在这里,我添加了一条新的规则,关键参数如下:

  • 动作 (Action): ACCEPT
  • 至关重要的一点:我启用了通常标记为“禁用源 NAT (Disable Source NAT)”或“禁用地址重写 (Disable Address Rewrite)”之类的选项(具体措辞可能因您的 OpenWrt 版本/主题而略有不同,但其目的是阻止路由器重写传入数据包的源 IP 地址)。

(自我修正:更准确地说,典型的 OpenWrt 端口转发是一条 DNAT 规则。问题在于,一个过于宽泛的 SNAT/Masquerade 规则可能也应用到了这些转发流量上。关键在于确保对于这些特定的端口转发连接,不执行 SNAT,从而允许原始源 IP 传递到内部服务器。“禁用源 NAT”选项通常可以直接在端口转发(DNAT)规则定义中找到,或者需要创建一个特定的“NO SNAT”规则。)

结果呢?成功了!

在应用并保存这条 NAT 规则后,我的服务器日志立即开始显示外部访问者真实的公网 IP 地址。最重要的是,Fail2Ban 现在可以准确识别并封禁恶意的外部 IP,而不会通过路由器的 IP 无意中将我的整个家庭网络列入黑名单。

为何有效(技术解读):

标准的端口转发涉及目标网络地址转换 (DNAT),路由器会将传入数据包的目标 IP(从其公网 WAN IP)更改为您内部服务器的 LAN IP。然而,许多默认的 OpenWrt 设置也会对通过它的流量(尤其是从 LAN 到 WAN 的流量)应用源网络地址转换 (SNAT) 或 IP 伪装 (Masquerading)。在某些配置下,这种 SNAT 也可能错误地应用于 WAN 到 LAN 的转发流量,在数据包到达您的内部服务器之前,将原始客户端的 IP 替换为路由器的 LAN IP。

针对相关流量的“禁用源 NAT”或“禁用地址重写”选项,会指示 OpenWrt 的 netfilter 防火墙不要对发往您转发端口的数据包执行这种源 IP 地址变更。这使得您的内部服务能够看到真实的原始 IP,这对于准确的日志记录、地理位置定位以及像 Fail2Ban 这样的安全工具至关重要。

如果您也遇到类似的问题,即 OpenWrt 路由器后的内部服务对于外部连接只看得到路由器的 IP,那么深入研究您的 NAT 规则,寻找一个选项来阻止对端口转发流量进行源地址转换,或许正是您需要的那个优雅的解决方案。


#OpenWrt Fail2Ban bans router IP

#OpenWrt server sees router IP

#OpenWrt logs show router IP

#Fix OpenWrt port forward IP

#SSH server behind OpenWrt shows router IP

#Nginx behind OpenWrt shows router IP

#OpenWrt hide client IP