NGINX stripping websocket "Upgrade" headers

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

NGINX stripping websocket "Upgrade" headers

satay
I have the following in my site.conf file:

------------------------------------------------------------
        #Websockets
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
------------------------------------------------------------

My understanding is that this will set the proper headers for websocket
communications.

Specifically, it will add a "Connection" header with value "Upgrade" and add
an "Upgrade" header with whatever value the client passes to it in the
"Upgrade" header (i.e. add the header on for hop-to-hop to upstream server).
In the case of websockets, the Upgrade header will be "websocket."

I am executing the following curl command to my server directly on my LAN
(no NGINX involved)

------------------------------------------------------------
curl -k --include --no-buffer --header "Connection: Upgrade" --header
"Upgrade: websocket" --header "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ=="
--header "Sec-WebSocket-Version: 13" --header "X-Plex-Token:
87LJshwQwRxT24jaY8Be" https://10.10.10.102:32400/:/websockets/notifications
------------------------------------------------------------

This results in the following headers being passed to the server (via packet
capture on the server):

------------------------------------------------------------
Frame 1268: 307 bytes on wire (2456 bits), 307 bytes captured (2456 bits) on
interface 0
Ethernet II, Src: IntelCor_2f:f6:e4 (24:77:03:2f:f6:e4), Dst:
AsrockIn_7c:fe:cf (70:85:c2:7c:fe:cf)
Internet Protocol Version 4, Src: 10.10.10.201, Dst: 10.10.10.102
Transmission Control Protocol, Src Port: 53019, Dst Port: 32400, Seq: 1,
Ack: 1, Len: 253
Hypertext Transfer Protocol
    GET /:/websockets/notifications HTTP/1.1\r\n
    Host: 10.10.10.102:32400\r\n
    User-Agent: curl/7.68.0\r\n
    Accept: */*\r\n
    Connection: Upgrade\r\n
    Upgrade: websocket\r\n
    Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==\r\n
    Sec-WebSocket-Version: 13\r\n
    X-Plex-Token: 87LJshwQwRxT24jaY8Be\r\n
    \r\n
    [Full request URI:
http://10.10.10.102:32400/:/websockets/notifications]
    [HTTP request 1/1]
    [Response in frame: 1269]
------------------------------------------------------------

This appears as normal, and is met with a proper "Switching Protocols"
result.

However, when I send this request out to my public IP through NGINX, my
server sees this in the headers:

------------------------------------------------------------
Frame 2494: 381 bytes on wire (3048 bits), 381 bytes captured (3048 bits) on
interface 0
Ethernet II, Src: AsustekC_c6:4c:30 (1c:b7:2c:c6:4c:30), Dst:
AsrockIn_7c:fe:cf (70:85:c2:7c:fe:cf)
Internet Protocol Version 4, Src: 10.10.10.1, Dst: 10.10.10.102
Transmission Control Protocol, Src Port: 40461, Dst Port: 32400, Seq: 1,
Ack: 1, Len: 315
Hypertext Transfer Protocol
    GET /:/websockets/notifications HTTP/1.1\r\n
    Host: plex.mydomain.com\r\n
    X-Real-IP: 10.10.10.201\r\n
    X-Forwarded-For: 10.10.10.201\r\n
    X-Forwarded-Proto: https\r\n
    Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==\r\n
    Sec-WebSocket-Version: 13\r\n
    Connection: Upgrade\r\n
    user-agent: curl/7.68.0\r\n
    accept: */*\r\n
    x-plex-token: 87LJshwQwRxT24jaY8Be\r\n
    \r\n
    [Full request URI: http://plex.mydomain.com/:/websockets/notifications]
    [HTTP request 1/1]
------------------------------------------------------------

Note that while the "Connection" header has been added (presumably because
it is added specifically in the conf file via text and not passed header
variable), the "Upgrade" header is missing.

If I change my /conf file to look like this:

------------------------------------------------------------
        #Websockets
        proxy_http_version 1.1;
        proxy_set_header Upgrade websocket;
        proxy_set_header Connection "Upgrade";
------------------------------------------------------------

Then the header is properly added.

My understanding is NGINX strips/doesn't pass along any empty headers. Based
on my my local IP test, it is clear that the appropriate header is set (you
can see it in my sent curl statement and in the packet that the server
receives). However, due to the fact that it is not passed on to the upstream
server via NGINX, it would appear that NGINX thinks the $http_upgrade is
empty from the client and therefore not passing it on.

Can anyone explain this?

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,286719,286719#msg-286719

_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: NGINX stripping websocket "Upgrade" headers

satay
Figured it out!

https://www.reddit.com/r/nginx/comments/eodrjc/nginx_stripping_websocket_upgrade_headers/

fireye quote:
----------------------------------------
Got it figured out, this is a quirk of HTTP/2.0 vs 1.1. Per RFC-2616:

The Upgrade header field is intended to provide a simple mechanism for
transition from HTTP/1.1 to some other, incompatible protocol.

It looks like nginx discards the Upgrade header, when presented by a client,
if the client communicates via HTTP2.0 already. You can confirm this by
using the --http1.1 flag with curl and looking at the headers being
transferred.
-----------------------------------------

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,286719,286721#msg-286721

_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: NGINX stripping websocket "Upgrade" headers

J.R.
In reply to this post by satay
> Got it figured out, this is a quirk of HTTP/2.0 vs 1.1. Per RFC-2616:

I tried to follow all your comments on reddit & plex, but I'm not
really sure if you resolved this issue or just decided it was
impossible...

Have you tried using the nginx stream module?

http://nginx.org/en/docs/stream/ngx_stream_core_module.html

As for your issue about needing to include both the LE cert and your
own in one file... Most people figure that out when they enable OCSP
stapling... hehe... But yeah, I was scratching my head too for a bit
at first then I found some posts mentioning what to do...
_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: NGINX stripping websocket "Upgrade" headers

satay
Yes, it is solved with the proper cert configuration.

I hadn't fully validated some advanced SSL options like OCSP stapling as I
was first trying to get all the basics working.
Since my browsers all validated the certificate chain without issue, I had
assumed they were installed properly.
I've had the issue with including the full chain (or at least the
intermediate) in the cert file before with some other web server products,
but in those cases none of the clients would properly connect without it.

I've looked at the link you send on the ngx_stream_core_module and googled a
bit more about it.  While I see some documentation talking about
configuring, I don't really see much in the way of exactly explaining its
use cases.  Best I can tell it is used for load balancing options - but I'm
not sure how they would apply in this case?

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,286719,286730#msg-286730

_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx