NGINX para revertir proxy websockets Y habilitar SSL (wss: //)?

136

Estoy tan perdido y nuevo en la construcción de NGINX por mi cuenta, pero quiero poder habilitar websockets seguros sin tener una capa adicional.

No quiero habilitar SSL en el servidor websocket, sino que quiero usar NGINX para agregar una capa SSL a todo.

Cada página web dice que no puedo hacerlo, ¡pero sé que puedo! ¡Gracias a quien (yo) pueda mostrarme cómo!

crockpotveggies
fuente

Respuestas:

185

Solo para notar que nginx ahora tiene soporte para Websockets en la versión 1.3.13. Ejemplo de uso:

location /websocket/ {

    proxy_pass ​http://backend_host;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 86400;

}

También puede consultar el registro de cambios de nginx y la documentación de proxy de WebSocket .

Tarántula
fuente
Tiene los mismos problemas de tiempo de espera expresados ​​anteriormente;)
3rdEden
55
@ 3rdEden: para problemas de tiempo de espera, proxy_read_timeoutfunciona, edité la respuesta.
Steve Kehlet
2
¿Dónde debo poner esta configuración y qué es backend_host?
Aysennoussi
3
@Sekai: una locationdirectiva se coloca dentro de una serveru otra locationdirectiva (ver documentos de ubicación ). backend_hostes un upstream(ver documentos ascendentes ): uno o un grupo de servidores a los que va a enviar un proxy.
Radko Dinev
1
¿Qué pasa con este problema de tiempo de espera? ¿Realmente tenemos que establecerlo en un número muy grande para evitarlo? ¿No hay ahora alguna solución más elegante?
Mohammed Noureldin
54

No tengas miedo, porque un valiente grupo de programadores de operaciones ha resuelto la situación con un nuevo nginx_tcp_proxy_module.

Escrito en agosto de 2012, así que si eres del futuro deberías hacer tu tarea.

Prerrequisitos

Asume que está usando CentOS:

  • Elimine la instancia actual de NGINX (sugiera usar un servidor de desarrollo para esto)
  • Si es posible, guarde sus viejos archivos de configuración de NGINX para poder reutilizarlos (eso incluye su init.d/nginxscript)
  • yum install pcre pcre-devel openssl openssl-devel y cualquier otra biblioteca necesaria para construir NGINX
  • Obtenga el nginx_tcp_proxy_module de GitHub aquí https://github.com/yaoweibin/nginx_tcp_proxy_module y recuerde la carpeta donde lo colocó (asegúrese de que no esté comprimido)

Construye tu nuevo NGINX

De nuevo, asume CentOS:

  • cd /usr/local/
  • wget 'http://nginx.org/download/nginx-1.2.1.tar.gz'
  • tar -xzvf nginx-1.2.1.tar.gz
  • cd nginx-1.2.1/
  • patch -p1 < /path/to/nginx_tcp_proxy_module/tcp.patch
  • ./configure --add-module=/path/to/nginx_tcp_proxy_module --with-http_ssl_module (puede agregar más módulos si los necesita)
  • make
  • make install

Opcional:

  • sudo /sbin/chkconfig nginx on

Configurar Nginx

Recuerde copiar primero sus archivos de configuración anteriores si desea reutilizarlos.

Importante: necesitará crear una tcp {}directiva al más alto nivel en su conf. Asegúrese de que no esté dentro de su http {}directiva.

La siguiente configuración de ejemplo muestra un único servidor websocket ascendente y dos servidores proxy para SSL y no SSL.

tcp {
    upstream websockets {
        ## webbit websocket server in background
        server 127.0.0.1:5501;
        
        ## server 127.0.0.1:5502; ## add another server if you like!

        check interval=3000 rise=2 fall=5 timeout=1000;
    }   

    server {
        server_name _;
        listen 7070;

        timeout 43200000;
        websocket_connect_timeout 43200000;
        proxy_connect_timeout 43200000;

        so_keepalive on;
        tcp_nodelay on;

        websocket_pass websockets;
        websocket_buffer 1k;
    }

    server {
        server_name _;
        listen 7080;

        ssl on;
        ssl_certificate      /path/to/cert.pem;
        ssl_certificate_key  /path/to/key.key;

        timeout 43200000;
        websocket_connect_timeout 43200000;
        proxy_connect_timeout 43200000;

        so_keepalive on;
        tcp_nodelay on;

        websocket_pass websockets;
        websocket_buffer 1k;
    }
}
crockpotveggies
fuente
55
Esto fue bastante útil, pero todavía estaba obteniendo tiempos de espera en 60 segundos. Logré arreglar esto configurando lo siguiente: timeout 43200000; websocket_connect_timeout 43200000; websocket_read_timeout 43200000; websocket_send_timeout 43200000; proxy_connect_timeout 43200000; proxy_read_timeout 43200000; proxy_send_timeout 43200000;
jbg
2
¡Gracias por compartir eso! Más tarde me di cuenta de que estaba teniendo problemas similares, y casualmente, el propio yaoweibin también respondió a mi problema de GitHub con un enlace a su comentario sobre el Problema # 28. Small world ...
crockpotveggies
1
Quería servir websockets desde el mismo puerto http y solo después de que el navegador se haya autenticado. Parece que esto no puede manejar websockets en el mismo puerto. ¿Cómo manejan las personas esto?
uroc
1
Tomará algunas modificaciones de software para detectar el protocolo entrante. Dado que los websockets en realidad comienzan como un protocolo de enlace HTTP (un nivel de software más alto que TCP), debe ajustar su aplicación para manejar tanto el tráfico TCP como HTTP. Todavía no puedo recomendar una forma de hacerlo.
crockpotveggies
2
En caso de que otras personas de 2018 vengan aquí, estas directivas ya no funcionan. Visite nginx.org/en/docs/http/websocket.html para obtener instrucciones recientes o vea la respuesta de Harlan T Wood a continuación.
GaryO
37

