Make Nginx Proxy only upgrade SignalR WebSocket connections in Docker Compose stack - docker

Is there a way to only upgrade SignalR WebSocket connections in your nginx.conf file without duplicating a whole section?
ASP.NET Core is rejecting my SignalR connections when they're not upgraded, however I'm running an API on that ASP.NET Core project that won't accept POST Request with bodies when the requests have the header "Connection: "upgrade"".
My current solution is just to duplicate the "location" section in my nginx.conf file as you see below. Is there a more elegant solution?
Thanks!
My nginx.conf file:
worker_processes 1;
events {
worker_connections 1024;
}
http {
server {
listen 80;
include /etc/nginx/mime.types;
gzip on;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
location / {
proxy_pass http://ui:80;
rewrite ^/(.*)$ /$1 break;
}
location /shopvac/signalr {
proxy_pass http://shopvac:80;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
rewrite ^/shopvac/(.*)$ /$1 break;
}
location /shopvac {
proxy_pass http://shopvac:80;
rewrite ^/shopvac/(.*)$ /$1 break;
}
}
}
My docker-compose.yaml file:
version: "3"
networks:
proxy:
database:
volumes:
database-data:
services:
proxy:
image: nginx:latest
depends_on:
- ui
- shopvac
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./logs/nginx:/var/log/nginx
ports:
- "80:80"
- "443:443"
networks:
- proxy
database:
image: postgres:latest
volumes:
- database-data:/var/lib/postgresql/data
expose:
- "5432"
networks:
- database
environment:
- POSTGRES_DB=home-system
- POSTGRES_USER=home-system
ui:
build:
context: ./ui
dockerfile: Dockerfile
expose:
- "80"
networks:
- proxy
shopvac:
build:
context: ./shopvac
dockerfile: Dockerfile
depends_on:
- database
volumes:
- /app
expose:
- "8080"
networks:
- proxy
- database

Found a solution!
So you can edit the "proxy_set_header Connection" property in nginx.conf to pass the necessary value forward.
The important section of the nginx.conf is now as follows:
location /shopvac {
proxy_pass http://shopvac:80;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
rewrite ^/shopvac/(.*)$ /$1 break;
}

Related

how to change the public static folder route

I was to access my css with nginx from public folder it works totally fine when i open
localhost:8080/css/main
but i want to access it from my express server running on 3000, 8080 is just web pack building this public folder and updating it on change so i want to access this folder with my express running on 3000 not on webpack on 8080
localhost:3000/css/main
i tried couple of things but didn't work so i didn't want to mess more with the file
upstream backend {
server dev-node:3000;
}
upstream webpack {
server dev-webpack:8080;
}
server {
listen 80;
server_name localhost;
root /app/public
#access_log /var/log/nginx/host.access.log main;
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires 1d;
}
location ^~ /(audio|build|images|style) {
autoindex on;
root /app/public;
}
location /api {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location / {
proxy_pass http://webpack;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location ~ /\.ht {
deny all;
}
}
docker file
version: '3'
services:
dev-nginx:
build:
context: ./nginx
container_name: dev-nginx
links:
- dev-node
ports:
- "80:80"
- "443:443"
volumes:
- ./client/public:/usr/share/nginx/html
dev-webpack:
build:
context: ./client
container_name: dev-webpack
volumes:
- ./client:/app
- /app/node_modules
ports:
- "8080:8080"
command: "yarn dev"
dev-node:
build:
context: ./server
container_name: dev-node
environment:
- NODE_ENV=production
- PORT=3000
volumes:
- ./server:/app
- /app/node_modules
ports:
- "3000:3000"
command: "yarn dev"

NGINX/Docker-Compose/Phoenix

