Closed karnal222 closed 3 years ago
HTTP/1.1 400 Bad Request Server: nginx/1.18.0...
Looks like the request goes not only though the CloudFront, but also though Nginx.
You need to configure Nginx properly to work with Websockets.
Yes nginx is already configured in a similar way as in your link or the tutorial I provided. My nginx.conf:
server { listen 80; root /var/www/html; index index.html index.htm index.nginx-debian.html; server_name something.cloudfront.net; location / { proxy_read_timeout 1d; proxy_send_timeout 1d; proxy_pass http://127.0.0.1:8022; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection \"upgrade\"; } }
Can you:
tcpdump -w
file (not just the output text above) and attach it?Is the problem reproducible every time or there are also some successful connection attempts?
Is the problem reproducible if you insert some intermediate TCP forwarder before websocat (e.g. socat tcp-l:7022,fork,reuseaddr tcp:127.0.0.1:8022
) and direct Nginx to it?
If I do everything without CloudFront and only nginx + websocat on the remote end it works perfectly. I can ssh into the machine. The problem is 100% reproducible either it works all of the time or, if using CloudFront, it fails 100% of the time with always the same error.
Attached is the server side of an unsuccessful connection through CloudFront. I can also post others if required.
I'll try to fiddle a bit with socat.
websocat_nossl_0.4_i686-unknown-linux-musl l-ws:127.0.0.1:8022 tcp:127.0.0.1:2222
. You can find pre-built old versions on Github releases.sudo websocat --binary ws-l:0.0.0.0:80 tcp:127.0.0.1:22 -vvv &
The error is the same (400 Bad request, Only websocket connections are welcome here). So the problem is probably not nginx. Maybe CloudFront mangles some Header or other stuff which websocat expects in some other form? I see some additional and missing headers in case I use no CloudFront and the connection is working (pcap attached)
Maybe CloudFront mangles some Header
Now I see: Sec-WebSocket-Version
and Sec-WebSocket-Key
headers required for WebSocket connection establishment are missing.
Maybe you need to manually enable certain headers. CloudFront's documentation mentions it should be compatible with WebSockets without additional configuration, so maybe you explicitly configured it to drop some headers?
Yeah in theory websockets should work with cloudfront. I tried to set the header manually (which I guess is not the correct thing to do) and the error looks a bit different on the client:
websocat: WebSocket response error: Sec-WebSocket-Accept is invalid
pcap:
On the server there does not seem to be an error:
[INFO websocat::net_peer] Incoming TCP connection from Some(V4(127.0.0.1:36364)) [DEBUG websocat::sessionserve] Underlying connection established [INFO websocat::sessionserve] Serving 1 ongoing connections [INFO websocat::ws_server_peer] Incoming connection to websocket: / [DEBUG websocat::ws_server_peer] Incoming { version: Http11, subject: (Get, AbsolutePath("/")), headers: Headers { Upgrade: websocket , Connection: Upgrade , Host: 127.0.0.1:8022 , User-Agent: Amazon CloudFront , Via: 1.1 5066297e571270218f6c9f8fb4fdd813.cloudfront.net (CloudFront) , X-Forwarded-For: 65.0.93.44 , X-Amz-Cf-Id: OvKSiW9AFIcSz5nIsUdMnZc5h55xU5Jkm6uetSGg7z4bKPSQW70U4g== , Sec-WebSocket-Key: dhpc9Q732h/9d72CNGvZ7A== , } } [DEBUG websocat::ws_server_peer] Headers { } [DEBUG websocat::ws_server_peer] Headers { Sec-WebSocket-Accept: R68+WKf5Jt2eNoBREskuR5bYoZY= , Connection: Upgrade , Upgrade: websocket , } [INFO websocat::ws_server_peer] Upgraded [INFO websocat::net_peer] Connected to TCP 127.0.0.1:22 [DEBUG websocat::net_peer] We have a winner. Disconnecting losers. [DEBUG websocat::ws_peer] incoming None [DEBUG websocat::my_copy] BrokenPipe: read_done [DEBUG websocat::my_copy] done [INFO websocat::sessionserve] Forward finished [DEBUG websocat::sessionserve] Forward shutdown finished [DEBUG websocat::my_copy] zero len [DEBUG websocat::my_copy] read_done [DEBUG websocat::my_copy] done [INFO websocat::sessionserve] Reverse finished [DEBUG websocat::sessionserve] Reverse shutdown finished [DEBUG websocat::ws_peer] drop WsWriteWrapper [INFO websocat::sessionserve] Both directions finished
I'll take a closer look at the headers (tomorrow^^, thx for help up to now :) )
I tried to set the header manually (which I guess is not the correct thing to do)
Do you mean setting it to a fixed value instead of client-supplied? If yes then this won't work. It is a protection against WebSocket clients (i.e. web pages without any special permissions from users) connecting to something other than WebSocket servers.
Sec-WebSocket-Accept is invalid
Sec-WebSocket-Accept
is (sort-of-cryptographically) derived from Sec-WebSocket-Key
.
You need to investigate and debug why Sec-WebSocket-Key
is getting lost on the way through the CloudFront.
Looks like it is working with: https://nickzamosenchuk.medium.com/configure-amazon-cloudfront-cdn-for-websocket-connection-43c44b3f877c :)
So is the issue resolved?
Yes :)
I was following this tutorial: https://kernal.eu/posts/ssh-over-websocket/ Version was the latest linux amd 64 static release. Instead of Cloudflare I tested AWS Cloudfront. If I do a normal site to site connection without CDN then ssh+websocat works. However if I use the CDN in between and on the client do:
I get:
On the remote end:
Websocat gives an error:
From tcpdump I could see:
For whatever reason, I get a 400 Bad Request error through the CDN.
Any ideas what could be the issue or how to debug this further?