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

时间:2017-10-26 08:03:59

标签: nginx proxy-protocol

环境

我已在AWS经典负载均衡器上设置了代理协议支持,如here所示,它将流量重定向到后端nginx(配置了ModSecurity)个实例。

一切都很好,我可以从开放的互联网上访问我的网站。

现在,由于我的nginx配置是在AWS User Data中完成的,我想在实例开始提供流量之前做一些检查,这可以通过AWS Lifecycle hook实现。

问题

在启用代理协议之前,我曾经检查过我的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?也许是其他语言的东西?

4 个答案:

答案 0 :(得分:4)

代理协议在流式传输之前附加纯文本行

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,则现在使用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,然后使用proxy_protocol在nginx上处理http,并使用我发布的nc命令选项
  2. 添加listen 127.0.0.1:<randomlargeport>并使用相同的方式执行测试。当您正在收听localhost时,这仍然是安全的
  3. 添加其他SSL端口并使用listen 127.0.0.1:443 ssllisten <private_ipv4>:443 ssl proxy_protocol
  4. 根据我的选择,所有解决方案都按优先顺序排列,您可以自行选择

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

不幸的是,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)

答案 3 :(得分:0)

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

curl --haproxy-protocol localhost

所以:

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