Убедитесь, что nginx работает правильно с прокси-протоколом локально
Среда
Я настроил поддержку протокола Proxy на классическом балансировщике нагрузки AWS, как показано здесь, который перенаправляет трафик на внутренние экземпляры nginx (сконфигурированные с ModSecurity).
Все отлично работает, и я могу попасть на мои сайты из открытого интернета.
Теперь, поскольку моя конфигурация nginx выполнена в пользовательских данных AWS, я хочу выполнить некоторые проверки, прежде чем экземпляр начнет обслуживать трафик, который достижим через перехватчики жизненного цикла AWS.
проблема
Перед включением прокси-протокола я проверял работоспособность моего экземпляра nginx, и ModSecurity работает, проверяя 403
ответ от этой команды
$ curl -ks "https://localhost/foo?username=1'%20or%20'1'%20=%20'"
После включения Proxy Protocol я больше не могу этого делать, так как команда завершается с ошибкой ниже, которая ожидается по этой ссылке.
# 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
Какие еще варианты у меня есть, чтобы программно проверить nginx, кроме curl? Может быть, что-то на другом языке?
3 ответа
Спасибо Тарун за подробное объяснение. Я обсуждал в группе и закончил тем, что создал еще один виртуальный хост nginx на порту 80 и использовал его для проверки ModSecurity, как показано ниже.
curl "http://localhost/foo?username=1'%20or%20'1'%20=%20'"`
Вы можете использовать --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 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���_'���/��ߗ
Это связано с тем, что клиент пытается сначала выполнить рукопожатие, а не отправлять сначала прокси-протокол, а затем рукопожатие.
Таким образом, вы возможные решения
- Завершите SSL на LB, а затем обработайте http на nginx с proxy_protocol и используйте опцию команды nc, которую я разместил
- Добавить
listen 127.0.0.1:<randomlargeport>
и выполнить свой тест, используя то же самое. Это все еще безопасно, так как вы слушаете только localhost - Добавьте другой порт SSL и используйте
listen 127.0.0.1:443 ssl
а такжеlisten <private_ipv4>:443 ssl proxy_protocol
Все решения в порядке приоритета, на мой выбор, вы можете сделать свой выбор
К сожалению, в моем случае 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)