Websockets behind Apache and Nginx proxy connection not upgraded - apache

I have a problem. Apache listens on a white ip and proxies all requests /ssd on nginx that proxies requests /city-dashboard to another server with websockets. In apache config:
ProxyPass /ssd/ http://10.127.32.24
ProxyPassReverse /ssd/ http://10.127.32.24
nginx config:
on nginx.conf:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
include /etc/nginx/conf.d/*.conf;
on default.conf
location /city-dashboard/stream {
proxy_pass http://10.127.32.24:5000/stream;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
Request headers:
Connection: Upgrade
Upgrade: Websocket
Response headers:
Connection: close
Status Code 400 Bad Request
what am I doing wrong?

What about authorization header in request ? it seems authorization problem.

Related

troubleshooting application behind nginx reverse proxy, as POST/PUT requests are replied with Error 400 (Bad Request)

I'm trying to host my Angular/ASP.net core 3.1 application on linux for the first time.
Nginx stands on the port 80 and serves the static files and acts as a reverse proxy for the api part which will be passed to the .NET/kestrel server.
The problem is that I systematically get a 400 status code error (Bad Request) on any web API request containing a body, like POST & PUT, but GET is ok.
I added some logs through a middleware, just to see if I can get the requests. Basically, something like:
app.Use(async (context, next) => {
context.Request.EnableBuffering();
string reqBody;
using (var reader = new StreamReader(context.Request.Body))
{
reqBody = await reader.ReadToEndAsync();
context.Request.Body.Seek(0, SeekOrigin.Begin);
ms_oLogger.Debug($"Incoming request: METHOD={context.Request.Method} PATH={context.Request.Path} BODY=\"{reqBody}\"");
}
await next();
});
This just loggs stuff for GET requests, but nothing appears for the problematic PUT/POST requests... Can I conclude that this is only a nginx problem?
I also enabled the logs on nginx for the given "/api" location, but I can not tell what happens... How can I know which tier has generated the 400 status code?
EDIT1: I started a blank new project just with a poor Web API project containing one GET and one POST method just to check if there was something wrong with my application, but I still get the problem.
So I set up a new ubuntu server (this time, ubuntu server instead of desktop version) and now it works!!!
I compared configuration etc... but could not figure out what was wrong!....
But my initial question is still valid: how can I troubleshoot where the problem comes from?
EDIT2: This is my default.conf:
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
try_files $uri $uri/ =404;
}
location /fusion {
root /opt/fichet/WebUI/NgApp;
}
location /fusion/api {
proxy_pass http://localhost:5000/api;
error_log /var/log/nginx/fusion_error_logs.log debug;
access_log /var/log/nginx/fusion_access.log;
proxy_http_version 1.1;
proxy_cache_bypass $http_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
}
}
It seems that no firewall is enabled ("sudo ufw status verbose" tells us that it is "inactive")
Remove these lines from your nginx config.
proxy_cache_bypass $http_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
These headers are used for WebSocket connections, and shouldn't be present for non-websocket requests. My guess is your upstream server doesn't mind them for GET requests but does for POST/PUT, for some reason.
If you are not using websockets, you can leave them removed.
If you are using websockets, you need nginx to add or not these headers based on whether the requests is websockets or not. Something like this should work:
http {
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
...
location /fusion/api {
proxy_pass ...
...
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
See here for more info.

Nginx as a WebSocket Proxy giving Invalid URL Prefix error

We are facing issue while configuring Nginx as a webSocket proxy in Web Application. We have added the following configuration related to websockets in nginx.conf:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream websocket {
server 234.61.34.23:3389;
}
location /websocket-tunnel/connect/ {
proxy_read_timeout 86400;
proxy_redirect off;
proxy_pass http://websocket;
proxy_buffering off;
proxy_http_version 1.1;
add_header Upgrade $http_upgrade;
add_header Connection "upgrade";
add_header Host $host;
add_header X-Real-IP $remote_addr;
add_header X-Forwarded-For $proxy_add_x_forwarded_for;
add_header X-Forwarded-Server $host;
}
234.61.55.59:8090 - is the IP address & Port of the machine on which Nginx is installed and running.
234.61.34.23:3389 - is the IP address & port of the backend system to connect.
Now from the javascript code in Web application we are creating websocket and generating the following request URL:
wss://234.61.55.59:8090/websocket-tunnel/connect?id=12345
Now getting the following error in Nginx error.log
[error] 9516#33264: *396 invalid URL prefix in "ws://websocket/websocket-tunnel/connect?id=12345"
Is there any thing we have missing in nginx websocket configuration? Any Suggestions on resolving this error please.

Deploy asp.net core mvc behind NGINX that does SSL Termination - Redirects to non secure login page

When requesting the site via SSL and not authenticated https://example.com
I get redirected to http://example.com/Account/Login
I have NGINX running at the edge as a load balancer that terminates the SSL and proxies the request as http to IIS.
NGINX Config
server {
server_name example.com;
listen 443 ssl;
location / {
proxy_pass http://cluster1;
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-Proto $scheme;
}
}
as you can see I'm setting the X-Forwarded-For and X-Forwarded-Proto headers
In my MVC app startup i have this in Configure
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto,
ForwardLimit = null,
RequireHeaderSymmetry = false
});
I have [RequireHttps] attribute on my controllers.
What am I missing?
Update .NETCore 1.0.1, 1.0.2, 1.1.0:
New update .NETCore 1.0.1, 1.0.2, 1.1.0 seems fixed the issue
Update workaround:
Understand the root cause from answer https://serverfault.com/a/516382/379823
It works for us on Ubuntu 16.04 LTS / Nginx/1.10.0
Add map:
map $http_x_forwarded_proto $thescheme {
default $scheme;
https https;
}
and changing the proxy_set_header line to use the new scheme:
# proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Proto $thescheme;
Previous workaround:
We got the same issue. We do workaround by NGINX redirect HTTP => HTTPS (will get double redirect).
When I just read carefully docs.asp.net again, it seems redirect HTTP => HTTPS is the only way this time.
server {
listen *:80;
add_header Strict-Transport-Security max-age=15768000;
return 301 https://$host$request_uri;
}
See docs here Securing our application / Configure SSL

Nginx does redirect, not proxy

I want to set up Nginx as a reverse proxy for a https service, because we have a special usecase where we need to "un-https" a connection:
http://nginx_server:8080/myserver ==> https://mysecureservice
But what happens is that the actual https service isn't proxied. Nginx does redirect me to the actual service, so the URL in the browser changes. I want to interact with Nginx as it was the actual service, just without https.
This is what I have:
server {
listen 0.0.0.0:8080 default_server;
location /myserver {
proxy_pass https://myserver/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
}
}
You have to use the proxy_redirect to handle the redirection.
Sets the text that should be changed in the “Location” and “Refresh” header fields of a
proxied server response. Suppose a proxied server returned the header field
“Location:https://myserver/uri/”. The directive
will rewrite this string to “Location: http://nginx_server:8080/uri/”.
Example:
proxy_redirect https://myserver/ http://nginx_server:8080/;
Source: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_redirect
You can setup nginx like this if you do not want the server to do redirects:
server
{
listen 80;
server_name YOUR.OWN.DOMAIN.URL;
location / {
proxy_pass http://THE.SITE.URL.YOU.WANT.TO.DELEGAGE/;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
For me, this config was sufficient:
events {
}
http {
server {
location / {
resolver 8.8.8.8;
proxy_pass https://www.example.com$request_uri;
}
}
}
(Note that the resolver directive has nothing to do with the problem in the OP, I just needed it to be able to proxy an external domain such as example.com)
The problem for me was just that I was missing the www. in www.example.com. In the Firefox developer's console, I could see the GET request to localhost coming back with a 301, and so I thought that NGINX was issuing 301s instead of just mirroring example.com. Not so: in fact the problem was that example.com was returning 301s to redirect to www.example.com, NGINX was dutifully mirroring those 301s, and then Firefox "changed the URL" (followed the redirect) straight from localhost to www.example.com.
I was having a similar issue. In my case, I was able to resolve the issue by added a trailing slash to the proxy_pass URL:
before
server {
location / {
proxy_pass http://example.com/path/to/some/folder;
}
}
after
server {
location / {
# added trailing slash
proxy_pass http://example.com/path/to/some/folder/;
}
}

Ratchet + nginx + SSL/secure websocket

I've been trying to run Ratchet.io over SSL (this problem: php ratchet websocket SSL connect?).
My webserver is running at myhost.mobi, and I have created a separate virtual host for websocket service "wws.myhost.mobi".
My web socket:
$webSock = new React\Socket\Server($loop);
$webSock->listen(8080, '0.0.0.0');
$webServer = new Ratchet\Server\IoServer(
new Ratchet\Http\HttpServer(
new Ratchet\WebSocket\WsServer(
new Ratchet\Wamp\WampServer(
$pusher
)
)
),
$webSock
);
My nginx config (I'm on nginx 1.5.8):
upstream websocketserver {
server localhost:8080;
}
server {
server_name wss.myapp.mobi;
listen 443;
ssl on;
ssl_certificate /etc/ssl/myapp-mobi-ssl.crt;
ssl_certificate_key /etc/ssl/myapp-mobi.key;
access_log /var/log/wss-access-ssl.log;
error_log /var/log/wss-error-ssl.log;
location / {
proxy_pass http://websocketserver;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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-Proto https;
proxy_read_timeout 86400; # neccessary to avoid websocket timeout disconnect
proxy_redirect off;
}
}
My client-side script:
var conn = new ab.Session('wss://wss.myapp.mobi', function(o) {
// ...
}, function() {
console.warn('WebSocket connection closed');
}, {
skipSubprotocolCheck: true
});
So, when I load the page in Firefox, I see an outgoing connection to wss://wss.myapp.mobi:8080/, which is hanging (the spinner) and never completes or dies. I do not see any trace of request arriving on the backend in the logs.
What am I missing there?
Thanks!
EDIT I have realized that I should be connecting to wss://wss.myapp.mobi, but now I am getting "101 Switching Protocols" status.
EDIT 2 Everything is working now with the config above. "101 Switching Protocols" status turns out to be a normal message. PROBLEM SOLVED!
By checking question edit history, it is clear that, the configuration in the question was correct, temuri was trying to connect from client with port set in,
upstream websocketserver {
server localhost:8080;
}
but this code block tells Nginx there is a tcp server running on port 8080, represents it as websocketserver alias, but the running server is not accessible to public.
Check the below configuration,
server {
server_name wss.myapp.mobi;
listen 443;
ssl on;
ssl_certificate /etc/ssl/myapp-mobi-ssl.crt;
ssl_certificate_key /etc/ssl/myapp-mobi.key;
access_log /var/log/wss-access-ssl.log;
error_log /var/log/wss-error-ssl.log;
location / {
proxy_pass http://websocketserver;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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-Proto https;
proxy_read_timeout 86400; # neccessary to avoid websocket timeout disconnect
proxy_redirect off;
}
}
this configuration binds the domain wss.myapp.mobi to port 443 enabling ssl and proxying the requests to the local websocket server via proxy_pass directive, rest directives are for connection upgrades handling.
So the websocket server can be accessed from browser client with
// connect through binded domain
// instead of wss.myapp.mobi:8080 which will not work
var url = 'wss://wss.myapp.mobi';