5

环境

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

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

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

问题

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

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

启用代理协议后,我不能再这样做了,因为命令失败并出现以下错误,这是根据这个链接预期的。

# 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

4 回答 4

8

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

curl --haproxy-protocol localhost

所以:

curl -ks "https://localhost/foo?username=1'%20or%20'1'%20=%20'"
于 2019-07-28T22:47:29.600 回答
7

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

PROXY TCP4 127.0.0.1 127.0.0.1 0 8080

上面是一个例子,但这是第一件事。现在,如果我有一个 NGINX 在 SSL 和 http 上同时使用,proxy_protocol那么它希望先看到这一行,然后再看到其他任何东西

所以如果这样做

$ 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,然后使用 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

所有解决方案都按照我的选择优先顺序,您可以做出自己的选择

于 2017-10-27T06:57:59.183 回答
0

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

curl "http://localhost/foo?username=1'%20or%20'1'%20=%20'"`
于 2017-10-28T07:10:59.873 回答
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)
于 2018-10-30T18:24:47.490 回答