为什么在删除后,iptables nat prerouting规则仍然继续重定向?

huangapple go评论87阅读模式
英文:

Why would an iptables nat prerouting rule continue to redirect even after removal

问题

尽可能简单地解释一下,我有两个运行的服务器。一个是运行在80端口上的Apache服务器,另一个是我用Golang创建的运行在29999端口上的reCAPTCHA服务器守护程序。我有一个应用程序在监控主机上的所有事件,如果有任何一种“应该被屏蔽”的流量到达服务器,它将通过ipset add <set> <ip>命令屏蔽你的IP,并在80和443端口上添加一个NAT预路由规则,将目标IP重定向到29999端口。以1.1.1.1为例:

-A RECAPTCHA-PREROUTE -s 1.1.1.1/32 -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 29999

-A RECAPTCHA-PREROUTE -s 1.1.1.1/32 -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 29999

稍微快进一点:我试图访问80端口(我被屏蔽了)。我被正确地重定向到我的守护程序。我解决了reCAPTCHA,reCAPTCHA正确地向守护程序发起请求,守护程序正确地移除了该IP的预路由防火墙规则,并从ipset中移除了该IP。在“解除屏蔽”之后,Golang守护程序执行http.Redirect(w, req, "/", 200),意图将你重定向到https://example.com/,但实际上,它将我直接发送回运行在29999端口上的reCAPTCHA守护程序,尽管我可以在另一个浏览器中打开(例如Edge),并且它会直接将我带到预期的目标页面。考虑到这一点,如果我在Edge上进行初始请求,其行为将与其他浏览器相同。我和我的一个同事相信这是浏览器中一些奇怪的缓存问题,我们没有正确处理它,但我们已经尽力找到了互联网上大部分可能的解决方案,例如提供正确的缓存头,并在HTML文档中添加meta标签。

例如:

Golang

w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")

w.Header().Set("Pragma", "no-cache")

w.Header().Set("Expires", "0")

HTML

<meta http-equiv='cache-control' content='no-cache'>

<meta http-equiv='expires' content='0'>

<meta http-equiv='pragma' content='no-cache'>

还有一件有趣的事情要注意,当你在解除屏蔽/防火墙移除后被错误地重定向到reCAPTCHA守护程序页面时,如果你在浏览器界面上按下ctrl+r大约20-50次,最终你将不再看到reCAPTCHA页面,而是正确地访问到预期的服务器端点。另外,如果你清除浏览器缓存,它也会立即将你带到正确的页面。(这就是为什么我们认为这是一种隐晦的缓存问题的原因)。

英文:

To give as basic of a rundown as possible, I have 2 servers running. One server is apache running on 80, the other is a recaptcha server daemon I've created in golang running over 29999. I have an application running that monitors all events coming across the host, and if any kind of "shun worthy" traffic hits the box. It will shun your ip via an ipset add &lt;set&gt; &lt;ip&gt;, and it will add a nat prerouting rules on 80 and 443 with the target IP to redirect them to port 29999. Using 1.1.1.1 for example

-A RECAPTCHA-PREROUTE -s 1.1.1.1/32 -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 29999

-A RECAPTCHA-PREROUTE -s 1.1.1.1/32 -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 29999

Fast forward a bit: Im trying to hit port 80 (im shunned). I get redirected correctly to my daemon. I solve the recaptcha, the recaptcha properly dials out to the daemon, the daemon properly removes the prerouting firewall rules for the IP, and removes them from the ipset. After the "unshun" the Golang daemon does a http.Redirect(w, req, &quot;/&quot;, 200) with the intention of sending you to https://example.com/ , BUT instead, it sends me right back to the recaptcha daemon running on 29999, even though I can open a separate browser(edge gag), and it will take me straight to the intended endpoint on the separate browser. With that in mind, if I would have run the initial request on edge, its behavior would reflect the other browsers. A colleague of mine and I are convinced that its some kind of weird caching issue in the browser that we are missing proper handling for, but we have since then exhausted most of the possible solutions that we could find on the internet, such as proper caching headers being served, and adding meta tags to the html document.

ex):

Golang

w.Header().Set(&quot;Cache-Control&quot;, &quot;no-cache, no-store, must-revalidate&quot;)

w.Header().Set(&quot;Pragma&quot;, &quot;no-cache&quot;)

w.Header().Set(&quot;Expires&quot;, &quot;0&quot;)

HTML

&lt;meta http-equiv=&#39;cache-control&#39; content=&#39;no-cache&#39;&gt;

&lt;meta http-equiv=&#39;expires&#39; content=&#39;0&#39;&gt;

&lt;meta http-equiv=&#39;pragma&#39; content=&#39;no-cache&#39;&gt;

One more interesting thing to note is, after you get redirected incorrectly to the recaptcha daemon page after the unshun/firewall removal. If you push ctrl+r on the browser UI about ~20-50 times. You will eventually stop getting the recaptcha page, and it will properly hit the intended server endpoint. Also if you clear your browsers cache, it will immediately take you to the correct page as well. (which is why we feel this is some obscure caching issue.

答案1

得分: 0

感谢在评论中回复的JimB,我找到了解决这个问题的方法。他正确地指出我没有正确关闭连接。我所需要做的就是添加一个关闭头部w.Header().Set("Connection", "close")和一个defer req.Body.Close()。其中req是处理程序的*http.Request

英文:

Thanks to JimB who responded in the comments, I found a solution to the issue. He was correct in stating that I hadn't closed the connection out properly.
All I had to do was add a close header w.Header().Set(&quot;Connection&quot;, &quot;close&quot;) and a defer req.Body.Close(). req being the handlers *http.Request

huangapple
  • 本文由 发表于 2022年7月30日 04:01:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/73170467.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定