I'm creating an application with docker-compose, vue.js, and phoenix/elixir. So far the phoenix application will work on localhost, but will not work when I run the application using docker-compose + NGINX and it's been difficult to debug. Any advice or suggestions would be helpful. The phoenix application itself does not have any of the configuration options changed from the "hello world" application and only adds in socket functionality for a chat room.
Here is the docker-compose file:
version: "3.9"
networks:
main_network:
volumes:
volume1:
varwwwcertbot:
certbotconf:
data_volume:
services:
phx:
build:
context: ./phx
restart: always
volumes:
- ./phx:/phx
ports:
- "4000:4000"
depends_on:
db:
condition: service_healthy
networks:
- main_network
db:
image: postgres
restart: always
volumes:
- ./docker-entrypoint-initdb.d/init.sql:/docker-entrypoint-initdb.d/init.sql
- data_volume:/var/lib/postgresql/data
environment:
- POSTGRES_NAME=dev-postgres
- POSTGRES_USER=pixel
- POSTGRES_DATABASE=lightchan
- POSTGRES_PASSWORD=exploration
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U pixel"]
interval: 5s
timeout: 5s
retries: 20
networks:
- main_network
frontend:
build:
context: ./frontend
restart: always
volumes:
- './frontend:/app'
- '/app/node_modules'
ports:
- "3000:3000"
networks:
- main_network
depends_on:
- "phx"
nginx:
build:
context: ./nginx
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- volume1:/usr/share/nginx/html
- varwwwcertbot:/var/www/certbot
- certbotconf:/etc/letsencrypt
networks:
- main_network
certbot:
image: certbot/certbot:latest
volumes:
- varwwwcertbot:/var/www/certbot
- certbotconf:/etc/letsencrypt
networks:
- main_network
Here is the nginx file:
events{
}
http{
map $http_upgrade $connection_upgrade {
default Upgrade;
'' close;
}
upstream websocket {
server 164.92.157.124:4000;
}
server {
listen 80;
server_name localhost lightchan.org www.lightchan.org;
root /usr/share/nginx/html;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
if ($scheme = http) {
return 301 https://lightchan.org$request_uri;
}
}
server {
listen 443 default_server ssl http2;
listen [::]:443 ssl http2;
server_name localhost lightchan.org www.lightchan.org;
ssl_certificate /etc/letsencrypt/live/lightchan.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/lightchan.org/privkey.pem;
location /media/pics/ {
autoindex on;
}
location / {
proxy_pass http://frontend:3000;
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 Host $http_host;
proxy_intercept_errors on;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location ^~ /socket/ {
proxy_pass http://websocket;
add_header X-uri "$uri";
proxy_http_version 1.1;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "upgrade";
}
location ^~ /api/ {
proxy_pass http://phx:4000;
add_header X-uri "$uri";
proxy_http_version 1.1;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "upgrade";
}
}
}
Note here that I attempted to proxy through an upstream for a /socket/ config and also attempted to use /api/ just using the phx docker tag. Both frontend and phx are linked in the docker-compose file, so they should be coming through. I've also turned on the 'upgrade' feature of Connection so this should be forwarding websockets too.
The phoenix application itself, still has the / front page uri, so I would suspect that being able to navigate to either www.lightchan.org/api or www.lightchan.org/socket would then allow me to see the phoenix hello world splash page, but instead there's a 502 error.
Any suggestions?
EDIT:
I've edited config/config.exs in the phoenix application to run on host 127.0.0.1 like this:
# Configures the endpoint
config :my_app, MyAppWeb.Endpoint,
url: [host: "127.0.0.1"],
render_errors: [view: MyAppWeb.ErrorView, accepts: ~w(html json), layout: false],
pubsub_server: MyApp.PubSub,
live_view: [signing_salt: "blah"]
and tested this running on locally, and it works, correctly showing the splash page at /. I've attempted to get this to work using http://<SERVER IP>:4000 on my server, but no luck.
EDIT EDIT:
Frustratingly, my autoindex for /media/pics is not working, but rather routing to the the frontend. Here is an embarrassingly thorough guide on routing priorities in NGINX (Guide on how to use regex in Nginx location block section?) and here (Nginx location priority). According to the second link:
location ^~ /images/ {
# matches any query beginning with /images/ and halts searching,
# so regular expressions will not be checked.
[ configuration D ]
}
should mean that
location ^~ /media/pics/ {
autoindex on;
}
location / {
<...>
should stop searching and the return an autoindex. So https://www.lightchan.org/media/pics/myimage.png should return myimage.png. That doesn't work and neither does
location /media/pics/ {
autoindex on;
}
Although I could have sworn it was working before...hmm.
Thanks to GreenMachine on Discord the answer was found here:
https://dev.to/eikooc/using-docker-for-your-elixir-phoenix-application-n8n
change dev.exs in config:
# from this:
http: [ip: {127, 0, 0, 1}, port: 4000],
# to this:
http: [ip: {0, 0, 0, 0}, port: 4000],

Issue with Docker-compose and nginx

Please assist
I'm trying to run both react.js and nest.js on http://localhost:3000 with docker-compose and Nginx, however
my react.js app isn't binding correctly. When I visit the link, I only see the nginx welcome page.
This is my docker-compose.yaml
version: "3.6"
services:
database:
image: postgres:13.1-alpine
env_file:
- ./database/.env
volumes:
- "db-data:/var/lib/postgresql/data"
networks:
- challenge
ports:
- "5432:5432"
backend:
build:
context: $PWD/../../backend
dockerfile: $PWD/backend/Dockerfile
volumes:
- ./backend/.env:/app/.env
- ../../backend/src:/app/src
- storage:/app/storage
ports:
- 3000
networks:
- challenge
depends_on:
- database
env_file:
- ./backend/.env
environment:
- FORCE_COLOR=1
frontend:
build:
context: $PWD/../../web
dockerfile: $PWD/frontend/Dockerfile
ports:
- 3001
networks:
- challenge
depends_on:
- backend
env_file:
- ./frontend/.env
volumes:
- ../../web/src:/app/frontend/src
nginx:
image: nginx
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
ports:
- 3000:80
depends_on:
- backend
- frontend
networks:
- challenge
volumes:
db-data:
storage:
networks:
challenge:
And this is my Dockerfile for the React
FROM node:14.18.1-alpine3.14 as build
WORKDIR /app/frontend
COPY package.json /app/frontend/
COPY yarn.lock /app/frontend/
RUN yarn install --frozen-lockfile
COPY . /app/frontend
RUN yarn run build
FROM nginx:1.21.3-alpine
COPY --from=build /app/frontend/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Also, this is my nginx.conf
events {
worker_connections 1024;
}
http {
client_max_body_size 1000M;
server {
gzip on;
gzip_disable "msie6";
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
listen 80;
server_name localhost;
location /api/v1 {
proxy_pass http://backend:3000;
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-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_cache_bypass $http_upgrade;
}
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}
Please assist as I've tried changing the ports, and exposing different ports but that doesn't seem to work.
You have two nginx servers: one inside frontend service and another one inside nginx service. Is it intended?
I suppose you need to access the nginx service provided by frontend so in that case you need to go to:
http://localhost:3001
and map 3001:80 because 80 is the exposed port in that image.

jwilder/nginx-proxy: no access to virtual host

I have a NAS behind a router. On this NAS I want to run for testing Nextcloud and Seafile together. Everything should be set up with docker. The jwilder/nginx-proxy container does no work as expected and I cannot find helpful information. I feel I am missing something very basic.
What is working:
I have a noip.com DynDNS that points to my routers ip: blabla.ddns.net
The router forwards ports 22, 80 and 443 to my NAS at 192.168.1.11
A plain nginx server running on the NAS can be accessed via blabla.ddns.net, its docker-compose.yml is this:
version: '2'
services:
nginxnextcloud:
container_name: nginxnextcloud
image: nginx
restart: always
ports:
- "80:80"
networks:
- web
networks:
web:
external: true
What is not working:
The same nginxserver like above behind the nginx-proxy. I cannot access this server. Calling blabla.ddns.net gives a 503 error, calling nextcloud.blabla.ddns.net gives "page not found". Viewing the logs of the nginx-proxy via docker logs -f nginxproxy logs every test with blabla.ddns.net and shows its 503 answer, but when I try to access nextcloud.blabla.ddns.net not even a log entry occurs.
This is the docker-compose.yml for one nginx behind a nginx-proxy:
version: '2'
services:
nginxnextcloud:
container_name: nginxnextcloud
image: nginx
restart: always
expose:
- 80
networks:
- web
environment:
- VIRTUAL_HOST=nextcloud.blabla.ddns.net
nginx-proxy:
image: jwilder/nginx-proxy
container_name: nginxproxy
ports:
- "80:80"
volumes:
- /var/run/docker.sock:/tmp/docker.sock
networks:
- web
networks:
web:
external: true
The generated configuration file for nginx-proxy /etc/nginx/conf.d/default.conf contains entries for my test server:
# If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the
# scheme used to connect to this server
map $http_x_forwarded_proto $proxy_x_forwarded_proto {
default $http_x_forwarded_proto;
'' $scheme;
}
# If we receive X-Forwarded-Port, pass it through; otherwise, pass along the
# server port the client connected to
map $http_x_forwarded_port $proxy_x_forwarded_port {
default $http_x_forwarded_port;
'' $server_port;
}
# If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any
# Connection header that may have been passed to this server
map $http_upgrade $proxy_connection {
default upgrade;
'' close;
}
# Apply fix for very long server names
server_names_hash_bucket_size 128;
# Default dhparam
ssl_dhparam /etc/nginx/dhparam/dhparam.pem;
# Set appropriate X-Forwarded-Ssl header
map $scheme $proxy_x_forwarded_ssl {
default off;
https on;
}
gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
log_format vhost '$host $remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
access_log off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers off;
resolver 127.0.0.11;
# HTTP 1.1 support
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $proxy_connection;
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 $proxy_x_forwarded_proto;
proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl;
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
# Mitigate httpoxy attack (see README for details)
proxy_set_header Proxy "";
server {
server_name _; # This is just an invalid value which will never trigger on a real hostname.
listen 80;
access_log /var/log/nginx/access.log vhost;
return 503;
}
# nextcloud.blabla.ddns.net
upstream nextcloud.blabla.ddns.net {
## Can be connected with "web" network
# nginxnextcloud
server 172.22.0.2:80;
}
server {
server_name nextcloud.blabla.ddns.net;
listen 80 ;
access_log /var/log/nginx/access.log vhost;
location / {
proxy_pass http://nextcloud.blabla.ddns.net;
}
}
Why is this minimal example not working?

How can I configure IdentityServer4 (DotNet Core) to work in Nginx reverse proxy

I've published my API, ID server STS and web ui on separate docker containers and I'm using a nginx container to act as the reverse proxy to serve these app. I can browse to each one of them and even open the discovery endpoint for the STS. Problem comes when I try to login into the web portal, it tries to redirect me back to the STS for logging in but I'm getting ERR_CONNECTION_REFUSED the url looks okay I think it's the STS that is not available from the redirection from the Web UI.
My docker-compose is as below:
version: '3.4'
services:
reverseproxy:
container_name: reverseproxy
image: nginx:alpine
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./proxy.conf:/etc/nginx/proxy.conf
- ./cert:/etc/nginx
ports:
- 8080:8080
- 8081:8081
- 8082:8082
- 443:443
restart: always
links:
- sts
sts:
container_name: sts
image: idsvrsts:latest
links:
- localdb
expose:
- "8080"
kernel:
container_name: kernel
image: kernel_api:latest
depends_on:
- localdb
links:
- localdb
portal:
container_name: portal
image: webportal:latest
environment:
- TZ=Europe/Moscow
depends_on:
- localdb
- sts
- kernel
- reverseproxy
localdb:
image: mcr.microsoft.com/mssql/server
container_name: localdb
environment:
- 'MSSQL_SA_PASSWORD=password'
- 'ACCEPT_EULA=Y'
- TZ=Europe/Moscow
ports:
- "1433:1433"
volumes:
- "sqldatabasevolume:/var/opt/mssql/data/"
volumes:
sqldata:
And this is the nginx.config:
worker_processes 1;
events { worker_connections 1024; }
http {
sendfile on;
upstream docker-sts {
server sts:8080;
}
upstream docker-kernel {
server kernel:8081;
}
upstream docker-portal {
server portal:8081;
}
ssl_ciphers EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_certificate cert.pem;
ssl_certificate_key key.pem;
ssl_password_file global.pass;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_cache_bypass $http_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-Host $server_name;
proxy_set_header X-Forwarded-Proto $scheme;
server {
listen 8080;
listen [::]:8080;
server_name sts;
location / {
proxy_pass http://docker-sts;
# proxy_redirect off;
}
}
server {
listen 8081;
listen [::]:8081;
server_name kernel;
location / {
proxy_pass http://docker-kernel;
}
}
server {
listen 8082;
listen [::]:8082;
server_name portal;
location / {
proxy_pass http://docker-portal;
}
}
}
The web ui redirects to the below url, which works okay if I browse to it using the STS server without nginx.
http://localhost/connect/authorize?client_id=myclient.id&redirect_uri=http%3A%2F%2Flocalhost%3A22983%2Fstatic%2Fcallback.html&response_type=id_token%20token&scope=openid%20profile%20kernel.api&state=f919149753884cb1b8f2b907265dfb8f&nonce=77806d692a874244bdbb12db5be40735
Found the issue. The containers could not see each other because nginx was not appending the port on the url.
I changed this:
'proxy_set_header Host $host;'
To this:
'proxy_set_header Host $host:$server_port;'

Resources