类似地这Cloudflare 博客文章中,我尝试使用透明套接字设置 Nginx(使用IP_TRANSPARENT
套接字选项)。我想通过这种方式来实现一个可以有效绑定到所有端口的反向 TCP 代理。
这IP_TRANSPARENT
选项是不受原生支持由 Nginx 监听,所以我尝试使用Systemd 套接字单元然后使用NGINX
环境变量。
套接字单元如下(不重要的行已被删除):
[Socket]
ListenStream=127.0.0.1:1234
Transparent=true
[Install]
WantedBy=sockets.target
我的Nginx服务单元如下(不重要的行已删除):
[Service]
# Configure Nginx to use the socket inherited from Systemd
Environment=NGINX=3;
ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf
我的nginx.conf
如下(不重要的行已被删除,upstream
是我想要代理到的上游主机的主机名):
stream {
server {
proxy_pass upstream:443
listen 127.0.0.1:1234;
}
}
Nginx 在 GCP 计算实例上运行内部直通负载均衡器IP 地址为 10.11.12.13,因此我添加了以下 iptables 规则将流量重定向到透明套接字:
iptables -t mangle -I PREROUTING -d 10.11.12.13/32 -p tcp -j TPROXY --on-port=1234 --on-ip=127.0.0.1
正如所描述的这里,GCP 为负载均衡器安装了本地路由,因此无需自己执行此操作。换句话说,以下命令返回RTNETLINK answers: File exists
:
ip route add local 10.11.12.13/32 dev lo src 127.0.0.1
但是,以下 curl 命令挂起:
curl https://10.11.12.13
从 curl 的角度来看,TCP 连接已正确建立,但是在跟踪 Nginx 工作进程时,它似乎挂在系统accept4
调用上。
tcpdump
从 Nginx 正在运行的实例运行显示以下内容:
15.59.51.564902 IP [REDACTED].50028 > 10.11.12.13.443: Flags [S], seq 3519046460, win 65535, options [mss 1320,nop,wscale 6,nop,nop,TS val 3905128064 ecr 0,sackOK,eol], length 0
15.59.51.564973 IP 10.11.12.13.443 > [REDACTED].50028: Flags [S.], seq 2512605282, ack 3519046461, win 28400, options [mss 1420,nop,nop,sackOK,nop,wscale 7], length 0
15.59.51.601898 IP [REDACTED].50028 > 10.11.12.13.443: Flags [.], ack 1, win 4096, length 0
15.59.51.601898 IP [REDACTED].50028 > 10.11.12.13.443: Flags [P.], seq 1:370, ack 1, win 4096, length 369
15.59.51.601898 IP 10.11.12.13.443 > [REDACTED].50028: Flags [.], ack 370, win 231, length 0
据我了解,这意味着客户端正在发送数据,服务器正在确认数据,但随后客户端不再发送任何数据,大概是因为它期待服务器的响应,但服务器并未响应。因此,我怀疑 Nginx 只是忽略了客户端发送的数据。
我希望我已经提供了足够的背景信息来理解这个问题。我不太确定接下来该怎么做,或者我所尝试做的事情是否存在根本性错误。任何帮助都将不胜感激。
答案1
我找到了解决方案这里这需要在 Nginx 服务单元上设置以下选项:
[Service]
NonBlocking=true
据推测,Nginx 假定在创建监听套接字时设置了此选项,因此如果 Systemd 传递一个没有设置此选项的套接字,则无法读取数据。