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 commandsport_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 runningdocker-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 isversion: '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.
CodePudding user response:
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;
}
}
CodePudding user response:
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://docs.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;
}