NGINX Proxy

From KG7QIN's Wiki
Revision as of 05:51, 16 February 2024 by Kg7qin (talk | contribs) (Created initial page)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

The following configs can be used to proxy connections from another application via NGINX. You can couple this with the Let's Encrypt Certbot and its NGINX plugin to create an SSL proxy for apps.

Note: You will need to modify these files to reflect the hostnames, fully qualified domains names (fqdns), and IPs of the systems you use this on. Items that need to be changed are shown using generic placeholders (demo.example, <stuff in brackets>, etc). Ensure that you fully read through all the configuration files before running and don't just copy and paste what you see here. This information is provided AS IS and there are plenty of resources available online if you run into problems.


Stand alone

This config is for a stand-alone install of NGINX on a VM. You can uncomment the lines that start with ssl to provide your own cert, or instlal Let

The v1 and v2 versions are listed as they depend entirely on what version of NGINX is being used.

v1

# Upstreams
upstream backend {
    server 127.0.0.1:3000;
}

# HTTPS Server
server {
#    listen 443;
    server_name changeme.local;

    # You can increase the limit if your need to.
    client_max_body_size 200M;

    error_log /var/log/nginx/access.log;

# uncomment and update if using an SSL cert obtained from elsewhere
#    ssl on;
#    ssl_certificate /etc/nginx/certificate.crt;
#    ssl_certificate_key /etc/nginx/certificate.key;
#    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # don’t use SSLv3 ref: POODLE

    location / {
        proxy_pass http://backend;
        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-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Nginx-Proxy true;

        proxy_redirect off;
   }
}

v2

events {
  worker_connections  4096;  ## Default: 1024
}

# HTTPS Server
http {

   # Upstreams
   upstream backend {
       server 127.0.0.1:8181;
   }

   server {
   #    listen 443;
       server_name <fqdn of server>;

       # You can increase the limit if your need to.
       client_max_body_size 200M;

       access_log /var/log/nginx/access.log;
       error_log /var/log/nginx/error.log;

   # uncomment and update if using an SSL cert obtained from elsewhere
   #    ssl on;
   #    ssl_certificate /etc/nginx/certificate.crt;
   #    ssl_certificate_key /etc/nginx/certificate.key;
   #    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # don’t use SSLv3 ref: POODLE
  
       gzip on;

       location / {
           proxy_pass http://backend;
           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-Forwarded-For $proxy_add_x_forwarded_for;
           proxy_set_header X-Forwarded-Proto https;
           proxy_set_header X-Nginx-Proxy true;

           proxy_redirect off;
       }

   }

}

Let's Encrypt

  • Install the Let's Encrypt Certbot and NGINX plugin to use Let's Encrypt SSL certs automatically managed by Certbot:
  apt install -y certbot python3-certbot-nginx
  • Request a cert for your domain to be used by NGINX
  certbot -d <domain> --nginx

Docker

Configuration

  • Create the /srv/nginx-proxy directory
  • Copy the following to /srv/nginx-proxy/docker-compose.yml:
version: "3"
services:
  proxy:
     image: jwilder/nginx-proxy:alpine
     restart: always
     volumes:
       - /var/run/docker.sock:/tmp/docker.sock:ro
       - ./certs:/etc/nginx/certs:ro
       - ./vhost.d:/etc/nginx/vhost.d
       - ./html:/usr/share/nginx/html
       - ./conf.d:/etc/nginx/conf.d
     environment:
       - DEFAULT_HOST=demo.example.com
     ports:
       - "443:443"
       - "80:80"
  • Copy the following to /srv/nginx-proxy/docker-compose.override.yml:
proxyapp:
  environment:
    - VIRTUAL_HOST=demo.example.com
    - VIRTUAL_PORT=8080
uspsapp:
  environment:
    - VIRTUAL_HOST=demo.example.com
    - VIRTUAL_PORT=8080

SSL Certificates

  • Create reg.conf in /srv/nginx/certs:
