I'm trying to use Nginx for a reverse proxy. I have an Angular application running on Node server on port 4200. I am using a Docker image for the deployment
Below is the Dockerfile configuration:
FROM node:12.16.3-alpine as builder
RUN mkdir /app
WORKDIR /app
COPY package*.json ./
RUN npm install && npm install node-sass
COPY . .
RUN npm run build:ssr --prod --output-path=dist
EXPOSE 4200
CMD [ "node", "dist/server.js" ]
FROM nginx:alpine
COPY ./.nginx/nginx.conf /etc/nginx/nginx.conf
RUN rm -rf /usr/share/nginx/html/*
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80 8080
ENTRYPOINT ["nginx", "-g", "daemon off;"]
I have the Nginx configuration as below:
worker_processes 4;
events { worker_connections 1024; }
http {
server {
listen 0.0.0.0:8080;
listen [::]:8080;
listen 127.0.0.1;
server_name localhost;
default_type application/octet-stream;
gzip on;
gzip_comp_level 6;
gzip_vary on;
gzip_min_length 1000;
gzip_proxied any;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
gzip_buffers 16 8k;
gunzip on;
client_max_body_size 256M;
root /usr/share/nginx/html;
autoindex on;
location / {
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;
proxy_pass http://127.0.0.1:4200;
}
}
}
When I am running the image in the Docker container using the command
docker run --name application -d -p 8080:8080 app
I am getting the below error:
*5 connect() failed (111: Connection refused) while connecting to upstream, client: xxx.xx.0.1, server: localhost, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:4200/", host: "localhost:8080" xxx.xxx.0.1 - - [14/Mar/2021:12:41:24 +0000] "GET / HTTP/1.1" 502 559 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36"
Related
What I'm trying to do is to deploy a dockerized monorepo project (using NX as the monorepo framework) with the Nestjs + React + MySQL + Nginx stack on a VPS. I want the nginx proxy to listen on the host's port 88 (because another stack uses port 80, it's an old stack I do not dare touch). The OS of the VPS is CentOS 7.
I'll try to spare most of the details of the builds (Dockerfile) but know that the builds work, it is all working in my local environment (mostly due to the fact that I dont use nginx-proxy for local development) and I know it's either a matter of my Docker configs (I use docker-compose) or the host's networking that comes into play.
Here's a 'bird's eye view' of the stack:
React-frontend container is running a react app (using nx serve react-frontend) on port 4200 in the container, exposing port 4200 to the host
backend-api container is running a nodejs app (using nodejs entrypoint) on port 3333 of the container, exposing the port to the host
a MySQL container running a mysql server running on port 3306 of the container, exposed on port 3307 of the Host
A nginx-proxy using the jwilder/nginx-proxy docker image (I also tried with nginxproxy/nginx-proxy docker image) listening on port 88 of the host and redirecting the request to react-frontend container through proxy pass (this is the part that I'm failing at).
So here's my "compose-prod.yml" docker-compose file:
version: "3.7"
networks:
corp:
driver: bridge
nginx-proxy:
external:
name: nginx-proxy
volumes:
backend-db-volume:
driver: local
services:
nginx-proxy:
image: jwilder/nginx-proxy # also tried nginxproxy/nginx-proxy image
container_name: nginx-proxy
networks:
- corp
- nginx-proxy
environment:
HTTP_PORT: 88
ports:
- "88:88" # also tried "88:80" but that gives me "connection refused" in the browser
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
backend-db:
image: backend-db
hostname: backend-db
restart: unless-stopped
volumes:
- backend-db-volume:/var/lib/mysql
networks:
- corp
build:
context: ./apps/backend-db
dockerfile: ./Dockerfile
ports:
- 3307:3306
expose:
- 3306
backend-api:
container_name: backend-api
depends_on:
- backend-db
build:
context: ./
cache_from:
- base-image:nx-base
dockerfile: ./apps/backend-api/Dockerfile
args:
NODE_ENV: "production"
BUILD_FLAG: ""
image: backend-api:nx-dev
ports:
- "3333:3333"
environment:
NODE_ENV: "production"
PORT: 3333
[... other env configs ommitted, like DB variables, etc.]
networks:
- corp
restart: on-failure
react-frontend:
container_name: react-frontend
build:
context: ./
dockerfile: ./apps/react-frontend/Dockerfile
args:
NODE_ENV: "production"
BUILD_FLAG: ""
image: react-frontend:nx-dev
environment:
VIRTUAL_HOST: react-frontend # note that my domain is react-frontend.com, obfuscated ofc ... which I also tried using in VIRTUAL_HOST config
VIRTUAL_PORT: 4200
NGINX_PROXY_CONTAINER: nginx-proxy
NODE_ENV: "production"
[...other env configs ommitted]
ports:
- "4200:4200"
expose:
- 4200
networks:
- nginx-proxy
- corp
restart: on-failure
The nginx-proxy container automatically detects containers running with VIRTUAL_HOST env. variable enabled, generates configs for those from the compose-prod.yml file. Right now, the configuration generated, that I get using the "docker exec nginx-proxy cat /etc/nginx/conf.d/default.conf" command is this:
# nginx-proxy version : 1.0.1-6-gc4ad18f
# 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 based on $proxy_x_forwarded_proto
map $proxy_x_forwarded_proto $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" '
'"$upstream_addr"';
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;
error_log /dev/stderr;
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;
proxy_set_header X-Original-URI $request_uri;
# 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.
server_tokens off;
listen 88;
access_log /var/log/nginx/access.log vhost;
return 503;
}
# react-frontend
upstream react-frontend {
## Can be connected with "react-frontend_corp" network
# react-frontend
server <IP of react-frontend container on Docker network>:4200;
# Cannot connect to network 'nginx-proxy' of this container
# Cannot connect to network 'react-frontend_corp' of this container
## Can be connected with "nginx-proxy" network
# react-frontend
server <IP of react-frontend container on Docker network>:4200;
}
server {
server_name react-frontend;
listen 88 ;
access_log /var/log/nginx/access.log vhost;
location / {
proxy_pass http://react-frontend;
}
}
When I access "example.com:88" I get a "503 Service Temporarily Unavailable" page in my browser returned from nginx and I see this in nginx's access logs:
nginx-proxy | nginx.1 | example.com xx.yy.zz.ip - - [06/Jul/2022:16:48:12 +0000] "GET / HTTP/1.1" 503 592 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36" "-"
nginx-proxy | nginx.1 | example.com xx.yy.zz.ip - - [06/Jul/2022:16:48:12 +0000] "GET /favicon.ico HTTP/1.1" 503 592 "http://example.com:88/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36" "-"
I omit the Dockerfiles since the nginx-proxy container is not built, it's taken as is from the image and all the builds work ... it's the deployment which gives me trouble.
Anyone who has any pointer on what I'm missing ? What I should check ? This is for a personal project and even though I can get around as a devop, Docker networking/deployment still baffles me sometimes.
EDIT: I'm adding the VPS (host) vhost config (nginx) here ... so maybe I can proxy-pass using this configuration ... how would I go about modifying this config so I can proxy pass requests to the "example" docker container exposing port 4200 (instead of a root directory on the VPS) ?
# configuration file /etc/nginx/conf.d/users/example.conf:
proxy_cache_path /var/cache/ea-nginx/proxy/example levels=1:2 keys_zone=example:10m inactive=60m;
#### main domain for example ##
server {
server_name example.com www.example.com mail.example.com;
listen 80;
listen [::]:80;
include conf.d/includes-optional/cloudflare.conf;
set $CPANEL_APACHE_PROXY_PASS $scheme://apache_backend_${scheme}_51_222_24_216;
# For includes:
set $CPANEL_APACHE_PROXY_IP 51.222.24.216;
set $CPANEL_APACHE_PROXY_SSL_IP 51.222.24.216;
set $CPANEL_PROXY_CACHE example;
set $CPANEL_SKIP_PROXY_CACHING 0;
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /var/cpanel/ssl/apache_tls/example.com/combined;
ssl_certificate_key /var/cpanel/ssl/apache_tls/example.com/combined;
ssl_protocols TLSv1.2 TLSv1.3;
proxy_ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
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:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256;
proxy_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:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256;
root /home/example/public_html;
location /cpanelwebcall {
include conf.d/includes-optional/cpanel-proxy.conf;
proxy_pass http://127.0.0.1:2082/cpanelwebcall;
}
location /Microsoft-Server-ActiveSync {
include conf.d/includes-optional/cpanel-proxy.conf;
proxy_pass http://127.0.0.1:2090/Microsoft-Server-ActiveSync;
}
location = /favicon.ico {
allow all;
log_not_found off;
access_log off;
include conf.d/includes-optional/cpanel-proxy.conf;
proxy_pass $CPANEL_APACHE_PROXY_PASS;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
include conf.d/includes-optional/cpanel-proxy.conf;
proxy_pass $CPANEL_APACHE_PROXY_PASS;
}
location / {
proxy_cache $CPANEL_PROXY_CACHE;
proxy_no_cache $CPANEL_SKIP_PROXY_CACHING;
proxy_cache_bypass $CPANEL_SKIP_PROXY_CACHING;
proxy_cache_valid 200 301 302 60m;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout http_429 http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
proxy_cache_revalidate on;
proxy_cache_min_uses 1;
proxy_cache_lock on;
include conf.d/includes-optional/cpanel-proxy.conf;
proxy_pass $CPANEL_APACHE_PROXY_PASS;
}
include conf.d/server-includes/*.conf;
include conf.d/users/example/*.conf;
include conf.d/users/example/example.com/*.conf;
}
server {
listen 80;
listen [::]:80;
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /var/cpanel/ssl/apache_tls/example.com/combined;
ssl_certificate_key /var/cpanel/ssl/apache_tls/example.com/combined;
server_name cpanel.example.com cpcalendars.example.com cpcontacts.example.com webdisk.example.com webmail.example.com;
include conf.d/includes-optional/cloudflare.conf;
set $CPANEL_APACHE_PROXY_PASS $scheme://apache_backend_${scheme}_51_222_24_216;
# For includes:
set $CPANEL_APACHE_PROXY_IP 51.222.24.216;
set $CPANEL_APACHE_PROXY_SSL_IP 51.222.24.216;
location /.well-known/cpanel-dcv {
root /home/example/public_html;
disable_symlinks if_not_owner;
}
location /.well-known/pki-validation {
root /home/example/public_html;
disable_symlinks if_not_owner;
}
location /.well-known/acme-challenge {
root /home/example/public_html;
disable_symlinks if_not_owner;
}
location / {
# Force https for service subdomains
if ($scheme = http) {
return 301 https://$host$request_uri;
}
# no cache
proxy_cache off;
proxy_no_cache 1;
proxy_cache_bypass 1;
# pass to Apache
include conf.d/includes-optional/cpanel-proxy.conf;
proxy_pass $CPANEL_APACHE_PROXY_PASS;
}
}
If you want to access your app using example.com or www.example.com you have to set the server_name example.com *.example.com;. You can reach the docker container using DNS too while using docker-compose, in your case backend-api:3333 or react-frontend:4200. There are few correction in your configs.
server {
server_name _; # This is just an invalid value which will never trigger on a real hostname.
server_tokens off;
listen 88;
access_log /var/log/nginx/access.log vhost;
return 503;
}
# react-frontend
upstream frontends {
server react-frontend:4200;
server react-frontend:4200;
}
upstream backends {
server backend-api:3333;
server backend-api:3333;
}
server {
server_name 127.0.0.1 example.com *.example.com;
listen 88;
access_log /var/log/nginx/access.log vhost;
location / {
proxy_pass http://frontends;
}
# if required , only then use it otherwise remove it [for direct api calls]
location /api/v1 {
proxy_pass http://backends;
}
}
One can add more options or configs , according to the requirements. We are seeing 503 default page because of server_name _ configs in the above snippet. One can configure it , if needed. (like below snippet)
server {
server_name _; # This is just an invalid value which will never trigger on a real hostname.
server_tokens off;
listen 88;
access_log /var/log/nginx/access.log vhost;
return 403 "..Ops";
}
Not sure, why two networks are required, if you are running everything in one compose file.
If two networks are required, below is the example for docker-compose file and nginx.conf.
docker-compose.yaml
version: "3.7"
networks:
corp:
driver: bridge
nginx-proxy:
external:
name: nginx-proxy
services:
nginx-proxy:
container_name: nginx-proxy
image: nginx
ports:
- 3210:80
networks:
- nginx-proxy
- corp
volumes:
- another-nginx.conf:/etc/nginx/conf.d/another-nginx.conf
react-frontend:
container_name: react-frontend
image: httpd
ports:
- 3211:80
networks:
- nginx-proxy
- corp
backend-x:
container_name: backend
image: nginx
ports:
- 3212:80
networks:
- corp
db-x:
container_name: db
image: httpd
ports:
- 3213:80
networks:
- corp
another-nginx.conf
server {
server_name _; # This is just an invalid value which will never trigger on a real hostname.
server_tokens off;
listen 88;
access_log /var/log/nginx/access.log;
return 503;
}
# react-frontend
upstream frontends {
server react-frontend:80;
}
upstream backends {
server backend:80;
}
server {
server_name 127.0.0.1 localhost example.com *.example.com;
listen 80;
access_log /var/log/nginx/access.log;
location / {
proxy_pass http://frontends;
}
# if required , only then use it otherwise remove it [for direct api calls]
location /api/v1 {
proxy_pass http://backends;
}
}
To create nginx-proxy network if not present.
docker network create nginx-proxy
I have a fairly standard ReactJS frontend (using port 3000) app which is served by a NodeJS backend server (using port 5000). Both apps are Dockerized and I have configured NGINX in order to proxy requests from the frontend to and from the server.
Dockerfile for front end (with NGINX "baked in"):
FROM node:lts-alpine as build
WORKDIR /app
COPY ./package.json ./
COPY ./package-lock.json ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx
EXPOSE 3000
EXPOSE 443
EXPOSE 80
COPY ./cert/app.crt /etc/nginx/
COPY ./cert/app.key /etc/nginx/
ENV HTTPS=true
ENV SSL_CRT_FILE=/etc/nginx/app.crt
ENV SSL_KEY_FILE=/etc/nginx/app.key
RUN rm /etc/nginx/conf.d/default.conf
COPY ./default.conf /etc/nginx/nginx.conf
COPY --from=build /app/build/ /usr/share/nginx/html
CMD ["nginx", "-g", "daemon off;"]
Dockerfile for server:
FROM node:lts-alpine as build
WORKDIR /app
EXPOSE 5000
ENV NODE_TLS_REJECT_UNAUTHORIZED=0
ENV DANGEROUSLY_DISABLE_HOST_CHECK=true
ENV NODE_CONFIG_DIR=./config/
COPY ./package.json ./
COPY ./package-lock.json ./
RUN npm install
COPY . .
CMD [ "npm", "start" ]
The docker-compose.yml for this setup is
version: '3.8'
services:
client:
container_name: client
depends_on:
- server
stdin_open: true
environment:
- CHOKIDAR_USEPOLLING=true
- HTTPS=true
- SSL_CRT_FILE=/etc/nginx/app.crt
- SSL_KEY_FILE=/etc/nginx/app.key
build:
dockerfile: Dockerfile
context: ./client
expose:
- "8000"
- "3000"
ports:
- "3000:443"
- "8000:80"
volumes:
- ./client:/app
- /app/node_modules
- /etc/nginx
networks:
- internal-network
server:
container_name: server
build:
dockerfile: Dockerfile
context: "./server"
expose:
- "5000"
ports:
- "5000:5000"
volumes:
- /app/node_modules
- ./server:/app
networks:
- internal-network
networks:
internal-network:
driver: bridge
And crucially, the NGINX default.conf is
worker_processes auto;
events {
worker_connections 1024;
}
pid /var/run/nginx.pid;
http {
include mime.types;
upstream loadbalancer {
server server:5000 weight=3;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
port_in_redirect off;
absolute_redirect off;
return 301 https://$host$request_uri;
}
server {
listen [::]:443 ssl;
listen 443 ssl;
server_name example.app* example.co* example.uksouth.azurecontainer.io* localhost*;
error_page 497 https://$host:$server_port$request_uri;
error_log /var/log/nginx/client-proxy-error.log;
access_log /var/log/nginx/client-proxy-access.log;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:AES128:AES256:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 24h;
keepalive_timeout 300;
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains';
ssl_certificate /etc/nginx/app.crt;
ssl_certificate_key /etc/nginx/app.key;
root /usr/share/nginx/html;
index index.html index.htm index.nginx-debian.html;
location / {
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;
try_files $uri $uri/ /index.html;
}
location /tours {
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_pass http://loadbalancer;
}
}
}
with this configuration I have two problems:
By running docker-compose up -d, this setup builds and deploys two Docker containers locally. When I use https://localhost:3000/id this works and the data is retrieved and shown in browser correctly - when I type http://localhost:3000/id this gets redirected to http://localhost:443/id and this does not work. I have attempted to use NGINX commands port_in_redirect off; absolute_redirect off; but this has not helped. How can I make sure that the redirect does not edit the port number? (this is likely not going to be an issue in production where the port numbers are not used).
The bigger problem: the deployment to Azure is done using a docker context and running docker-compose -f ./docker-compose-azure.yml up. This runs and creates two Docker containers and a side-car process. The docker-compose-azure.yml file is
version: '3.8'
services:
client:
image: dev.azurecr.io/example-client
depends_on:
- server
stdin_open: true
environment:
- CHOKIDAR_USEPOLLING=true
- HTTPS=true
- SSL_CRT_FILE=/etc/nginx/app.crt
- SSL_KEY_FILE=/etc/nginx/app.key
restart: unless-stopped
domainname: "example-dev"
expose:
- "3000"
ports:
- target: 3000
#published: 3000
protocol: tcp
mode: host
networks:
- internal-network
server:
image: dev.azurecr.io/example-server
restart: unless-stopped
ports:
- "5000:5000"
networks:
- internal-network
networks:
internal-network:
driver: bridge
If I don't use HTTPS and a simple reverse proxy - the two issues outline above go away. But with the configuration above, calls to the Azure FQDN/URL fail; HTTPS requests timing out "ERR_CONNECTION_TIMED_OUT", and for HTTP, the site could not be found. What am I doing wrong here?
Thanks for your time.
I think Jan Garaj's answer has touched upon all the important bits. Here is my take, trying to give a targeted answer.
HTTP to HTTPS redirect
Currently the return 301 statement is using the $host variable that only holds the Hostname and not the port information. To capture both, you can use the $http_host variable instead. source
server {
listen [::]:80;
#//307 to preserve POST data
return 307 https://$http_host$request_uri;
}
Problems with the Azure config
In the Azure config, you have this bit:
ports:
- target: 3000
#published: 3000
protocol: tcp
mode: host
which identifies 3000 as the internal client port which listens to the requests. But you have to remember that you have a NGINX proxy inside that only listens to ports 80 or 443 (the server blocks in Nginx config). So this is the reason you get the ERR_CONNECTION_TIMED_OUT error because the requests are sent to port 3000 where nothing is listening.
As you want to do a HTTPS deployment, you can set this to 443 and the Nginx will take care of the request.
enabling HTTP redirect on Azure
The final bit is to configure the Azure deployment such that when a HTTP request is made to your URL, it should get redirected to the HTTPS counterpart. We already have the NGINX redirect block for port 80.
BUT, it will not help. As we specify the target to be 443 inside the container, the HTTP request will try to hit 443 and get refused.This article also mentions the same towards the end
Use your browser to navigate to the public IP address of the container group. The IP address shown in this example is 52.157.22.76, so the URL is https://52.157.22.76. You must use HTTPS to see the running application, because of the Nginx server configuration. Attempts to connect over HTTP fail.
This could be solved if it were possible to add another port to Azure config, the port 80.
ports:
- port: 443
protocol: TCP
- port: 80
protocol: TCP
I am not sure if Azure allows this, but if it does then thats the final solution.
I think you need to check/update Nginx configuration file properly and also make sure SSL certificate files are available
# http block would be
server {
listen 80 default_server;
return 301 https://$server_name$request_uri;
}
and in https server block, you need to update location block
location /tours {
proxy_pass http://server:5000;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
}
location / {
try_files $uri $uri/ /index.html;
}
Updated
Your Nginx config file would be
worker_processes auto;
events {
worker_connections 1024;
}
pid /var/run/nginx.pid;
http {
include mime.types;
server {
listen [::]:443 ssl;
listen 443 ssl;
server_name my-redirected-domain.com my-azure-domain.io localhost;
access_log /var/log/nginx/client-proxy.log;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:AES128:AES256:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 24h;
keepalive_timeout 300;
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains';
ssl_certificate /etc/nginx/viewform.app.crt;
ssl_certificate_key /etc/nginx/viewform.app.key;
root /usr/share/nginx/html;
index index.html index.htm index.nginx-debian.html;
location / {
try_files $uri $uri/ /index.html;
}
location /tours {
proxy_pass http://server:5000;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
server {
listen 80 default_server;
return 301 https://$server_name$request_uri;
}
}
Use port 443 everywhere to avoid any confusion with port remapping (that can be an advance setup):
1.) Define client container to be running on port 443:
version: '3.8'
services:
client:
...
ports:
- port: 443
protocol: TCP
2.) Define Nginx to be running on the port 443 with proper TLS setup as you have in your updated nginx.conf
Deploy and open https://<public IP> (you will very likely need to add sec. exception in the browser).
BTW: Azure has quite good article about Nginx with TLS (but more advance setup is used):
https://learn.microsoft.com/en-us/azure/container-instances/container-instances-container-group-ssl
IMHO better redirect from http to https is:
server {
listen [::]:80;
return 301 https://$host$request_uri;
}
As our organization is using SSO for staff we are getting 502 bad gateway when users try to login with shibboleth.
The users who has more groups access, and try to login they are getting 502, but the users who has less access they are able to logged in.
The maximum header size with all the access is 32768.
We tried the --max-http-header-size 42768 in docker, how ever it was not helpful.
The users with normal access(less header size) is able to log in.
Our setup:
VM1 host the nginx as reverse proxy. The configuration is below.
VM2 host more than one docker.
server {
listen 80;
server_name **********;
proxy_buffering off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
client_body_timeout 60s;
client_header_timeout 60s;
keepalive_timeout 70s;
send_timeout 60s;
client_body_buffer_size 32k;
client_header_buffer_size 32k;
client_max_body_size 0;
large_client_header_buffers 4 32k;
access_log off;
error_log /data/nginx/logs/****_error.log warn;
location / {
proxy_pass http://******:8098;
}
}
Error log:
2019/09/25 10:25:38 [error] 20070#0: *123 upstream prematurely closed
connection while reading response header from upstream, client: ****,
server: ******, request: "GET /auth/shibboleth?redirect=L2FjY291bnQ=
HTTP/1.1", upstream: "http://******:8098/auth/shibboleth?redirect=L2FjY291bnQ=",
host: "*****", referrer:
"https://******/profile/SAML2/Redirect/SSO?execution=e1s2"
2019/09/25 10:25:50 [error] 20070#0: *125 upstream prematurely closed
connection while reading response header from upstream, client: ****,
server: *****, request: "GET / HTTP/1.1", upstream: "http://****:8098/",
host: "*****"
Docker setup
FROM node:8-alpine as intermediate
RUN apk add --no-cache git openssh alpine-sdk python2
RUN python2 -m ensurepip && \
rm -r /usr/lib/python*/ensurepip && \
pip install --upgrade pip setuptools && \
if [[ ! -e /usr/bin/python ]]; then ln -sf /usr/bin/python2
/usr/bin/python; fi
WORKDIR /usr/src/app
RUN touch config.js && mkdir config
COPY package*.json ./
RUN http_proxy="http://****:3128" https_proxy="http://****:3128" npm install
COPY . .
RUN rm -rf .private
FROM node:8-alpine
WORKDIR /usr/src/app
COPY --from=intermediate /usr/src/app /usr/src/app
EXPOSE 8080
CMD [ "node", "app.js", "-p 8080" ]
This is apparently common. The fix:
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
See e.g.
Kubernetes nginx ingress controller returns 502 but only for AJAX/XmlHttpRequest requests - Stack Overflow
Fixing Nginx "upstream sent too big header" error when running an ingress controller in Kubernetes
I have this Dockerfile I use to deploy my nodejs app together with nginx:
#Create our image from Node
FROM node:latest as builder
MAINTAINER Cristi Boariu <cristiboariu#gmail.com>
# use changes to package.json to force Docker not to use the cache
# when we change our application's nodejs dependencies:
ADD package.json /tmp/package.json
RUN cd /tmp && npm install
RUN mkdir -p /opt/app && cp -a /tmp/node_modules /opt/app/
# From here we load our application's code in, therefore the previous docker
# "layer" thats been cached will be used if possible
WORKDIR /opt/app
ADD . /opt/app
EXPOSE 8080
CMD npm start
### STAGE 2: Setup ###
FROM nginx:1.13.3-alpine
## Copy our default nginx config
RUN mkdir /etc/nginx/ssl
COPY nginx/default.conf /etc/nginx/conf.d/
COPY nginx/star_zuumapp_com.chained.crt /etc/nginx/ssl/
COPY nginx/star_zuumapp_com.key /etc/nginx/ssl/
RUN cd /etc/nginx && chmod -R 600 ssl/
RUN mkdir -p /opt/app
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]
and this is my nginx file:
upstream api-server {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name example.com www.example.com;
access_log /var/log/nginx/dev.log;
error_log /var/log/nginx/dev.error.log debug;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarder-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_pass http://api-server;
proxy_redirect off;
}
}
server {
listen 443 default ssl;
server_name example.com www.example.com;
ssl on;
ssl_certificate /etc/nginx/ssl/star_example_com.chained.crt;
ssl_certificate_key /etc/nginx/ssl/star_example_com.key;
server_tokens off;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
ssl_prefer_server_ciphers on;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarder-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://api-server;
proxy_redirect off;
}
}
I already spent a few hours debugging this without success.
Basically, I receive:
502 Bad Gateway
when trying to test it locally on:
https://localhost/docs/#
From docker logs:
172.17.0.1 - - [04/May/2018:05:35:36 +0000] "GET /docs/ HTTP/1.1" 502 166 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1 Safari/605.1.15" "-"
2018/05/04 05:35:36 [error] 7#7: *1 connect() failed (111: Connection refused) while connecting to upstream, client: 172.17.0.1, server: example.com, request: "GET /docs/ HTTP/1.1", upstream: "http://127.0.0.1:8080/docs/", host: "localhost"
Can somebody help please?
You should configure:
server 172.17.42.1:8080;
172.17.42.1 is gateway ip address of docker.
or
server app_ipaddress_container:8080;
in nginx.conf file.
Because of port 8080 is listened on host, not on the container.
I try to install nodebb on my server for the domain le.club.systemes.sonores.rocks.
I would like it works like that:
nginx > node.js
For information:
nginx is configured to use FPM.
# node -v
v5.4.0
# npm -v
3.3.12
What i did
I install nodebb with these pages:
https://docs.nodebb.org/en/latest/installing/os/ubuntu.html
https://docs.nodebb.org/en/latest/configuring/proxies/nginx.html
I update node.js with this page:
https://davidwalsh.name/upgrade-nodejs
At the beginning, i get a 502 Bad Gateway error.
I try to solve it with the help of these pages:
nginx: connect() failed (111: Connection refused) while connecting to upstream
http://jvdc.me/fix-502-bad-gateway-error-on-nginx-server-after-upgrading-php/
Finally i get the config page. At the end, i get a successful message and th button to launch the forum.
The forum never run and at reload i get a 502 Bad Gateway error.
I try start in the folder nodebb but it doesn't change anything apparently.
I try to restart FPM and nginxwithout success.
service php5-fpm restart
service nginx restart
My configuration:
my nginx host
#vi /etc/nginx/sites-available/le.club.systemes.sonores.rocks
upstream le.club.systemes.sonores.rocks {
ip_hash;
server localhost:4567;
keepalive 8;
}
server {
listen 80;
listen [::]:80;
#root /usr/share/nginx/html/node/le.club.systemes.sonores.rocks;
#index index.php index.html index.htm;
# Make site accessible from http://le.club.systemes.sonores.rocks/
server_name le.club.systemes.sonores.rocks;
#large_client_header_buffers 4 32k;
# Logs
access_log /var/log/leclubsystemessonoresrocks.access_log;
error_log /var/log/leclubsystemessonoresrocks.error_log;
location / {
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_pass http://localhost:4567/;
proxy_pass http://le.club.systemes.sonores.rocks/;
proxy_redirect off;
# Socket.IO Support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Redirect server error pages to the static page
#error_page 403 /403.html;
#error_page 404 /404.html;
#error_page 500 502 503 504 /50x.html;
}
nodebb config
/usr/share/nginx/html/node/le.club.systemes.sonores.rocks/nodebb# vi config.json
{
"url": "http://localhost:4567",
"secret": "xxx",
"database": "mongo",
"port": 4567,
"mongo": {
"host": "localhost",
"port": "4567",
"username": "yyy",
"database": "0"
}
}
nginx.conf
user www-data;
worker_processes 4;
pid /run/nginx.pid;
events {
worker_connections 768;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
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/x-javascript text/xml application/xml application/xml+rss text/javascript;
##
# nginx-naxsi config
##
# Uncomment it if you installed nginx-naxsi
##
#include /etc/nginx/naxsi_core.rules;
##
# nginx-passenger config
##
# Uncomment it if you installed nginx-passenger
##
#passenger_root /usr;
#passenger_ruby /usr/bin/ruby;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
##
# Special for 502 error
##
fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
##
# Access control
##
include blockips.conf;
}
www.config
# vi /etc/php5/fpm/pool.d/www.conf
listen = /var/run/php5-fpm.sock
;listen = 127.0.0.1:9000
Error log
for nginx
# vi /var/log/leclubsystemessonoresrocks.error_log
2016/01/08 23:20:28 [error] 31636#0: *29 connect() failed (111: Connection refused) while connecting to upstream, client: 2a01:e35:xxx:xxx:xxx:xxx:xxx:xxx, server: le.club.systemes.sonores.rocks, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:4567/", host: "le.club.systemes.sonores.rocks"
for nodebb [edit]
/usr/share/nginx/html/node/le.club.systemes.sonores.rocks/nodebb/l
ogs# vi output.log
8/1 22:58 [30578] - ^[[32minfo^[[39m: Time: Fri Jan 08 2016 22:58:01 GMT+0100 (CET)
8/1 22:58 [30578] - ^[[32minfo^[[39m: Initializing NodeBB v0.9.3
8/1 22:58 [30578] - ^[[31merror^[[39m: NodeBB could not connect to your Mongo database. Mongo returned the following error: connect ECONNREFUSED 127.0.0.1:4567
8/1 22:58 [30578] - ^[[31merror^[[39m: Error: connect ECONNREFUSED 127.0.0.1:4567
at Object.exports._errnoException (util.js:856:11)
at exports._exceptionWithHostPort (util.js:879:20)
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1063:14)
[cluster] Child Process (30578) has exited (code: 0, signal: null)
Do i have to configure to start node.js when nginx start?
Could it be a problem with the server configuration?
Of the proxy config?
Thank you in advance for any help.
jb