Двойной прокси-сервер NGINX не разрешает подключения через веб-сокет

У меня на компьютере установлено два прокси-сервера nginx: один для развертывания SSL, а другой для проксирования для конкретного приложения (только второй контролируется версией). Когда у меня был только один прокси-сервер, я мог успешно подключаться к веб-сокетам, но после перехода на два все запросы на обновление веб-сокетов отвечают ошибкой 502 Bad Gateway. Я могу подтвердить, что обычные запросы http/https работают с моей настройкой двойного прокси. Вот мой текущий конфиг.

Прокси 1

server {

        # SSL configuration
        #
        # listen 443 ssl default_server;
        # listen [::]:443 ssl default_server;
        #
        # Note: You should disable gzip for SSL traffic.
        # See: https://bugs.debian.org/773332
        #
        # Read up on ssl_ciphers to ensure a secure configuration.
        # See: https://bugs.debian.org/765782
        #
        # Self signed certs generated by the ssl-cert package
        # Don't use them in a production server!
        #
        # include snippets/snakeoil.conf;

        server_name staging.ambitx.io;

        location / {
                proxy_pass http://127.0.0.1:81;
                include proxy_params;
        }

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/staging.ambitx.io/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/staging.ambitx.io/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}
server {
    if ($host = staging.ambitx.io) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

        listen 80 default_server;
        listen [::]:80 default_server;

        server_name staging.ambitx.io;
    return 404; # managed by Certbot

}

proxy_params

proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

Прокси 2

(работает на докере с портом 81 на хост-компьютере, привязанным к порту 80 в контейнере)

resolver 127.0.0.11 ipv6=off;

server {
  listen       80;
  listen  [::]:80;

  location / {
    root /var/www/staticfiles;
    index  index.html index.htm;
    try_files $uri /index.html =404;
  }
  
  location /ws {
    access_log off;

    proxy_pass       http://wsserver;

    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }

  location /api {
    proxy_pass http://apiserver;
  }
}

Первоначально у меня были утверждения ниже в location /ws блок конфигурации Proxy 2…

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

но я удалил их, потому что они будут перезаписывать заголовки, установленные прокси-сервером 1.

Любые идеи? Пожалуйста, дайте мне знать, если вам нужна дополнительная информация.

веб-сокет nginx

1 ответ
1

Конечно, вы не должны отказываться от поддержки проксирования WebSocket ни с одного из ваших промежуточных прокси-серверов. Проксирование WebSocket очень специальное заданиечтобы разрешить переключение протоколов и поддерживать установленное и активное соединение WebSocket, вы также должны вернуть его поддержку вашему первому прокси-серверу nginx:

...
location / {
    proxy_pass http://127.0.0.1:81;
    include proxy_params;
}
location /ws {
    proxy_pass http://127.0.0.1:81;
    include proxy_params;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}
...

Удаление всех трех proxy_set_header директивы от вашего второго прокси-сервера nginx также были плохой идеей. Пока X-Real-IP а также X-Forwarded-For заголовки будут переданы вашему приложению WebSocket точно так же, как они установлены вашим первым прокси-сервером, Host заголовок особенный. Если не задано явно, оно будет передано равным имени восходящего потока, используемому в proxy_pass директива, т.е. Host: wsserver. Как вы можете прочитать из proxy_set_header директива документация:

По умолчанию переопределяются только два поля:

proxy_set_header Host $proxy_host;
proxy_set_header Connection close;

(второй наверняка сломает любую попытку установить соединение WebSocket). Итак, чтобы сохранить оригинал Host заголовок из клиентского запроса (обычно это хорошая идея, вы можете прочитать больше об этом заголовке здесь), вернуть

proxy_pass Host $http_host;

линия к обоим location / { ... } а также location /ws { ... } вашей второй конфигурации прокси-сервера nginx.

Иван Шацкий

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *