Nginx: broken_header с proxy_protocol и ELB

Я пытаюсь настроить proxy_protocol в моей конфигурации nginx. Мой сервер находится за балансиром нагрузки AWS (ELB), и я включил прокси-протокол для обоих портов 80 и 443.

Однако, это то, что я получаю, когда попал на свой сервер:

broken header: "��/��
                                                             '���\DW�Vc�A{����
                                                                              �@��kj98���=5���[email protected]�</A
    " while reading PROXY protocol, client: 172.31.12.223, server: 0.0.0.0:443

Это прямая копия для копирования из журнала ошибок nginx - нечетные символы и все.

Вот отрезок из моей конфигурации nginx:

server {
  listen  80 proxy_protocol;
  set_real_ip_from 172.31.0.0/20; # Coming from ELB
  real_ip_header proxy_protocol;
  return  301 https://$http_host$request_uri;
}

server {
  listen      443 ssl proxy_protocol;
  server_name *.....com
  ssl_certificate      /etc/ssl/<....>;
  ssl_certificate_key  /etc/ssl/<....?;
  ssl_prefer_server_ciphers On;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4;
  ssl_session_cache shared:SSL:10m;
  add_header X-Frame-Options DENY;
  add_header X-Content-Type-Options nosniff;
  ssl_stapling on;
  ssl_stapling_verify on;


  ...

Я не могу найти помощь по этой проблеме. У других людей были проблемы с заголовком, но ошибка с плохими заголовками всегда читаема - они не выглядят так, как будто они закодированы, как и для меня.

Любые идеи?

Ответ 1

Два предложения:

  • Убедитесь, что ваш слушатель ELB настроен на использование TCP в качестве протокола, а не HTTP. У меня есть конфигурация LB, например, следующая: для Nginx с настройкой proxy_protocol:

    {
      "LoadBalancerName": "my-lb",
      "Listeners": [
         {
          "Protocol": "TCP",
          "LoadBalancerPort": 80,
          "InstanceProtocol": "TCP",
          "InstancePort": 80
        }
      ],
      "AvailabilityZones": [
        "us-east-1a",
        "us-east-1b",
        "us-east-1d",
        "us-east-1e"
      ],
      "SecurityGroups": [
         "sg-mysg"
      ]
    }
    
  • Вы упомянули, что вы включили протокол прокси в ELB, поэтому я предполагаю, что вы следовали за шагами настройки AWS. Если это так, то ELB должен правильно обработать HTTP-запрос с первой строкой, как что-то вроде PROXY TCP4 198.51.100.22 203.0.113.7 35646 80\r\n. Однако, если HTTP-запрос не входит в Nginx с линией PROXY ..., это может вызвать проблему, которую вы видите. Вы можете воспроизвести, что если вы нажмете DNS-имя EC2 непосредственно в браузере или вы сделаете ssh в экземпляр EC2 и попробуйте что-то вроде curl localhost, тогда вы должны увидеть аналогичную ошибку разбитого заголовка в журналах Nginx.

Чтобы узнать, работает ли он с правильно сформированным HTTP-запросом, вы можете использовать telnet:

    $ telnet localhost 80
    PROXY TCP4 198.51.100.22 203.0.113.7 35646 80
    GET /index.html HTTP/1.1
    Host: your-nginx-config-server_name
    Connection: Keep-Alive

Затем проверьте журналы Nginx и посмотрите, есть ли у вас такая же ошибка разбитого заголовка. Если нет, ELB, скорее всего, не отправит правильно отформатированный запрос PROXY, и я бы предложил повторно выполнить конфигурацию протокола ELB Proxy, возможно, с новым LB, чтобы убедиться, что он настроен правильно.

Ответ 2

Решение Stephen Karger, приведенное выше, является правильным, вы должны настроить, чтобы настроить ELB для поддержки прокси. Вот документы AWS для выполнения именно этого: http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/enable-proxy-protocol.html. Документы сначала немного сложны, поэтому, если вы хотите, вы можете просто перейти к шагам 3 и 4 в разделе Enable Proxy Protocol Using the AWS CLI. Это единственные необходимые шаги для включения канала прокси-сервера. Кроме того, как предложил Стивен, вы должны убедиться, что ваш ELB использует TCP вместо http или https, так как оба из них не будут корректно работать с реализацией прокси-сервера ELB. Я предлагаю переместить ваш канал сокета от общих портов, таких как 80 и 443, так что вы все равно можете поддерживать эти стандартизированные соединения с их поведением по умолчанию. Конечно, этот вызов полностью зависит от того, как выглядит ваш стек приложения.

Если это помогает, вы можете использовать пакет npm wscat для отладки ваших соединений с веб-сайтами следующим образом:

$ npm install -g wscat
$ wscat --connect 127.0.0.1

Если соединение работает в локальном, то это наверняка ваш балансировщик нагрузки. Однако, если этого не происходит, проблема с вашим хостом сокета почти определенно.

Кроме того, такой инструмент, как nmap, поможет вам открыть открытые порты. Хороший контрольный список для отладки:

npm install -g wscat
# can you connect to it from within the server?
ssh [email protected]
wscat -c 127.0.0.1:80
# can you connect to it from outside the server?
exit
wscat -c 69.69.69.69:80
# if not, is your socket port open for business?
nmap 69.69.69.69:80

Вы также можете использовать nmap с вашего сервера для обнаружения открытых портов. установить nmap на ubuntu, просто sudo apt-get install nmap. на osx, brew install nmap

Вот рабочий конфигуратор, который у меня есть, хотя на данный момент он не поддерживает ssl. В этой конфигурации у меня есть порт 80, загружающий приложение для рельсов, порт 81 подает сокет через мой локоть, а порт 82 открыт для внутренних соединений сокетов. Надеюсь, это поможет кому-то! Любой, кто использует Rails, единорог и Faye для развертывания, должен найти это полезным.:) счастливый взлом!