Esto funcionó para mí:

location / {
    # redirect all HTTP traffic to localhost:8080
    proxy_pass http://localhost:8080;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # WebSocket support
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

- prestado de: https://github.com/nicokaiser/nginx-websocket-proxy/blob/df67cd92f71bfcb513b343beaa89cb33ab09fb05/simple-wss.conf

Harlan T Wood
fuente
3
Tuve problemas para que los sockets web de TeamCity funcionaran detrás de mi proxy inverso. Tu # WebSocket supportcorte lo hizo por mí. Anteriormente estaba tratando de reenviar el puerto 400, sin embargo, wss funciona en 443. FYI futuros lectores :)
Mario Tacke
¿Has descubierto la solución? Ya que tenía que también se enfrenta a un problema similar stackoverflow.com/q/53411060/7713811
Nɪsʜᴀɴᴛʜ
Me gusta más esta respuesta, ya que muchas personas (como usted) usan / para websockets y HTTP2 regular.
mikemaccana
@ Cualquiera, ¿cuál sería el Javascript que llama?
Andrew Simpson
17

para .net core 2.0 Nginx con SSL

location / {
    # redirect all HTTP traffic to localhost:8080
    proxy_pass http://localhost:8080;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # WebSocket support
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $http_connection;
}

Esto funciono para mi

Altair CA
fuente
¿Cuál es el código C #? Actualmente tengo esto para windows / iis _server = new WebSocketServer ("wss: //0.0.0.0: 8200 / MessageRelayer") {Certificate = new X509Certificate2 (PfxFileName, SslPassword), RestartAfterListenError = true};
Andrew Simpson
Estoy usando SignalR
Altair CA
Esta fue la única solución que funcionó para mí. ¡Gracias!
m-ketan
8

Para mí, todo se redujo a la proxy_passconfiguración de ubicación. Necesitaba cambiar para usar el protocolo HTTPS y tener un certificado SSL válido configurado en el lado del servidor del nodo. De esa manera, cuando introduzco un servidor de nodo externo, solo tengo que cambiar la IP y todo lo demás sigue siendo la misma configuración.

Espero que esto ayude a alguien en el camino ... Estuve mirando el problema todo el tiempo ... suspiro ...

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}
upstream nodeserver {
        server 127.0.0.1:8080;
}
server {
        listen 443 default_server ssl http2;
        listen [::]:443 default_server ssl http2 ipv6only=on;
        server_name mysite.com;
        ssl_certificate ssl/site.crt;
        ssl_certificate_key ssl/site.key;
        location /websocket { #replace /websocket with the path required by your application
                proxy_pass https://nodeserver;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection $connection_upgrade;
                proxy_http_version 1.1;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_host;
                proxy_intercept_errors on;
                proxy_redirect off;
                proxy_cache_bypass $http_upgrade;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-NginX-Proxy true;
                proxy_ssl_session_reuse off;
            }
}
CyberDigital
fuente
Lo intenté localtion /horizon, pero no funciona. Solo localtion /o location /websockifyfunciona. No sé por qué ...
njuguoyi
6

Un buen y conciso artículo de Pankaj Malhotra discute cómo hacer esto con NGINX y está disponible aquí .

La configuración básica de NGINX se reproduce a continuación:

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

upstream appserver {
    server 192.168.100.10:9222; # appserver_ip:ws_port
}

server {
    listen 8888; // client_wss_port

    ssl on;
    ssl_certificate /path/to/crt;
    ssl_certificate_key /path/to/key;


    location / {
        proxy_pass http://appserver;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }
}
btiernay
fuente
1
¿Las versiones modernas de NGINX también abordan los problemas de tiempo de espera?
crockpotveggies
2

Usando nginx / 1.14.0

tengo un servidor websocket que se ejecuta en el puerto 8097 y los usuarios se conectan a wss en el puerto 8098, nginx simplemente descifra el contenido y lo reenvía al servidor websocket

Entonces tengo este archivo de configuración (en mi caso /etc/nginx/conf.d/default.conf)

server {
    listen   8098;
        ssl on;
        ssl_certificate      /etc/ssl/certs/combined.pem;
        ssl_certificate_key  /root/domain.key;
    location / {

        proxy_pass http://hostname:8097;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 86400;

    }
}
John Smith
fuente