[req]
default_bits = 2048
default_md = sha256
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
C = Some Country
ST = Some State
L = Some City/Location
O = Who are you?
CN = <fqdn of server>
[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = <fqdn of server>
DNS.2 = <hostname of server without domain>
IP.1 = <IP of server>
  • Generate a new CSR and print key to console (save key in <fqdn>.key file:
openssl req -config reg.conf -newkey rsa:2048 -sha256 -nodes -days 730 -out <hostname>.csr -outform pem
  • Send the CSR to a signing authority or self sign it:
# openssl x509 -req -sha256 -days 3650 -in  demo.example.com.csr -signkey  demo.example.com.key -out  demo.example.com.crt

Note: Make sure the certs are in /srv/nginx-proxy/certs

  • Download the full chain and individual certificate
  • Make sure full chain is in correct format with:
openssl pkcs7 -print_certs -in chain.p7b -out chain.cer
  • Correct chain file will look simliar to:
subject=C = Some Country, ST = Some State, L = Some City, O = Some Org, CN = <fqdn of server>

issuer=DC = <Domain>, DC = <Domain>, CN = <CA Issuing Authority>

-----BEGIN CERTIFICATE-----
<cert>
-----END CERTIFICATE-----

subject=DC = <Domain>, DC = <Domain>, CN = <CA Server>

issuer=DC = <Domain>, DC = <Domain>, CN = <CA Issuing Authority>

-----BEGIN CERTIFICATE-----
<snip>
-----END CERTIFICATE-----

subject=DC = <Domain>, DC = <Domain>, CN = <CA Server>

issuer=CN = <Root CA>

-----BEGIN CERTIFICATE-----
<snip>
-----END CERTIFICATE-----

subject=CN = <CA Server>

issuer=CN = <Root CA>

-----BEGIN CERTIFICATE-----
<snip>
-----END CERTIFICATE-----
  • Certificate files need to match the VIRTUAL_HOST of the container as follows:
root@server:/srv/nginx-proxy/certs# ls -alh
total 48K
drwxr-xr-x 2 root root 4.0K Sep 21 22:47 .
drwxr-xr-x 6 root root 4.0K Sep 21 22:49 ..
-rw-r--r-- 1 root root 9.9K Sep 21 22:45 chain.p7b
-rw-r--r-- 1 root root  426 Sep 21 22:32 reg.conf
-rw-r--r-- 1 root root 1.2K Sep 21 22:36 <hostname>.csr
-rw-r--r-- 1 root root 8.0K Sep 21 22:46 <fqdn>.chain.pem     <-- used by NGINX
-rw-r--r-- 1 root root 2.5K Sep 21 22:45 <fqdn>.crt           <-- used by NGINX
-rw-r--r-- 1 root root  424 Sep 21 22:27 <fqdn>.dhparam.pem   <-- used by NGINX
-rw-r--r-- 1 root root 1.7K Sep 21 22:37 <fqdn>.key           <-- used by NGINX
  • Generate dhparam with:
openssl dhparam -out /etc/nginx/ssl/dhparam-2048.pem 2048
  • Configure NGINX to listen on port 443 by adding to the ports section of the /srv/nginx-proxy/docker-compose.yml file
      - "443:443"

Lets Encrypt

  • Copy the following to /srv/nginx-proxy/docker-compose.yml:
version: "3"
services:
  proxy:
     image: jwilder/nginx-proxy:alpine
     restart: always
     volumes:
       - /var/run/docker.sock:/tmp/docker.sock:ro
       - ./certs:/etc/nginx/certs:ro
       - ./vhost.d:/etc/nginx/vhost.d
       - ./html:/usr/share/nginx/html
       - ./conf.d:/etc/nginx/conf.d
     environment:
       - DEFAULT_HOST=demo.example.com
     ports:
       - "443:443"
       - "80:80"
     labels:
       - "com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy=true"
  le:
    image: jrcs/letsencrypt-nginx-proxy-companion
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./certs:/etc/nginx/certs:rw
      - ./vhost.d:/etc/nginx/vhost.d:rw
      - ./html:/usr/share/nginx/html:rw
    environment:
      - NGINX_PROXY_CONTAINER=proxy_proxy_1
  • For each app you want to use Let's Encrypt SSL on, update /srv/nginx-proxy/docker-compose.override.yml:
usasapp:
  environments:
    - VIRTUAL_HOST=usas.sampletown.org
    - VIRTUAL_PORT=8080
    - LETSENCRYPT_HOST=usas.sampletown.org
    - LETSENCRYPT_EMAIL=hostmaster@example.com

Timeout

To adjust the timeout of NGINX Proxy, add the following to /srv/nginx-proxy/docker-compose.yml under volumes:

- ./conf.d:/etc/nginx/conf.d
  • Copy the following to /srv/nginx-proxy/conf.d/proxy-serttings.conf:
proxy_connect_timeout       300;
proxy_send_timeout          300;
proxy_read_timeout          90m;
send_timeout                300;

If issues are due to upload size, add the following additional line to the config file above:

client_max_body_size        5000m;

Note: Adjust values as needed.

Compression

  • Add the following to /srv/nginx/proxy/conf.d/compression.conf:
# ----------------------------------------------------------------------
# | Compression                                                        |
# ----------------------------------------------------------------------

# https://nginx.org/en/docs/http/ngx_http_gzip_module.html

# Enable gzip compression.
# Default: off
gzip on;

# Compression level (1-9).
# 5 is a perfect compromise between size and CPU usage, offering about 75%
# reduction for most ASCII files (almost identical to level 9).
# Default: 1
gzip_comp_level 5;

# Don't compress anything that's already small and unlikely to shrink much if at
# all (the default is 20 bytes, which is bad as that usually leads to larger
# files after gzipping).
# Default: 20
gzip_min_length 256;

# Compress data even for clients that are connecting to us via proxies,
# identified by the "Via" header (required for CloudFront).
# Default: off
gzip_proxied any;

# Tell proxies to cache both the gzipped and regular version of a resource
# whenever the client's Accept-Encoding capabilities header varies;
# Avoids the issue where a non-gzip capable client (which is extremely rare
# today) would display gibberish if their proxy gave them the gzipped version.
# Default: off
gzip_vary on;

# Compress all output labeled with one of the following MIME-types.
# `text/html` is always compressed by gzip module.
# Default: text/html
gzip_types
  application/atom+xml
  application/geo+json
  application/javascript
  application/x-javascript
  application/json
  application/ld+json
  application/manifest+json
  application/rdf+xml
  application/rss+xml
  application/vnd.ms-fontobject
  application/wasm
  application/x-web-app-manifest+json
  application/xhtml+xml
  application/xml
  font/eot
  font/otf
  font/ttf
  image/bmp
  image/svg+xml
  text/cache-manifest
  text/calendar
  text/css
  text/javascript
  text/markdown
  text/plain
  text/xml
  text/vcard
  text/vnd.rim.location.xloc
  text/vtt
  text/x-component
  text/x-cross-domain-policy;

Miscellaneous

ACMEv1/ACMEv2 error

  • LetsEncrypt no longer supports ACMEv1 for certificate management. If your site stops automatically renewing/generating certificates, this may appear in the logs:
docker-compose logs --tail=50 le
...
le_1 | 2020-01-09 11:48:43,395:INFO:simp_le:1382: Generating new account key
le_1 | ACME server returned an error: urn:acme:error:unauthorized :: The client lacks sufficient authorization ::
Account creation on ACMEv1 is disabled. Please upgrade your ACME client to a version that supports ACMEv2 / RFC 8555.
See https://community.letsencrypt.org/t/end-of-life-plan-for-acmev1/88430 for details.
  • To check if it successful, go to the certs/accounts directory. You will see something like this if it is using ACMEv2 - in this case our proxy docker-compose.yml is in /data/proxy:
/data/proxy/certs/accounts# ls
acme-v02.api.letsencrypt.org

Checking site configuration

Force Lets Encrypt certificate renewal

  • Force certificate renewal for all:
docker exec name_of_lets_encrypt_container/app/force_renew
Example:
 docker exec proxy_le_1 /app/force_renew

Get Lets Encrypt certificate status

  • Get certificate status - note in this case, I did a force renew on 3/30, so 90 days is 6/28.
docker exec name_of_lets_encrypt_container/app/cert_status
Example:
 docker exec proxy_le_1 /app/cert_status
##### Certificate status #####
/etc/nginx/certs/files.ssdt.io/fullchain.pem: no corresponding chain.pem file, unable to verify certificate
Certificate was issued by Let's Encrypt Authority X3
Certificate is valid until Jun 28 13:11:46 2020 GMT
...