Nginx as a WebSocket Proxy giving Invalid URL Prefix error - nginx-reverse-proxy

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.

Related

NGINX reverse proxy HTTPS routes non-functional despite certbot SSL

I have NGINX set up as a reverse proxy to serve a node server from a single Ubuntu 18.04 ec2 instance (NGINX and the node server are both on the same instance). Certbot successfully installed and configured and HTTP routes are coming through with no issue but when I try to hit an HTTPS endpoint I get ERR_CONNECTION_CLOSED on my client (which is hosted on GH-Pages but I don't think that's relevant?).
My ec2 instance is set up to accept all traffic on ports 80 and 443, my server is listening on port 3333.
Currently ufw is set to inactive but I have tried enabling it and allowing 'NGINX FULL'. The requests still failed in this scenario but I received a connection timeout error instead of connection closed.
NGINX error logs example output:
2020/05/13 23:17:23 [error] 13581#13581: *15 connect() failed (111: Connection refused) while connecting to upstream, client: 159.xxx.xxx.35, server: api.example.net, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:3333/", host: "54.xxx.xx.xxx:80"
My NGINX server blocks are as follows:
server {
listen 443 ssl;
server_name api.example.net www.api.example.net;
ssl_certificate /etc/letsencrypt/live/api.example.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.net/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
location / {
proxy_pass http://localhost:3333/;
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 X-Real-IP $remote_addr;
proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forward-Proto http;
proxy_set_header X-Nginx-Proxy true;
proxy_redirect off;
}
}
server {
listen 80;
server_name api.example.net www.api.example.net;
# return 301 https://$host$request_uri;
location / {
proxy_pass http://localhost:3333/;
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 X-Real-IP $remote_addr;
proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forward-Proto http;
proxy_set_header X-Nginx-Proxy true;
proxy_redirect off;
}
}
I've been googling for the last 18 hours and my brain is bleeding, any and all suggestions will be greatly appreciated.
I was able to connect to your server on https (you forgot to redact the domain in the error log). The server itself seems to work fine (I got the error Cannot GET /). I suspect your client on gh-pages get the ERR_CONNECTION_CLOSED error because CORS is not allowing it to talk to your server.
This question may be helpful: POST API call returns CORS error without https and ERR_CONNECTION_CLOSED without
If you want to allow CORS with nginx, then this may work (snippet from enable-cors.org). After you get it working, you should probably improve security by not allowing all origins.
#
# Wide-open CORS config for nginx
#
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
#
# Custom headers and headers various browsers *should* be OK with but aren't
#
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
#
# Tell client that this pre-flight info is valid for 20 days
#
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
}
}
I hope this helps!

Running Apache Zeppelin with nginx as reverse proxy

In our current architecture we have two apache front servers, in front of them, we have an nginx load balancer. And in front of that an nginx reverse proxy.
My problem is that i'm trying to run Apache Zeppelin through the reverse proxy, and i'm having some problems with the websockets.
I get an error like this : 400 HTTP method GET is not supported by this URL
And here is a screenshot of what the Chrome's Networks tab shows :
I add my reverse proxy config for Zeppelin:
error_log /var/log/nginx/nginx_error.log warn;
server {
listen 80;
server_name localhost;
location /zeppelin/ {
proxy_pass http://zeppelin:8080/;
proxy_http_version 1.1;
proxy_set_header Upgrade websocket;
proxy_set_header Connection upgrade;
}
# fallback
location / {
return 301 http://ci.blablalablab.com/app/;
}
}
Zeppelin is running inside a docker container, and i have exposes the 8080 port, its host name is : zeppelin.
If you have any questions on the architecture or so, don't hesitate to ask.
Thank you very much guys !
you can add to your reverse proxy configuration
location /ws { # For websocket support
proxy_pass http://zeppelin:8080/ws;
proxy_http_version 1.1;
proxy_set_header Upgrade websocket;
proxy_set_header Connection upgrade;
proxy_read_timeout 86400;
}
Reference: Zeppelin 0.7 auth docs
After a lot of digging around, i ended up with this configuration :
location /zeppelin/ {
proxy_pass http://zeppelin:8080/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_redirect off;
}
location /zeppelin/ws {
proxy_pass http://zeppelin:8080/ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
This is working pretty good, thank you everyone for your efforts ;)