# sets up deployed ruby on rails server
upstream unicorn {
  server unix:/path/to/unicorn/unicorn.sock fail_timeout=0;
}

# sets up Faye socket
upstream rack_upstream {
  server 127.0.0.1:9292;
}

# sets port 80 to proxy to rails app
server {
  listen 80 default_server;
  keepalive_timeout 300;
  client_max_body_size 4G;
  root /path/to/rails/public;
  try_files $uri/index.html $uri.html $uri @unicorn;

  location @unicorn {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded_Proto $scheme;
    proxy_redirect off;
    proxy_pass http://unicorn;
    proxy_read_timeout 300s;
    proxy_send_timeout 300s;
  }

  error_page 500 502 503 504 /500.html;
  location = /500.html {
    root /path/to/rails/public;
  }
}

# open 81 to load balancers (external socket connection)
server {
  listen 81 proxy_protocol;
  server_name _;
  charset UTF-8;
  location / {
    proxy_pass http://rack_upstream;
    proxy_redirect off;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }
}

# open 82 to internal network (internal socket connections)
server {
  listen 82;
  server_name _;
  charset UTF-8;
  location / {
    proxy_pass http://rack_upstream;
    proxy_redirect off;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }
}

Ответ 3

У меня была эта ошибка и наткнулся на этот билет:

что в конечном итоге привело меня к выяснению, что в моем файле nginx.conf у меня было ненужное объявление proxy_protocol. Я удалил это, и все снова работало.

Как ни странно, все отлично работало с версией nginx 1.8.0, но когда я обновил версию nginx 1.8.1, это когда я начал видеть ошибку.

Ответ 4

У меня тоже возникла проблема с нечитаемыми заголовками, и вот причина и как я это исправил.

В моем случае Nginx правильно настроен с use-proxy-protocol=true. Он жалуется на сломанный заголовок исключительно потому, что AWS ELB вообще не добавил требуемый заголовок (например, PROXY TCP4 198.51.100.22 203.0.113.7 35646 80). Nginx видит зашифрованную полезную нагрузку HTTPS напрямую. Вот почему он печатает все нечитаемые символы.

Так почему же AWS ELB не добавил заголовок PROXY? Оказалось, что я использовал неправильные порты в командах для включения политики Proxy Protocol. Порты экземпляра следует использовать вместо 80 и 443.

ELB имеет следующее отображение портов.

80 -> 30440
443 -> 31772

Команды должны быть

aws elb set-load-balancer-policies-for-backend-server \
  --load-balancer-name a19235ee9945011e9ac720a6c9a49806 \
  --instance-port 30440 \
  --policy-names ars-ProxyProtocol-policy

aws elb set-load-balancer-policies-for-backend-server \
  --load-balancer-name a19235ee9945011e9ac720a6c9a49806 \
  --instance-port 31272 \
  --policy-names ars-ProxyProtocol-policy

но я использовал 80 и 443 по ошибке.

Надеюсь, это кому-нибудь поможет.