验证 nginx 是否在本地使用代理协议正常工作

Verify if nginx is working correctly with Proxy Protocol locally

环境

我已经在 AWS 经典负载均衡器上设置了代理协议支持,如图所示 here which redirects traffic to backend nginx (configured with ModSecurity) 个实例。

一切正常,我可以从开放的互联网访问我的网站。

现在,由于我的 nginx 配置是在 AWS User Data 中完成的,我想在实例开始服务流量之前进行一些检查,这可通过 AWS 生命周期挂钩实现。

问题

在启用代理协议之前,我曾经检查过我的nginx实例是否健康,ModSecurity通过检查来自这个命令的403响应来工作

$ curl -ks "https://localhost/foo?username=1'%20or%20'1'%20=%20'"

启用代理协议后,我无法再执行此操作,因为命令失败并出现以下错误,这是预期的 link

# curl -k https://localhost -v
* About to connect() to localhost port 443 (#0)
*   Trying ::1...
* Connected to localhost (::1) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* NSS error -5938 (PR_END_OF_FILE_ERROR)
* Encountered end of file
* Closing connection 0
curl: (35) Encountered end of file

# cat /var/logs/nginx/error.log
2017/10/26 07:53:08 [error] 45#45: *5348 broken header: "���4"�U�8ۭ�u��%d�z��mRN�[e��<�,�
�+̩�    �0��/̨��98k�̪32g�5=�/<
" while reading PROXY protocol, client: 172.17.0.1, server: 0.0.0.0:443

除了 curl 之外,我还有哪些其他选项可以以编程方式检查 nginx?也许是其他语言?

代理协议在流式传输任何内容之前附加一个纯文本行

PROXY TCP4 127.0.0.1 127.0.0.1 0 8080

以上是一个例子,但这是最先发生的事情。现在,如果我有一个 NGINX 使用 proxy_protocol 监听 SSL 和 http,那么它希望首先看到这一行,然后是任何其他东西

所以如果

$ curl localhost:81
curl: (52) Empty reply from server

并且在 nginx 日志中

web_1  | 2017/10/27 06:35:15 [error] 5#5: *2 broken header: "GET / HTTP/1.1

如果我这样做

$ printf "PROXY TCP4 127.0.0.1 127.0.0.1 0 80\r\nGET /test/abc\r\n\r\n" | nc localhost 81
You can reach API /test/abc and args_given = ,

有效。因为我能够发送它接受的代理协议

现在如果我使用下面的 SSL 的话

printf "PROXY TCP4 127.0.0.1 127.0.0.1 0 8080\r\nGET /test/abc\r\n\r\n" | openssl s_client -connect localhost:8080

还是会报错

web_1  | 2017/10/27 06:37:27 [error] 5#5: *1 broken header: ",(��   @_5���_'���/��ߗ

那是因为客户端尝试先握手而不是先发送代理协议再握手

所以你可能的解决方案是

  1. 在 LB 上终止 SSL,然后在 nginx 上使用 proxy_protocol 处理 http 并使用我发布的 nc 命令选项
  2. 添加 listen 127.0.0.1:<randomlargeport> 并使用相同的方法执行测试。这仍然是安全的,因为你只听本地主机
  3. 添加另一个 SSL 端口并使用 listen 127.0.0.1:443 ssllisten <private_ipv4>:443 ssl proxy_protocol

所有解决方案都是按照我的选择顺序排列的,你可以自己选择

感谢Tarun 的详细解释。我在团队内部进行了讨论,最终在端口 80 上创建了另一个 nginx 虚拟主机,并使用它来检查 ModSecurity,如下所示。

curl "http://localhost/foo?username=1'%20or%20'1'%20=%20'"`

不幸的是 bash 版本在我的情况下不起作用,所以我写了 python3 代码:

#!/usr/bin/env python3

import socket
import sys

def check_status(host, port):
    '''Check app status, return True if ok'''
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.settimeout(3)
        s.connect((host, port))
        s.sendall(b'GET /status HTTP/1.1\r\nHost: api.example.com\r\nUser-Agent: curl7.0\r\nAccept: */*\r\n\r\n')
        data = s.recv(1024)
        if data.decode().endswith('OK'):
            return True
        else:
            return False
try:
    status = check_status('127.0.0.1', 80)
except:
    status = False
if status:
    sys.exit(0)
else:
    sys.exit(1)

您可以使用 --haproxy-protocol curl 选项,它将额外的代理协议信息添加到请求中。

curl --haproxy-protocol localhost

所以:

curl -ks "https://localhost/foo?username=1'%20or%20'1'%20=%20'"