本地主机的 iptables 和 Nginx 反向代理

本地主机的 iptables 和 Nginx 反向代理

概括

我使用 Nginx 作为反向代理,特别是它的功能,proxy_bind以便将真实的客户端 IP 发送到后端。我遵循本文档来自 nginx.com
当 HTTP 服务器托管在另一台机器上或直接托管在反向代理上时,它都能很好地运行,除非在第二种情况下 HTTP 服务器在本地主机上监听。

=> 我无法使这个配置起作用:

# /etc/nginx/conf.d/nginx-revprox-test.conf
server {
    listen 80;
    location / {
        proxy_pass  http://127.0.0.1:8080;
        proxy_bind  $remote_addr transparent;
    }
}

细节

假设:

  • IP 地址:
    • 反向代理/本地后端:192.168.1.90 (eth0)&172.17.0.1 (eth1)
    • 外部后端:172.17.0.2
    • 客户:192.168.1.91
  • 端口:
    • 前端:(80无 TLS)
    • 后端:8080
  • 案例一:HTTP 服务器托管在外部后端(http://172.17.0.2:8080)。

    # /etc/nginx/conf.d/nginx-revprox-test.conf
    server {
        listen 192.168.1.90:80;
        location / {
            proxy_pass  http://172.17.0.2:8080;
            proxy_bind  $remote_addr transparent;
        }
    }
    

    手动的说我们必须配置 iptables:

    iptables -t mangle -A PREROUTING -p tcp -s 172.17.0.2 --sport 8080 -j MARK --set-xmark 0x1/0xffffffff
    ip rule add fwmark 1 lookup 100
    ip route add local 0.0.0.0/0 dev lo table 100
    

    => 它运行良好。

  • 案例二:HTTP 服务器与反向代理托管在同一台计算机上,并侦听eth1http://172.17.0.1:8080)。

    # /etc/nginx/conf.d/nginx-revprox-test.conf
    server {
        listen 192.168.1.90:80;
        location / {
            proxy_pass  http://172.17.0.1:8080;
            proxy_bind  $remote_addr transparent;
        }
    }
    

    根据这个答案此图, 仅有的输出路由后适用于本地生成的数据包。我们需要更改 iptables 规则:

    iptables -t mangle -A OUTPUT -p tcp -s 172.17.0.2 --sport 8080 -j MARK --set-xmark 0x1/0xffffffff
    

    => 它也有效。

  • 案例三:HTTP 服务器托管在本地,但监听lohttp://127.0.0.1:8080)。

    # /etc/nginx/conf.d/nginx-revprox-test.conf
    server {
        listen 192.168.1.90:80;
        location / {
            proxy_pass  http://127.0.0.1:8080;
            proxy_bind  $remote_addr transparent;
        }
    }
    

    =>[!]我无法让它工作,即使有一个输出iptables 中的规则。
    curl http://192.168.1.90:80在客户端上不起作用(它只能在服务器上起作用,但这不是重点)。

我遗漏了一些有关 iptables 的信息。你能帮助我吗?

答案1

对于情况 3,您可以避免 Netfilter 标记 - 必要的过滤也直接由 提供支持ip rule。以下命令会将所有流量从后端服务器路由到 nginx 前端:

# add a new routing table that routes all packets back to this server
ip route add local 0.0.0.0/0 dev lo table 100

# add a rule for choosing that routing table:
ip rule add from 127.0.0.1/32 ipproto 6 sport 8080 iif lo lookup 100
# ^ individual parts:
# from 127.0.0.1/32 - select just one loopback IP, the one with nginx on it
# ipproto 6  - select just TCP
# sport 8080 - select just traffic from the backend HTTP server port
# iif lo     - select just traffic originating from this machine
# lookup 100 - route these matching packets via routing table 100

我在以下方法的帮助下反复试验,找到了ip rule手册页和数据包流图(12)。


您也可以使用 Netfilter 标记,但我建议使用上述解决方案,因为它可以避免启用route_localnet这可能会导致安全问题)。为了完整起见,下面是使其工作的方法:

  1. 在本地接口上启用路由(按照https://unix.stackexchange.com/a/158256/72419):
    sysctl -w net.ipv4.conf.all.route_localnet=0
    
  2. 配置路由表:
    ip rule add fwmark 1 lookup 100
    ip route add local 0.0.0.0/0 dev lo table 100
    
  3. 让 Netfilter 在 OUTPUT 链中标记数据包:
    iptables -t mangle -A OUTPUT -p tcp -s 127.0.0.1 --sport 8080 -j MARK --set-xmark 0x1/0xffffffff
    
    OUTPUT 链至关重要 - 本地生成的数据包不会经过 PREROUTING 表。

不过,您也许能够避免与之相关的安全问题route_localnet-https://github.com/yrutschle/sslh/blob/master/doc/tproxy.md#transparent-proxy-to-one-host修复了这个问题

iptables -t raw -A PREROUTING ! -i lo -d 127.0.0.0/8 -j DROP
iptables -t mangle -A POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP

相关内容