环境
我已经在AWS经典负载均衡器上设置了代理协议支持,如图所示,它将流量重定向到后端nginx(使用ModSecurity配置)实例。
一切都很好,我可以从开放的互联网上访问我的网站。
现在,由于我的nginx配置是在AWS用户数据中完成的,我想在实例开始提供流量之前进行一些检查,这可以通过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?也许是其他语言的东西?
您可以使用--haproxy-protocol
curl选项,该选项将额外的代理协议信息添加到请求中。
curl --haproxy-protocol localhost
因此:
curl -ks "https://localhost/foo?username=1'%20or%20'1'%20=%20'"
代理协议在流式传输任何之前附加一行纯文本
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 80rnGET /test/abcrnrn" | 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 8080rnGET /test/abcrnrn" | openssl s_client -connect localhost:8080
它仍然会出错
web_1 | 2017/10/27 06:37:27 [error] 5#5: *1 broken header: ",(�� @_5���_'���/��ߗ
这是因为客户端尝试先进行握手,而不是先发送代理协议,然后与握手
所以你可能的解决方案是
- 终止LB上的SSL,然后使用proxy_procol在nginx上处理http,并使用我发布的nc命令选项
- 添加一个
listen 127.0.0.1:<randomlargeport>
并使用它执行测试。这仍然是安全的,因为您只监听localhost - 添加另一个SSL端口并使用
listen 127.0.0.1:443 ssl
和listen <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.1rnHost: api.example.comrnUser-Agent: curl7.0rnAccept: */*rnrn')
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)