nginx+socket.io+ssl: WebSocket connection to 'ws://wechat.pageguy.tech/socket.io/?EIO=3&transport=websocket' failed: Unexpected response code: 302

I want to configure an node.js project using socket.io through nginx with ssl,But I failed,when I start it an error occours like this:
I spent a whole noon search and read documents but still can not figure out how to solve this..If anyone meet this problem before or have any idea about it please help me,thanks a lot.
This is my client side js code:
let socket = io.connect('//wechat.pageguy.tech/ws', {transports: ['websocket']});
This is nginx code:
server {
listen 443;
server_name wechat.pageguy.tech;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
#charset koi8-r;
#access_log /var/log/nginx/log/host.access.log main;
ssl on;
ssl_certificate /etc/ssl/private/pageguy.crt;
ssl_certificate_key /etc/ssl/private/pageguy.key.unsecure;
location / {
#root /usr/share/nginx/html;
#index index.html index.htm;
proxy_pass http://localhost:3335;
}
location /ws/ {
proxy_pass http://localhost:3005;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
}
}
My express port is 3335,my socket.io port is 3005.Everything were fine before I added ssl in the nginx configuration file.
Nobody help me 555, but I solved problems by myself.
in client side js code:
let socket = io.connect('https//wechat.pageguy.tech/', {transports: ['websocket']});
in nginx configure file:
location /socket.io/(not /ws/ because its default value)

neo4j webinterface behind nginx reverse proxy

I'm trying to expose a neo4j database to the internet.
For security reasons, I'd like to hide it behind a SSL/basic_auth combination via nginx. Here is the corresponding nginx config:
location /neo4j/ {
proxy_pass https://localhost:7473/;
proxy_read_timeout 600;
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_set_header Host $http_host;
proxy_buffering off;
proxy_redirect off;
auth_basic "restricted";
auth_basic_user_file /etc/nginx/auth/htpasswd;
proxy_headers_hash_max_size 1024;
proxy_headers_hash_bucket_size 128;
proxy_ssl_session_reuse off;
rewrite /neo4j/(.*) /$1 break;
}
While I'm able to access https://example.com/neo4j/browser, the webinterface tells me, that it can't connect to the neo4j and my webbrowser's console gets filled up with OPTIONS https://example.com/db/data 405(Not allowed)
I also tried the neo4j built in https server in conjunction with the authentication extension (https://github.com/neo4j-contrib/authentication-extension).
With this option, I also can access the webinterface.
But the interface also displays, that it's not able to connect to the neo4j and the webbrowser's console gets filled up with OPTIONS http://example.com:7473/db/data/ net::ERR_EMPTY_RESPONSE and the hint The page at 'https://example.com:7473/browser/' was loaded over HTTPS, but displayed insecure content from 'http://example.com:7473/db/data/': this content should also be loaded over HTTPS.
Does anyone know, how to get it working? Many thanks in advance!
I came across the same problem and it's kind of weird the lack of info about Nginx as a webserver combined with neo4j. It's odd the only reference to a reverse proxy in the official doc is Apache - not impressed.
Just FYI I'm using a dockerised neo4j (https://github.com/neo4j/docker-neo4j/tree/master/2.3.2) as it comes by default (in case you want to know other settings). It should not matter if you run neo4j natively outside docker. The following Nginx conf will be the same.
location /neo4j/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_buffering off;
proxy_pass http://YOUR-IP:7474/browser/;
}
location /db/data/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_buffering off;
proxy_pass http://YOUR-IP:7474/db/data/;
}
Replace YOUR-IP by yours and change 7474 for 7473 if you are using HTTPS instead of HTTP.
This worked for me.
The OPTIONS request is needed to verify the connection to the Neo4j server. I think of it as a heartbeat to verify the connection. It seems that Nginx does not support OPTIONS requests, but the request can be intercepted with something like this:
location / {
if ($request_method = OPTIONS ) {
add_header Access-Control-Allow-Origin "https://example.com";
add_header Access-Control-Allow-Methods "GET, OPTIONS";
add_header Access-Control-Allow-Headers "Authorization";
add_header Access-Control-Allow-Credentials "true";
add_header Content-Length 0;
add_header Content-Type text/plain;
return 200;
}
}
Source: http://blog.rogeriopvl.com/archives/nginx-and-the-http-options-method/

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';