Nginx as reverse proxy with acme (letsencrypt): Difference between revisions

From Alpine Linux
m (Undo revision 26790 by Zcrayfish (talk) - wrong button pressed on patroller page!)
Tag: Undo
 
(33 intermediate revisions by 6 users not shown)
Line 1: Line 1:
== Introduction ==
== Introduction ==


This setup will allow you to have multiple servers/containers be accessible via a single IP address with the added benefit of centralized generation of letsencrypt certificates and secure https (according to ssllabs ssltest). Be aware you first need to setup regular HTTP server to be able to generate your HTTPS certificates and keys. After you have generated them you can add your host based configuration.
This setup will allow you to have multiple servers/containers accessible via a single IP address with the added benefit of a centralized generation of [https://letsencrypt.org/ letsencrypt] certificates and secure https (according to '''ssllabs ssltest'''). Be aware that you first need to setup a regular HTTP server in order to be able to generate your HTTPS certificates and keys. After you have generated them, you can then add your HTTPS host based configuration.
 
See the [[Nginx|NGINX]] page for general information about Nginx, starting/stopping the service etc.


== Installation ==
== Installation ==


For this howto we need two tools, NGINX and acme-client. lets install them.
For this howto, we need three tools: [[Nginx|NGINX]], {{pkg|acme-client}} and {{pkg|openssl}} (to generate [https://wiki.openssl.org/index.php/Diffie-Hellman_parameters Diffie–Hellman Parameters]).


{{Cmd|apk add nginx acme-client}}
{{Cmd|apk update
apk add nginx acme-client openssl}}


== Setup ==
== Setup ==
Line 15: Line 18:
==== Global configuration ====
==== Global configuration ====


First step is to refactor our global nginx.conf. Its target at a low traffic http server, to increase performance make changes at top level.
First step is to refactor our global <code>nginx.conf</code>. Its target at a low traffic http server, to increase performance make changes at top level.
 
{{Cat|/etc/nginx/nginx.conf|<nowiki># /etc/nginx/nginx.conf
The security settings are taken from https://cipherli.st . Please also read https://hstspreload.org for details about HSTS.
 
<pre>
# ngnix configuration file


user nginx;
user nginx;
worker_processes 1; # use "auto" to use all available cores (high performance)


worker_processes  1; # use "auto" to use all available cores (high performance)
# Configures default error logger.
error_log /var/log/nginx/error.log warn; # Log warn, error, crit, alert, emerg


events {
events {
     worker_connections 1024; # increase if you need more connections
    # The maximum number of simultaneous connections that can be opened by a worker process.
     worker_connections 1024; # increase if you need more connections
}
}


Line 33: Line 35:
     # server_names_hash_bucket_size controls the maximum length
     # server_names_hash_bucket_size controls the maximum length
     # of a virtual host entry (ie the length of the domain name).
     # of a virtual host entry (ie the length of the domain name).
     server_names_hash_bucket_size  64;
     server_names_hash_bucket_size  64; # controls the maximum length of a virtual host entry (ie domain name)
     server_tokens                  off; # hide who we are
     server_tokens                  off; # hide who we are, don't show nginx version to clients
     sendfile                        off; # can cause issues
     sendfile                        off; # can cause issues
    # secure nginx according to https://cipherli.st/
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
    ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off; # Requires nginx >= 1.5.9
    ssl_stapling on; # Requires nginx >= 1.3.7
    ssl_stapling_verify on; # Requires nginx => 1.3.7
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;
    add_header Strict-Transport-Security "max-age=63072000"; # https://hstspreload.org
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    ssl_dhparam dhparam.pem;


     # nginx will find this file in the config directory set at nginx build time
     # nginx will find this file in the config directory set at nginx build time
    # Includes mapping of file name extensions to MIME types of responses
     include mime.types;
     include mime.types;


     #fallback in case we can't determine a type
     # fallback in case we can't determine a type
     default_type application/octet-stream;
     default_type application/octet-stream;


     # buffering causes issues
     # buffering causes issues, disable it
    # increase buffer size. still useful even when buffering is off
     proxy_buffering off;
     proxy_buffering off;
    proxy_buffer_size 4k;
    # allow the server to close the connection after a client stops responding. Frees up socket-associated memory.
    reset_timedout_connection on;


     # include hosts
    # Specifies the main log format.
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
            '$status $body_bytes_sent "$http_referer" '
            '"$http_user_agent" "$http_x_forwarded_for"';
 
    # Sets the path, format, and configuration for a buffered log write.
    # Buffer log writes to speed up IO, or disable them altogether
    access_log /var/log/nginx/access.log main buffer=16k;
    #access_log off;
 
     # include virtual hosts configs
     include conf.d/*.conf;
     include conf.d/*.conf;
}
}
</pre>
</nowiki>}}
 
==== SSL configuration ====


==== Diffie–Hellman Parameters ====
Configure a file with all SSL-parameters that we can include in the virtual hosts configs later on.<br>
The security settings are inspired by the [https://ssl-config.mozilla.org/#server=nginx&version=1.16.1&config=modern&openssl=1.1.1k&guideline=5.7 Mozilla SSL Configuration Generator]. Please also read https://hstspreload.org for details about HSTS.
{{Cat|/etc/nginx/conf.d/ssl-params.inc|<nowiki># secure nginx, see https://ssl-config.mozilla.org


In the above configuration ssl_dhparam is used so we need to generate a global dhparam file. We want to use a 4096 key size but this can take a very long time. Because of this we are adding an extra option (dsaparam) to generate our dhparam file (see: https://wiki.openssl.org/index.php/Manual:Dhparam(1)#OPTIONS)
ssl_protocols TLSv1.3
ssl_prefer_server_ciphers off;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off; # Requires nginx >= 1.5.9
ssl_stapling on; # Requires nginx >= 1.3.7
ssl_stapling_verify on; # Requires nginx => 1.3.7
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;


{{Cmd|openssl dhparam -dsaparam -out /etc/nginx/dhparam.pem 4096}}
# https://hstspreload.org
add_header Strict-Transport-Security "max-age=63072000" always;
# By default, HSTS header is not added to subdomain requests. If you have subdomains and want
# HSTS to apply to all of them, you should add the includeSubDomains variable like this:
#add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;


At this point you should be able to (re)start your nginx server but it will not use any of the security features (yet).
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
</nowiki>}}


==== Per site configuration files (conf.d) ====
==== Per site configuration files (conf.d) ====


Since Alpine v3.5 we ship NGINX with an default.conf within the /etc/nginx/conf.d directory.
Since Alpine v3.5, we ship '''NGINX''' with a <code>default.conf</code> within the {{path|/etc/nginx/conf.d}} directory.
To add support for another website you can add files with the .conf extension to this directory.


/etc/nginx/conf.d/alpinelinux.org.conf:
To add support for another website, you can add files with the '''.conf''' extension to this directory:


<pre>
{{Cat|/etc/nginx/conf.d/alpinelinux.org.conf|<nowiki>
server {
server {
     listen        80;
     listen        80;
Line 93: Line 113:
     }
     }
}
}
</pre>
</nowiki>}}


==== Common configuration includes ====
==== Common configuration includes ====


If you need to setup multiple proxy setups you can include duplicated data like shown below.
If you need to setup multiple proxy setups, you can include duplicated data such as shown below:


/etc/nginx/conf.d/proxy_set_header.inc:
{{Cat|/etc/nginx/conf.d/proxy_set_header.inc|<nowiki>proxy_set_header    X-Forwarded-By      $server_addr:$server_port;
 
<pre>
proxy_set_header    X-Forwarded-By      $server_addr:$server_port;
proxy_set_header    X-Forwarded-For      $remote_addr;
proxy_set_header    X-Forwarded-For      $remote_addr;
proxy_set_header    X-Forwarded-Proto    $scheme;
proxy_set_header    X-Forwarded-Proto    $scheme;
proxy_set_header    Host                $host;
proxy_set_header    Host                $host;
</pre>
</nowiki>}}
 
 


=== acme-client ===
=== acme-client ===
 
To allow '''NGINX''' to support https, we need to add certificates and support for ACME (Automatic Certificate Management Environment) responses.
To allow NGINX to support https we need to add certificates and support for ACME (Automatic Certificate Management Environment) responses.


==== ACME responses ====
==== ACME responses ====


/etc/nginx/conf.d/acme.inc:
{{Cat|/etc/nginx/conf.d/acme.inc|<nowiki># Allow access to the ACME Challenge for Let's Encrypt
 
location ^~ /.well-known/acme-challenge {
<pre>
    allow all;
location /.well-known/acme-challenge {
     alias /var/www/acme;
     alias /var/www/acme;
}
}
</pre>
</nowiki>}}
 
And add this to your proxy configuration
 
/etc/nginx/conf.d/alpinelinux.org.conf


<pre>
And add this to your proxy configuration:
server {
{{Cat|/etc/nginx/conf.d/alpinelinux.org.conf|<nowiki>server {
     listen        80;
     listen        80;
     server_name  alpinelinux.org;
     server_name  alpinelinux.org;
Line 137: Line 150:
     }
     }
}
}
</pre>
</nowiki>}}


==== Generate certificates ====
==== Automatic generation of certificates ====


Create the following file and make it executable
Create the following file:
/etc/periodic/weekly/acme-client
{{Cat|/etc/periodic/weekly/acme-client|<nowiki>#!/bin/sh
 
<pre>
#!/bin/sh


hosts="alpinelinux.org"
hosts="alpinelinux.org"


for host in $hosts; do
for host in $hosts; do
         acme-client -Nnmv $host && renew=1
         acme-client -a https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf -Nnmv $host && renew=1
done
done


[ "$renew" = 1 ] && rc-service nginx reload
[ "$renew" = 1 ] && rc-service nginx reload
</nowiki>}}
Make it executable:
chmod +x /etc/periodic/weekly/acme-client
This script will run weekly to verify whether one of your certificates is outdated and renew them when needed.
If you have several domains, you can add them to the '''hosts=''' variable with a space between each domain. This will create a separate certificate and key for each:
<pre>
hosts="alpinelinux.org example.com foo.org bar.io"
</pre>
</pre>


This script will run weekly to verify if one of your certificates is outdated and renew them when needed.
==== Initial generation of keys and certificates ====


To create your initial certificates and keys you have to run this manually the first time.
To create your initial certificates and keys, you have to run this manually the first time:


  {{Cmd|/etc/periodic/weekly/acme-client}}
  {{Cmd|/etc/periodic/weekly/acme-client}}


Watch the output and see if all goes well. When its finished you should have files in:
Watch the output and see if all goes well. When it's finished, you should have files in:


  /etc/ssl/acme/alpinelinux.nl/fullchain.pem
  /etc/ssl/acme/alpinelinux.nl/fullchain.pem
/etc/ssl/acme/private/alpinelinux.org/privkey.pem


/etc/ssl/acme/private/alpinelinux.org/privkey.pem


=== NGINX HTTPS ===
=== NGINX HTTPS ===
Line 174: Line 195:
Add the following below the previous HTTP configuration:
Add the following below the previous HTTP configuration:


/etc/nginx/conf.d/alpinelinux.org.conf
{{Cat|/etc/nginx/conf.d/alpinelinux.org.conf|<nowiki>
 
<pre>
server {
server {
     listen                 443 ssl;
     listen 443 ssl http2;
    listen [::]:443 ssl http2;
     server_name            alpinelinux.org
     server_name            alpinelinux.org
     ssl                    on;
     ssl                    on;
     ssl_certificate        /etc/ssl/acme/alpinelinux.org/fullchain.pem;
     ssl_certificate        /etc/ssl/acme/alpinelinux.org/fullchain.pem;
     ssl_certificate_key    /etc/ssl/acme/private/alpinelinux.org/privkey.pem;
     ssl_certificate_key    /etc/ssl/acme/private/alpinelinux.org/privkey.pem;
    include /etc/nginx/conf.d/ssl-params.inc; # SSL parameters


     location / {
     location / {
Line 189: Line 211:
     }
     }
}
}
</pre>
</nowiki>}}


=== Redirect HTTP to HTTPS ===
=== Redirect HTTP to HTTPS ===
Line 197: Line 219:
Create the following file:
Create the following file:


/etc/nginx/conf.d/redirect_http.inc
{{Cat|/etc/nginx/conf.d/redirect_http.inc|<nowiki>
 
<pre>
location / {
location / {
         return 301 https://$host$request_uri;
         return 301 https://$host$request_uri;
}
}
</pre>
</nowiki>}}


==== Update host configuration ====
==== Update host configuration ====


/etc/nginx/conf.d/alpinelinux.org.conf
{{Cat|/etc/nginx/conf.d/alpinelinux.org.conf|<nowiki>
 
<pre>
server {
server {
     listen        80;
     listen        80;
Line 215: Line 233:
     include      conf.d/acme.inc;
     include      conf.d/acme.inc;
     include      conf.d/redirect_http.inc;
     include      conf.d/redirect_http.inc;
    location / {
        include conf.d/proxy_set_header.inc;
        proxy_pass http://downstream_http_server_host;
    }
}
}
</pre>
</nowiki>}}


=== Complete host example with IPv6 support ===


/etc/nginx/conf.d/alpinelinux.org.conf


<pre>
=== Complete host example with IPv6 support ===
# alpinelinux.org


{{Cat|/etc/nginx/conf.d/alpinelinux.org.conf|<nowiki>
server {
server {
     listen                  80;
     listen                  80;
Line 235: Line 246:
     server_name            alpinelinux.org;
     server_name            alpinelinux.org;
     include                conf.d/acme.inc;
     include                conf.d/acme.inc;
     include                conf.d/redirect_https.inc;
     include                conf.d/redirect_http.inc;
}
}


server {
server {
     listen                  443 ssl;
     listen                  443 ssl http2;
     listen                  [::]:443 ssl;
     listen                  [::]:443 ssl http2;
     server_name            alpinelinux.org;
     server_name            alpinelinux.org;
     ssl                    on;
     ssl                    on;
     ssl_certificate        /etc/ssl/acme/alpinelinux.org/fullchain.pem;
     ssl_certificate        /etc/ssl/acme/alpinelinux.org/fullchain.pem;
     ssl_certificate_key    /etc/ssl/acme/private/alpinelinux.org/privkey.pem;
     ssl_certificate_key    /etc/ssl/acme/private/alpinelinux.org/privkey.pem;
    include /etc/nginx/conf.d/ssl-params.inc; # SSL parameters


     location / {
     location / {
Line 251: Line 264:
     }
     }
}
}
</nowiki>}}


</pre>
 
[[Category:Authentication]]
[[Category:Networking]]
[[Category:Web Server]]

Latest revision as of 16:48, 28 May 2024

Introduction

This setup will allow you to have multiple servers/containers accessible via a single IP address with the added benefit of a centralized generation of letsencrypt certificates and secure https (according to ssllabs ssltest). Be aware that you first need to setup a regular HTTP server in order to be able to generate your HTTPS certificates and keys. After you have generated them, you can then add your HTTPS host based configuration.

See the NGINX page for general information about Nginx, starting/stopping the service etc.

Installation

For this howto, we need three tools: NGINX, acme-client and openssl (to generate Diffie–Hellman Parameters).

apk update apk add nginx acme-client openssl

Setup

NGINX HTTP

Global configuration

First step is to refactor our global nginx.conf. Its target at a low traffic http server, to increase performance make changes at top level.

Contents of /etc/nginx/nginx.conf

# /etc/nginx/nginx.conf user nginx; worker_processes 1; # use "auto" to use all available cores (high performance) # Configures default error logger. error_log /var/log/nginx/error.log warn; # Log warn, error, crit, alert, emerg events { # The maximum number of simultaneous connections that can be opened by a worker process. worker_connections 1024; # increase if you need more connections } http { # server_names_hash_bucket_size controls the maximum length # of a virtual host entry (ie the length of the domain name). server_names_hash_bucket_size 64; # controls the maximum length of a virtual host entry (ie domain name) server_tokens off; # hide who we are, don't show nginx version to clients sendfile off; # can cause issues # nginx will find this file in the config directory set at nginx build time # Includes mapping of file name extensions to MIME types of responses include mime.types; # fallback in case we can't determine a type default_type application/octet-stream; # buffering causes issues, disable it # increase buffer size. still useful even when buffering is off proxy_buffering off; proxy_buffer_size 4k; # allow the server to close the connection after a client stops responding. Frees up socket-associated memory. reset_timedout_connection on; # Specifies the main log format. log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; # Sets the path, format, and configuration for a buffered log write. # Buffer log writes to speed up IO, or disable them altogether access_log /var/log/nginx/access.log main buffer=16k; #access_log off; # include virtual hosts configs include conf.d/*.conf; }

SSL configuration

Configure a file with all SSL-parameters that we can include in the virtual hosts configs later on.
The security settings are inspired by the Mozilla SSL Configuration Generator. Please also read https://hstspreload.org for details about HSTS.

Contents of /etc/nginx/conf.d/ssl-params.inc

# secure nginx, see https://ssl-config.mozilla.org ssl_protocols TLSv1.3 ssl_prefer_server_ciphers off; ssl_session_timeout 1d; ssl_session_cache shared:SSL:10m; ssl_session_tickets off; # Requires nginx >= 1.5.9 ssl_stapling on; # Requires nginx >= 1.3.7 ssl_stapling_verify on; # Requires nginx => 1.3.7 resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s; # https://hstspreload.org add_header Strict-Transport-Security "max-age=63072000" always; # By default, HSTS header is not added to subdomain requests. If you have subdomains and want # HSTS to apply to all of them, you should add the includeSubDomains variable like this: #add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always; add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; add_header X-Robots-Tag none;

Per site configuration files (conf.d)

Since Alpine v3.5, we ship NGINX with a default.conf within the /etc/nginx/conf.d directory.

To add support for another website, you can add files with the .conf extension to this directory:

Contents of /etc/nginx/conf.d/alpinelinux.org.conf

server { listen 80; server_name alpinelinux.org; location / { include conf.d/proxy_set_header.inc; proxy_pass http://downstream_http_server_host; } }

Common configuration includes

If you need to setup multiple proxy setups, you can include duplicated data such as shown below:

Contents of /etc/nginx/conf.d/proxy_set_header.inc

proxy_set_header X-Forwarded-By $server_addr:$server_port; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $host;


acme-client

To allow NGINX to support https, we need to add certificates and support for ACME (Automatic Certificate Management Environment) responses.

ACME responses

Contents of /etc/nginx/conf.d/acme.inc

# Allow access to the ACME Challenge for Let's Encrypt location ^~ /.well-known/acme-challenge { allow all; alias /var/www/acme; }

And add this to your proxy configuration:

Contents of /etc/nginx/conf.d/alpinelinux.org.conf

server { listen 80; server_name alpinelinux.org; include conf.d/acme.inc; location / { include conf.d/proxy_set_header.inc; proxy_pass http://downstream_http_server_host; } }

Automatic generation of certificates

Create the following file:

Contents of /etc/periodic/weekly/acme-client

#!/bin/sh hosts="alpinelinux.org" for host in $hosts; do acme-client -a https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf -Nnmv $host && renew=1 done [ "$renew" = 1 ] && rc-service nginx reload

Make it executable:

chmod +x /etc/periodic/weekly/acme-client

This script will run weekly to verify whether one of your certificates is outdated and renew them when needed.

If you have several domains, you can add them to the hosts= variable with a space between each domain. This will create a separate certificate and key for each:

hosts="alpinelinux.org example.com foo.org bar.io"

Initial generation of keys and certificates

To create your initial certificates and keys, you have to run this manually the first time:

/etc/periodic/weekly/acme-client

Watch the output and see if all goes well. When it's finished, you should have files in:

/etc/ssl/acme/alpinelinux.nl/fullchain.pem
/etc/ssl/acme/private/alpinelinux.org/privkey.pem


NGINX HTTPS

Per site HTTPS configuration

Add the following below the previous HTTP configuration:

Contents of /etc/nginx/conf.d/alpinelinux.org.conf

server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name alpinelinux.org ssl on; ssl_certificate /etc/ssl/acme/alpinelinux.org/fullchain.pem; ssl_certificate_key /etc/ssl/acme/private/alpinelinux.org/privkey.pem; include /etc/nginx/conf.d/ssl-params.inc; # SSL parameters location / { include conf.d/proxy_set_header.inc; proxy_pass http://downstream_http_server_host; } }

Redirect HTTP to HTTPS

Shared configuration

Create the following file:

Contents of /etc/nginx/conf.d/redirect_http.inc

location / { return 301 https://$host$request_uri; }

Update host configuration

Contents of /etc/nginx/conf.d/alpinelinux.org.conf

server { listen 80; server_name alpinelinux.org; include conf.d/acme.inc; include conf.d/redirect_http.inc; }


Complete host example with IPv6 support

Contents of /etc/nginx/conf.d/alpinelinux.org.conf

server { listen 80; listen [::]:80; server_name alpinelinux.org; include conf.d/acme.inc; include conf.d/redirect_http.inc; } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name alpinelinux.org; ssl on; ssl_certificate /etc/ssl/acme/alpinelinux.org/fullchain.pem; ssl_certificate_key /etc/ssl/acme/private/alpinelinux.org/privkey.pem; include /etc/nginx/conf.d/ssl-params.inc; # SSL parameters location / { include conf.d/proxy_set_header.inc; proxy_pass http://downstream_http_server_host; } }