NGINX Problem with Configuring Websocket, no problem with SSE - asp.net

i am working with NGINX configuration right now.
I am stuck at some strange point.
NGINX allow me to connect via SSE but not via Websocket.
This is my config:
server {
listen 80;
server_name _;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
This is error from browser console via WEBSOCKET:
[2022-08-30T07:49:22.667Z] Error: Failed to start the transport 'WebSockets': Error: WebSocket failed to connect. The connection could not be found on the server, either the endpoint may not be a SignalR endpoint, the connection ID is not present on the server, or there is a proxy blocking WebSockets. If you have multiple servers check that sticky sessions are enabled.
This is WEBSOCKET: Browser Error
This is SSE: SSE
##EDIT:
This is section which fail:
try {
await this._startTransport(connectUrl, requestedTransferFormat);
this.connectionId = negotiate.connectionId;
return;
}
catch (ex) {
this._logger.log(LogLevel.Error, `Failed to start the transport '${endpoint.transport}': ${ex}`);
negotiate = undefined;
transportExceptions.push(new FailedToStartTransportError(`${endpoint.transport} failed: ${ex}`, HttpTransportType[endpoint.transport]));
if (this._connectionState !== "Connecting" /* Connecting */) {
const message = "Failed to select transport before stop() was called.";
this._logger.log(LogLevel.Debug, message);
return Promise.reject(new Error(message));
}
###UPDATE
I FOUND THE SOLUTION. MY ANSWER IS CORRECT ANSWER! :)

I am comming back with the solution.
It is easy.
First thing is:
You need to add in nginx.conf in http { } section this:
map $http_upgrade $connection_upgrade
{
default upgrade;
'' close;
}
and this is config:
server {
listen 50150;
server_name _;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_cache off;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
and you are good to go!
[2022-08-30T10:30:48.900Z] Information: WebSocket connected to ws:~~/~hub?id=Zk-~~.
I hope someone will get use of it.
Best regards

Related

Nginx Reverse Proxy - websocket config to make Fortigate CLI interface work properly

I am trying to get my fortigate router's web interface behind my reverse proxy, not to be accessible from the internet, but to use my LetsEncrypt cert on my internal network. This is the config I'm using:
upstream websockets {
server 192.168.1.99:443;
}
server {
listen 443 ssl;
allow 192.168.1.0/24;
deny all;
server_name f60e.*;
include /config/nginx/ssl.conf;
client_max_body_size 0;
location / {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Authorization "";
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Origin "";
proxy_pass_header X-XSRF-TOKEN;
proxy_pass https://192.168.1.99;
proxy_send_timeout 300;
proxy_read_timeout 300;
send_timeout 300;
client_max_body_size 1000m;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /websockets/ {
proxy_pass https://websockets;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_set_header Origin "";
}
}
Everything appears to work except for the "Edit in CLI" button. When I attempt to use it, the interface window comes up blank and after a few seconds it says "Connection lost" and I get this error in my browser console
GET https://f60e.walnuthomelab.com/favicon/site.webmanifest net::ERR_CONNECTION_TIMED_OUT
main.js:1
WebSocket connection to 'wss://f60e.walnuthomelab.com/ws/cli/open?cols=66&rows=34' failed:
createWebSocket # main.js:1

Nginx Proxy to multiple ports from same location

I currently have this configuration
server {
listen 4000;
location / {
proxy_pass http://websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_redirect off;
}
upstream websocket {
server localhost:4444;
}
upstream websocket2 {
server localhost:4445;
}
With this configuration, all requests from localhost:4000 are being proxied to localhost:4444.
I don't want to proxy those request only to localhost:4444 but also to localhost:4445
localhost:4000 proxies requests to
localhost:4444
localhost:4445
Is it possible?
From the Nginx documentation here, you can declare an upstream with multiple servers. Then you can forward the traffic to this upstream and the traffic will be dispatched between servers. So just put your servers in 1 upstream websocket to archive what you want.
upstream websocket {
server localhost:4444;
server localhost:4445;
}
server {
listen 4000;
location / {
proxy_pass http://websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_redirect off;
}

configuring django channels for websockets in production

I have a page that has a .../chat/ url, and the whole thing works on localhost. I'm trying to deploy on ubuntu and having a hard time.
I guess getting to the point looks like posting what I've got:
/etc/nginx/sites-enabled/mysite:
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s;
upstream channels-backend {
server localhost:8001;
}
server {
listen 80;
server_name foo.com;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/ubuntu/mysite/mysite/;
}
location / {
include proxy_params;
limit_req zone=mylimit;
proxy_pass http://unix:/run/gunicorn.sock;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
I tried changing my server block to contain:
...
location / {
try_files $uri #proxy_to_app;
include proxy_params;
limit_req zone=mylimit;
proxy_pass http://unix:/run/gunicorn.sock;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location #proxy_to_app {
proxy_pass http://channels-backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_set_header Host $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-Host $server_name;
}
...
but this prompted my webpage to 503.
My python and js seem fine as it works on localhost, and running daphne -b 0.0.0.0 -p 8001 myproject.asgi:application results in a Listening... message so that seems fine.
My /etc/supervisor/conf.d/mysite.conf is:
[program:mysite_asgi]
directory=/home/ubuntu/mysite/mysite
command=/home/ubuntu/mysite/mysite/venv/bin/daphne -b 0.0.0.0 -p 8001 mysite.asgi:application
autostart=true
autorestart=true
stopasgroup=true
user=ubuntu
stdout_logfile=/home/ubuntu/mysite/daphnelog/asgi.log
redirect_stderr=true
The browser console shows WebSocket connection to 'ws://foo.com/chat/' failed: Error during WebSocket handshake: Unexpected response code: 503, I'm unsure if I've posted a complete picture of what needs to be said in order to help you help me--please let me know if there's more information to reach that end. Thank you!

Seems like nginx proxy server doen't work

I'm newbie in linux and nginx hosting. I have a simple asp net core app that I need to run on https://localhost:5050. So I send a request on https://localhost and wait that nginx process it to :5050 port. But it doesn't..
I created \etc\nginx\sites-available\aspnetcore.conf with the following code
server {
listen 443;
location / {
proxy_pass https://localhost:5050;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
and a symbolinc link to /etc/nginx/sites-enabled/aspnetcore.conf.
Also I have in Startup.cs file
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
So my app doesn't get any request. What am I doing wrong?
UPD:
The problem in https contact. I configured ssl as it describes here
https://www.digitalocean.com/community/tutorials/how-to-create-a-self-signed-ssl-certificate-for-nginx-on-debian-9
But still there are no requests...
upstream backend {
server localhost:5050;
}
server {
listen 443;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}

Nginx Reverse Proxy Websocket Authentication - HTTP 403

I'm using Nginx as a reverse proxy of a Spring boot application. I also use Websockets with sockjs and stomp messages.
Here is the context configuration.
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/localization" >
<websocket:sockjs/>
</websocket:stomp-endpoint>
<websocket:simple-broker prefix="/topic" />
</websocket:message-broker>
Here is the client code:
var socket = new SockJS(entryPointUrl);
var stompClient = Stomp.over(socket);
var _this = this;
stompClient.connect({}, function () {
stompClient.subscribe('/app/some-url', function (message) {
// do some stuff
});
});
I also you Spring Security to protect some content.
#Configuration
#Order(4)
public static class FrontendSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/js/**", "/css/**", "/webjars/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.logout().permitAll();
}
}
Everything works great, expect when I run this application behind a Nginx reverse proxy. Here is the reverse configuration:
proxy_pass http://testsysten: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 (nginx 1.4)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
# Max body size
client_max_body_size 10M;
The connection always fails with a HTTP 403 code.
I'm using version 1.9.7.
Do you have any idea, why the client does not gets authenticated?
I know similar questions, like this one but the solutions do not work at all.
Update
I managed to run the application over HTTP. I need to pass the CSRF token in the Nginx configuration. New configuration is:
proxy_pass http://testsysten: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;
# Pass the csrf token (see https://de.wikipedia.org/wiki/Cross-Site-Request-Forgery)
# Default in Spring Boot
proxy_pass_header X-XSRF-TOKEN;
# WebSocket support (nginx 1.4)
proxy_http_version 1.1;
Only missing is redirect over HTTPS. In the Spring logs is see following entry:
o.s.w.s.s.t.h.DefaultSockJsService - Processing transport request: GET http://testsystem:80/localization/226/3mbmu212/websocket
Seems like Nginx Proxy needs to rewrite the to the right port.
I solved the problem by myself. Basically, Nginx needs to pass some additional header values if you want to use Websocket and Spring Security. The following lines need to be added to location section in your Nginx config:
# Pass the csrf token (see https://de.wikipedia.org/wiki/Cross-Site-Request-Forgery)
# Default in Spring Boot and required. Without it nginx suppresses the value
proxy_pass_header X-XSRF-TOKEN;
# Set origin to the real instance, otherwise a of Spring security check will fail
# Same value as defined in proxy_pass
proxy_set_header Origin "http://testsysten:8080";
The accepted solution did not work for me although I was using a very classical HTTPS configuration:
server {
listen 443 ssl;
location /ws {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:8888;
}
...
The problem is that Spring checks the origin and specifically that code was causing me trouble:
// in org.springframework.web.util.UriComponentsBuilder.adaptFromForwardedHeaders(HttpHeaders):
if ((this.scheme.equals("http") && "80".equals(this.port)) ||
(this.scheme.equals("https") && "443".equals(this.port))) {
this.port = null;
}
In that code the scheme is 'http' and the port is 8888, which is not discarded because it is not the standard port.
The browser however hits https://myserver/ and the 443 port is omitted because it is the default HTTPS one.
Therefore the ports do not match (empty != 8888) and origin check fails.
Either you can disable origin checks in Spring WebSockets:
registry.addHandler( resgisterHandler(), "/ws" ).setAllowedOrigins( "*" );
or (probably safer) you can add the scheme and port to the NGINX proxy configuration:
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
If you are interested, those headers are read in
org.springframework.web.util.UriComponentsBuilder.adaptFromForwardedHeaders(HttpHeaders)
For Spring Boot 2.2.2+
Starting with Spring Boot version 2.2.2 you should be adding following setting for these X-Forwarded-* headers to be taken into account:
server.forward-headers-strategy=native
(in application.properties for instance)
I had faced a similar problem. I was unable to use the basic Spring Security authentication with NGINX. Apart from setting the proxy_pass_header X-XSRF-TOKEN;, I also had to set underscores_in_headers on;, since NGINX by default does not allow headers with underscores and the CSRF token is named _csrf.
So my final configuration file looked like this:
server {
underscores_in_headers on;
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
root /usr/share/nginx/html;
index index.html index.htm;
# Make site accessible from http://localhost/
server_name localhost;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
# Uncomment to enable naxsi on this location
# include /etc/nginx/naxsi.rules
}
location /example/ {
proxy_pass_header X-XSRF-TOKEN;
proxy_pass http://localhost:8080/;
}
}
I solved this problem without CSRF header in NGINX proxy.
My stack: spring-boot, spring-security (with redis session store), spring-boot-websocket with default STOMP implementation, NGINX to serve frontend and proxied to another services that frontend consume.
In first time I use the default configuration show in the NGINX Blog here and here (copy and paste for history):
http {
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream websocket {
server 192.168.100.10:8010;
}
server {
listen 8020;
location / {
proxy_pass http://websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
}
But dont work, still 403 Forbidden.
I fixed this issue with the configuration below (the real important part to fix websocket is # WebSocket Proxy):
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 30010;
server_name localhost;
client_max_body_size 10M;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
# Backend API Proxy
location /api {
proxy_pass http://192.168.0.100:30080;
proxy_set_header Host $http_host;
proxy_set_header Access-Control-Allow-Origin 192.168.0.100;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-Proxy true;
rewrite ^/api/?(.*) /$1 break;
proxy_redirect off;
}
# CDN Proxy
location ~ ^/cdn/(.*) {
proxy_pass http://192.168.0.110:9000;
rewrite ^/cdn/(.*) /$1 break;
}
# This is the configuration that fix the problem with WebSocket
# WebSocket Proxy
location /ws {
proxy_pass http://192.168.0.120:30090;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_set_header Access-Control-Allow-Origin 192.168.0.120;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-Proxy true;
}
}
}
In my case (Spring Boot app), in addition to setting the Origin header as specified in the accepted answer, I had to set the Host header to match the ip:port of the Origin header, or to get rid of it altogether.
This is my working vhost config:
server {
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /etc/ssl/certs/<your-cert-file>.pem;
ssl_certificate_key /etc/ssl/private/<your-key-file>.key;
server_name <your-server-fqdn>;
access_log /var/log/nginx/<your-server-fqdn>.access.log;
error_log /var/log/nginx/<your-server-fqdn>.error.log error;
root /srv/www/<your-server-fqdn>;
index index.html index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://127.0.0.1:8080/v1;
}
location /async-api {
proxy_pass http://127.0.0.1:8080/stomp;
proxy_http_version 1.1;
# either set Host header as follows or get rid of the directive altogether
#proxy_set_header Host "127.0.0.1:8080";
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
# Set origin to the real instance, otherwise a of Spring security check will fail
# Same value as defined in proxy_pass
proxy_set_header Origin "http://127.0.0.1:8080";
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /admin-api {
proxy_pass http://127.0.0.1:8080/api;
}
}

Resources