Nginx: WebSocket wildcard location - nginx

I use a nginx instance in front of a Go service.
I want to redirect anything on port 80 to https. [done]
All (non-websocket) https requests at /* should go to https://localhost:8443/* [done]
All websocket https requests at /ws/* should go to https://localhost:8443/ws/* [missing]
My current config:
ssl_certificate ...
ssl_certificate_key ...
ssl_ciphers ...
ssl_prefer_server_ciphers on;
server {
listen 80;
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name www.mydomain.com mydomain.com;
add_header Strict-Transport-Security "max-age=31536000";
location /ws { <--- This only works for /ws but not /ws/app1
proxy_pass http://localhost:8443/ws;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / { <--- Catches anything, even without wildcard ?!
proxy_pass http://localhost:8443;
}
}
server {
listen 443 ssl;
server_name *.mydomain.com;
return 444;
}
Why is this necessary ? Well, as I understand, you have to set the upgrade headers explicitly, so I guess you have to specify another location.
Ideally, I would just use one location, but then websockets are blocked (because upgrade headers never make it to the Go service...)
I'm not a nginx expert, so bear with me =).
[EDIT]
I got it working now. I'm not sure if its ok to always set_header Upgrade/Connection, even if it's not a websocket request, but my Go service doesn't give a ****, so it works for me =]
ssl_certificate ...
ssl_certificate_key ...
ssl_ciphers ...
ssl_prefer_server_ciphers on;
server {
listen 80;
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name www.mydomain.com mydomain.com;
add_header Strict-Transport-Security "max-age=31536000";
location / { <--- Catches anything, even without wildcard ?!
proxy_pass http://localhost:8443;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
server {
listen 443 ssl;
server_name *.mydomain.com;
return 444;
}

Check out the article at https://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms
You are not using any location_match, so the match is a prefix match.
Use ~ as the location match modifier to have it interpreted as a regular expression.
The line location /ws should match every query starting with /ws.

Related

NGINX routing question - I've spent some hours on this to no luck yet

I've spent hours on multiple message boards and forums looking for this config pattern in NGINX. I want a subdomain to get redirected to port 8080 before the catch-all grabs everything else that comes into the domain, including all other subdomains, and points it at 443.
The 443 redirect already works perfectly. Any and all help would be appreciated thank you.
I have adminer.server.app that I want to goto HTTP://adminer.server.app (it really goes to 8080 after NGINX would do it's thing)
Again, all other traffic is taken care of so far.
Here is a snippet of my conf file:
server {
listen 80;
listen [::]:80;
server_name server.app www.server.app;
location / {
return 301 https://$host$request_uri;
}
#for certbot challenges (renewal process)
location ~ /.well-known/acme-challenge {
allow all;
root /data/letsencrypt;
}
}
#https://adminer.server.app
server {
listen 8080;
server_name adminer.server.app;
server_tokens off;
resolver 8.8.8.8;
location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
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;
}
}
#https://server.app
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name server.app;
server_tokens off;
ssl_certificate /etc/letsencrypt/live/certs/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/certs/privkey.pem;
ssl_buffer_size 8k;
ssl_dhparam /etc/ssl/certs/dhparam-2048.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
ssl_ecdh_curve secp384r1;
ssl_session_tickets off;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8;
return 301 https://www.server.app$request_uri;
}
I can bring the phpmyadmin up by using my IP:8080, so at the moment this isn't critical. I'm not going to expose it after I'm done configuring the MySQL anyway. Closing request for help.

Nginx configuration like Synology reverse proxy

I am trying configuring nginx (based on bitname/nginx:latest) as equivalent of Synology reverse proxy. This is due to missing wild-card redirect at Synology. While doing so, I face many issues; therfore I am requesting help for proper nginx configuration.
requirements
HTTPS upgrade
Redirect any wild-card subdomain (443) to a port 30'000
Hide the redirect port from user visibility
WebSockets must be supported (At Synology following header: Upgrade $http_upgrade AND Connection $connection_upgrade)
Example
Browser calls http://app1.my-example.com/
re-direct to https://app1.my-example.com:30000/
Browser displays: https://app1.my-example.com/, resolving via Port 30000
Current Code (not working so far)
# Test
server {
listen 8080;
server_name ~^(.*)\.my\-example.com$;
access_log /opt/bitnami/nginx/logs/yourapp_access.log;
error_log /opt/bitnami/nginx/logs/yourapp_error.log;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header HOST $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass https://$host:30000$request_uri/;
proxy_redirect off;
}
}
# Catch malicious requests
server {
listen 8080 default_server;
listen [::]:8080 default_server;
server_name _;
return 444;
}
I was able to solve my issue and would like to share the results. The only thing I do not get is, why redirect.my-example is OK as proxy_pass. It would hit the very same route (probably an endless-loop). Feedback/Improvement would be apreciated!
# custom code for hop by hop headers
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# Upgrade connection
server {
listen 8080 default_server;
listen [::]:8080 default_server;
server_name _;
return 301 https://$host$request_uri;
}
# Redirect Subdomains (incl. Web-Socket)
server {
listen 8443 ssl;
ssl_certificate /certs/server.crt;
ssl_certificate_key /certs/server.key;
server_name my-example.de portal.my-example.de;
access_log /opt/bitnami/nginx/logs/yourapp_access.log;
error_log /opt/bitnami/nginx/logs/yourapp_error.log;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header HOST $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_pass https://redirect.my-example.de:30000;
proxy_redirect off;
}
}
# Catch malicious requests
server {
listen 8443 default_server;
listen [::]:8443 default_server;
ssl_certificate /certs/server.crt;
ssl_certificate_key /certs/server.key;
server_name _;
return 444;
}

the redirect from http to https does not work in nginx

I am trying to redirect all the http traffic to https and my nginx conf looks like this:
upstream upstreamServer {
server upstream_serv:80;
}
server {
listen 80;
server_name ~^(([a-zA-Z0-9]+)|)test\.xy\.abc\.io$ ;
access_log /var/log/nginx/access.log backend;
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name ~^(([a-zA-Z0-9]+)|)test\.xy\.abc\.io$ ;
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
ssl_certificate /path/to/cert_chain.pem;
ssl_certificate_key /path/to/cert_key.pem;
ssl_trusted_certificate /path/to/cert_chain.pem;
access_log /var/log/nginx/access.log backend;
# Redirect all traffic in /.well-known/ to lets encrypt
location /.well-known/acme-challenge/ {
root /var/tmp;
index index.html index.htm;
}
location / {
proxy_pass http://upstreamServer;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_buffering off;
if ($uri ~* ".(js|png|jpg|jpeg|svg|gif|avi|mp3|mp4)$" ){
expires 1d;
add_header Cache-Control public;
}
proxy_pass_request_headers on;
}
}
But for some reason it doesn't work. I read about how the nginx chooses the server block and location block. The setup looks correct to me according to what I understand but still the site keeps loading on http when I hit the url http://test.xy.abc.io instead of redirecting me to https.
I also tried using only
return 301 https://$host$request_uri;
instead of
location / {
return 301 https://$host$request_uri;
}
but it doesn't work either.
Did I get right that your page is still loading the unencrypted http version? Did you reaload the service to load the changed config file? (sorry to ask that stupid question back)
nginx -t && nginx -s reload
I personally use in all nginx instances I maintain something like this:
server {
listen 80 default_server;
# no server_name means all
# For let's encrypt domains: .well-known/acme-challenge
location '/.well-known/acme-challenge' {
default_type "text/plain";
root /var/www/certbot;
}
# Redirect http -> https.
location / {
return 301 https://$host$request_uri$is_args$args;
}
}
The problem was there is a GCP loadbalancer before my nginx proxy. Which was forwarding all the requests on https to my nginx proxy no matter if the orignal reuquest was http or https. After searching the internet I found that loadbalancer can not force https on clients. So this what I had to do in my nginx location block.
if ($http_x_forwarded_proto = http) {
return 301 https://$host$request_uri;
}
and the complete solution looks like this:
server {
listen 80;
listen 443 ssl;
server_name ~^(([a-zA-Z0-9]+)|)test\.xy\.abc\.io$ ;
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
ssl_certificate /path/to/cert_chain.pem;
ssl_certificate_key /path/to/cert_key.pem;
ssl_trusted_certificate /path/to/cert_chain.pem;
access_log /var/log/nginx/access.log backend;
# Redirect all traffic in /.well-known/ to lets encrypt
location /.well-known/acme-challenge/ {
root /var/tmp;
index index.html index.htm;
}
location / {
if ($http_x_forwarded_proto = http) {
return 301 https://$host$request_uri;
}
proxy_pass http://upstreamServer;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_buffering off;
if ($uri ~* ".(js|png|jpg|jpeg|svg|gif|avi|mp3|mp4)$" ){
expires 1d;
add_header Cache-Control public;
}
proxy_pass_request_headers on;
}
}

Facebook has detected that proevent.am isn't using a secure connection to transfer information

I have an application with django and what to add login feature with facebook but receive 'Facebook has detected that proevent.am isn't using a secure connection to transfer information'.
Https is already enabled in my website. All my requests to http are being redirected to https with nginx. I use certificates from Let's Encrypt.
upstream proevent {
server web:8000; ## change to lp_web_1:8000
}
upstream ws_server {
server web:9000;
}
server {
listen 80;
server_name http_proevent;
location / {
return 301 https://$host$request_uri;
}
location /ws/ {
try_files $uri #proxy_to_ws;
}
location #proxy_to_ws {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_pass http://ws_server;
}
location /staticfiles/ {
alias /usr/src/app/staticfiles/;
}
location /mediafiles/ {
alias /usr/src/app/mediafiles/;
}
location /.well-known/acme-challenge/ {
allow all;
default_type text/plain;
root /var/www/certbot;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name https_proevent;
location / {
proxy_pass http://proevent; #for demo purposes
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
ssl_certificate /etc/letsencrypt/live/proevent.am/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/proevent.am/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
Until proevent.am updates its security settings, you won't be able to use Facebook to log in to it.

'conflicting server name' with two default/catchall locations for nginx - one with default_server, one without?

I'm at my wit's end here. I've been fighting an nginx configuration for hours. Here are the two blocks I'm trying to use:
server {
listen 80 default_server;
location /health-check {
default_type 'text/plain';
access_log off;
return 200;
}
}
server {
listen 80;
location / {
return 301 https://$http_host$request_uri;
}
}
# other application servers/upstreams follow -- one is provided here for completeness,
# although the issue is almost certainly above
upstream quinoa-icehouse {
server 172.17.8.100:49153;
}
server {
server_name ~^quinoa-icehouse\.(?<domain>.+)$;
server_name_in_redirect off;
port_in_redirect off;
listen 443 ssl spdy;
listen 80;
ssl_certificate /etc/ssl/deis.cert;
ssl_certificate_key /etc/ssl/deis.key;
location / {
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect off;
proxy_connect_timeout 30s;
proxy_send_timeout 1200s;
proxy_read_timeout 1200s;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_next_upstream error timeout http_502 http_503 http_504;
add_header X-Deis-Upstream $upstream_addr;
proxy_pass http://quinoa-icehouse;
}
}
Note that I want the /health-check endpoint to work only when other server names aren't matched, but I want the 301 to occur whenever a server name is matched.
I seem to have tried every combination of these directives, only to get:
[INFO] - 2014/12/30 01:26:34 [warn] 39#0: conflicting server name "" on 0.0.0.0:80, ignored
Is there a way for me to accomplish what I seek? Thank you!!
Essentially, you are going outside the Nginx' defined parameters. You cannot have two default server blocks sitting on each other so to speak.
You can however achieve what you need by defining:
A catch all block listening on Port 80 to redirect all requests to Port 443
A catch all block listening on Port 443 for all unmatched domains
You then need to ensure the following:
Drop the "default_server" directive and rely on the position of the server blocks as in the example answer
Ensure other application servers/upstreams only listen on Port 443.
So your config should be something along these lines:
http {
[ ... ]
# Default to redirect from Port 80 to Port 443
server {
listen 80;
return 301 https://$host$request_uri;
}
# Default for unmatched domains on Port 443
server {
listen 443 ssl spdy;
ssl_certificate /etc/ssl/someCert.cert;
ssl_certificate_key /etc/ssl/someKey.key;
# Return 403, 404 or 444
return 403;
}
# Other servers.
# 1. These must be below this for this configuration to work.
# 2. None should listen on Port 80
server {
server_name ABC
listen 443 ssl spdy;
ssl_certificate /etc/ssl/someCert.cert;
ssl_certificate_key /etc/ssl/someKey.key;
[ ... ]
}
server {
server_name XYZ
listen 443 ssl spdy;
ssl_certificate /etc/ssl/someCert.cert;
ssl_certificate_key /etc/ssl/someKey.key;
[ ... ]
}
}
Refer to: Why is nginx responding to any domain name?
Note also that for simple server blocks that will just return simple responses, you don't need to have location blocks.

Resources