Compare commits
1
Commits
master
...
letsencrypt
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ef1efe1c9 |
@@ -0,0 +1,2 @@
|
|||||||
|
# Dati Let's Encrypt gestiti da certbot: contengono chiavi private, NON committare.
|
||||||
|
certbot/
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# Reverse proxy nginx + Let's Encrypt
|
||||||
|
|
||||||
|
Reverse proxy nginx davanti ad Apache per `ifattidinapoli.it`. I certificati SSL
|
||||||
|
sono gestiti automaticamente da Let's Encrypt tramite certbot (validazione
|
||||||
|
HTTP-01 via webroot), con **un unico certificato multi-dominio (SAN)** che copre
|
||||||
|
tutti gli host serviti da questo nginx.
|
||||||
|
|
||||||
|
## Domini coperti dal certificato
|
||||||
|
|
||||||
|
- `www.ifattidinapoli.it` (primario / CN)
|
||||||
|
- `ifattidinapoli.it`
|
||||||
|
- `preproduction.ifattidinapoli.it`
|
||||||
|
- `test.ifattidinapoli.it`
|
||||||
|
|
||||||
|
La lista si definisce nell'array `domains` di [init-letsencrypt.sh](init-letsencrypt.sh).
|
||||||
|
Il primo dominio e' il primario e da' il nome alla cartella
|
||||||
|
`/etc/letsencrypt/live/<primario>/` referenziata in [nginx.conf](nginx.conf).
|
||||||
|
|
||||||
|
## Primo setup
|
||||||
|
|
||||||
|
Prerequisiti:
|
||||||
|
- La rete docker esterna `nginx-network` deve esistere (`docker network create nginx-network`).
|
||||||
|
- Le porte 80 e 443 dell'host devono essere pubblicamente raggiungibili per
|
||||||
|
tutti i domini (la validazione HTTP-01 passa dalla porta 80).
|
||||||
|
- I record DNS di tutti i domini devono puntare a questo server.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. (consigliato) prova prima in staging: metti staging=1 in init-letsencrypt.sh
|
||||||
|
./init-letsencrypt.sh
|
||||||
|
|
||||||
|
# 2. verifica che tutto funzioni, poi rimetti staging=0 e rilancia
|
||||||
|
./init-letsencrypt.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Lo script:
|
||||||
|
1. scarica i parametri TLS raccomandati,
|
||||||
|
2. crea un certificato dummy self-signed cosi' nginx puo' partire,
|
||||||
|
3. avvia nginx,
|
||||||
|
4. cancella il dummy e richiede il certificato reale a Let's Encrypt,
|
||||||
|
5. ricarica nginx.
|
||||||
|
|
||||||
|
## Rinnovo
|
||||||
|
|
||||||
|
Il rinnovo e' automatico. [renew.sh](renew.sh) lancia `certbot renew` (che
|
||||||
|
rinnova solo se manca meno di 30 giorni alla scadenza) e ricarica nginx.
|
||||||
|
|
||||||
|
Schedularlo in crontab sull'host (`crontab -e`):
|
||||||
|
|
||||||
|
```
|
||||||
|
0 3 * * * cd /percorso/di/cicd/nginx && ./renew.sh > /tmp/renew.log 2>&1 || logger -t CRON_RENEW -f /tmp/renew.log
|
||||||
|
```
|
||||||
|
|
||||||
|
Se lo script fallisce, l'output finisce in syslog; altrimenti nessun output.
|
||||||
|
|
||||||
|
## Note sulla migrazione da Sectigo
|
||||||
|
|
||||||
|
- I vecchi certificati Sectigo sono in `ssl/`. Il mount `./ssl:/etc/ssl:ro` in
|
||||||
|
[docker-compose.yml](docker-compose.yml) e i file in `ssl/` possono essere
|
||||||
|
rimossi una volta verificato che Let's Encrypt funziona.
|
||||||
|
- La cartella `certbot/` (chiavi private incluse) e' in `.gitignore` e **non va
|
||||||
|
committata**. Le vecchie chiavi Sectigo sono invece presenti nella history git:
|
||||||
|
valutare con il fornitore se revocarle.
|
||||||
@@ -11,12 +11,19 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
- ./ssl:/etc/ssl:ro
|
- ./ssl:/etc/ssl:ro
|
||||||
|
- ./certbot/conf:/etc/letsencrypt
|
||||||
|
- ./certbot/www:/var/www/certbot
|
||||||
logging:
|
logging:
|
||||||
driver: "json-file"
|
driver: "json-file"
|
||||||
options:
|
options:
|
||||||
max-file: 4
|
max-file: 4
|
||||||
max-size: 100m
|
max-size: 100m
|
||||||
compress: "true"
|
compress: "true"
|
||||||
|
certbot:
|
||||||
|
image: certbot/certbot
|
||||||
|
volumes:
|
||||||
|
- ./certbot/conf:/etc/letsencrypt
|
||||||
|
- ./certbot/www:/var/www/certbot
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Executable
+106
@@ -0,0 +1,106 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Bootstrap del certificato Let's Encrypt unico (multi-dominio / SAN) per il
|
||||||
|
# reverse proxy di ifattidinapoli.it.
|
||||||
|
#
|
||||||
|
# A differenza del progetto Gogs-Reverse (un certificato separato per dominio),
|
||||||
|
# qui si richiede UN SOLO certificato che copre tutti i domini serviti da questo
|
||||||
|
# nginx. Il primo dominio dell'array e' il primario (CN) e da' il nome alla
|
||||||
|
# cartella /etc/letsencrypt/live/<primario>/ referenziata in nginx.conf.
|
||||||
|
#
|
||||||
|
# Lanciarlo una volta sola, al primo setup. Per i rinnovi usare renew.sh.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if ! [ -x "$(command -v docker-compose)" ]; then
|
||||||
|
echo 'Error: docker-compose is not installed.' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Domini coperti dal certificato. Il PRIMO e' il primario (CN) e da' il nome
|
||||||
|
# alla cartella live/. Se lo cambi, aggiorna anche i path in nginx.conf.
|
||||||
|
domains=(
|
||||||
|
"www.ifattidinapoli.it"
|
||||||
|
"ifattidinapoli.it"
|
||||||
|
"preproduction.ifattidinapoli.it"
|
||||||
|
"test.ifattidinapoli.it"
|
||||||
|
)
|
||||||
|
|
||||||
|
primary_domain="${domains[0]}"
|
||||||
|
|
||||||
|
rsa_key_size=4096
|
||||||
|
data_path="./certbot"
|
||||||
|
email="riccardo.didato@gmail.com"
|
||||||
|
# Metti a 1 per usare l'ambiente di STAGING di Let's Encrypt (test senza
|
||||||
|
# consumare i rate limit). Passato il test, rimetti a 0 e rilancia.
|
||||||
|
staging=0
|
||||||
|
|
||||||
|
# Costruisce la sequenza di argomenti -d per certbot
|
||||||
|
domain_args=""
|
||||||
|
for domain in "${domains[@]}"; do
|
||||||
|
domain_args="$domain_args -d $domain"
|
||||||
|
done
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# FASE 0: parametri TLS raccomandati (una tantum)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
if [ ! -e "$data_path/conf/options-ssl-nginx.conf" ] || [ ! -e "$data_path/conf/ssl-dhparams.pem" ]; then
|
||||||
|
echo "### Downloading recommended TLS parameters ..."
|
||||||
|
mkdir -p "$data_path/conf"
|
||||||
|
curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf > "$data_path/conf/options-ssl-nginx.conf"
|
||||||
|
curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem > "$data_path/conf/ssl-dhparams.pem"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# FASE 1: certificato dummy self-signed
|
||||||
|
# nginx non parte se i path ssl_certificate non esistono. Creiamo un cert
|
||||||
|
# temporaneo cosi' nginx si avvia e puo' servire la validazione ACME su :80.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
if [ -d "$data_path/conf/live/$primary_domain" ]; then
|
||||||
|
echo "### Existing certificate found for $primary_domain. Skipping dummy generation."
|
||||||
|
else
|
||||||
|
echo "### Creating dummy certificate for $primary_domain ..."
|
||||||
|
path="/etc/letsencrypt/live/$primary_domain"
|
||||||
|
mkdir -p "$data_path/conf/live/$primary_domain"
|
||||||
|
docker-compose run --rm --entrypoint "\
|
||||||
|
openssl req -x509 -nodes -newkey rsa:$rsa_key_size -days 1 \
|
||||||
|
-keyout '$path/privkey.pem' \
|
||||||
|
-out '$path/fullchain.pem' \
|
||||||
|
-subj '/CN=localhost'" certbot
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "### Starting nginx ..."
|
||||||
|
docker-compose up --force-recreate -d nginx
|
||||||
|
echo
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# FASE 2: richiesta del certificato REALE
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
echo "### Deleting dummy certificate ..."
|
||||||
|
docker-compose run --rm --entrypoint "\
|
||||||
|
rm -Rf /etc/letsencrypt/live/$primary_domain && \
|
||||||
|
rm -Rf /etc/letsencrypt/archive/$primary_domain && \
|
||||||
|
rm -Rf /etc/letsencrypt/renewal/$primary_domain.conf" certbot
|
||||||
|
echo
|
||||||
|
|
||||||
|
case "$email" in
|
||||||
|
"") email_arg="--register-unsafely-without-email" ;;
|
||||||
|
*) email_arg="--email $email" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ "$staging" != "0" ]; then staging_arg="--staging"; fi
|
||||||
|
|
||||||
|
echo "### Requesting Let's Encrypt certificate for: ${domains[*]} ..."
|
||||||
|
docker-compose run --rm --entrypoint "\
|
||||||
|
certbot certonly --webroot -w /var/www/certbot \
|
||||||
|
$staging_arg \
|
||||||
|
$email_arg \
|
||||||
|
$domain_args \
|
||||||
|
--rsa-key-size $rsa_key_size \
|
||||||
|
--agree-tos \
|
||||||
|
--force-renewal" certbot
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "### Reloading nginx ..."
|
||||||
|
docker-compose exec nginx nginx -s reload
|
||||||
+44
-9
@@ -5,34 +5,53 @@ events { worker_connections 1024; }
|
|||||||
http {
|
http {
|
||||||
client_max_body_size 5M;
|
client_max_body_size 5M;
|
||||||
|
|
||||||
|
# Path del certificato Let's Encrypt unico multi-dominio (SAN).
|
||||||
|
# Il nome della cartella live/ corrisponde al primo -d passato a certbot
|
||||||
|
# in init-letsencrypt.sh (dominio primario / CN).
|
||||||
|
# ssl_certificate /etc/letsencrypt/live/www.ifattidinapoli.it/fullchain.pem;
|
||||||
|
# ssl_certificate_key /etc/letsencrypt/live/www.ifattidinapoli.it/privkey.pem;
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name ifattidinapoli.it;
|
server_name ifattidinapoli.it;
|
||||||
return 301 https://www.ifattidinapoli.it$request_uri;
|
|
||||||
|
location /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/certbot;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
return 301 https://www.ifattidinapoli.it$request_uri;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 443 ssl;
|
listen 443 ssl;
|
||||||
server_name ifattidinapoli.it;
|
server_name ifattidinapoli.it;
|
||||||
ssl_certificate /etc/ssl/2025/www_ifattidinapoli_it.chained.crt;
|
ssl_certificate /etc/letsencrypt/live/www.ifattidinapoli.it/fullchain.pem;
|
||||||
ssl_certificate_key /etc/ssl/2025/www_ifattidinapoli_it.key;
|
ssl_certificate_key /etc/letsencrypt/live/www.ifattidinapoli.it/privkey.pem;
|
||||||
return 301 https://www.ifattidinapoli.it$request_uri;
|
return 301 https://www.ifattidinapoli.it$request_uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
|
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name www.ifattidinapoli.it preproduction.ifattidinapoli.it;
|
server_name www.ifattidinapoli.it preproduction.ifattidinapoli.it;
|
||||||
return 301 https://$host$request_uri;
|
|
||||||
|
location /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/certbot;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 443 ssl;
|
listen 443 ssl;
|
||||||
server_name www.ifattidinapoli.it preproduction.ifattidinapoli.it default_server;
|
server_name www.ifattidinapoli.it preproduction.ifattidinapoli.it default_server;
|
||||||
|
|
||||||
ssl_certificate /etc/ssl/2025/www_ifattidinapoli_it.chained.crt;
|
ssl_certificate /etc/letsencrypt/live/www.ifattidinapoli.it/fullchain.pem;
|
||||||
ssl_certificate_key /etc/ssl/2025/www_ifattidinapoli_it.key;
|
ssl_certificate_key /etc/letsencrypt/live/www.ifattidinapoli.it/privkey.pem;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
proxy_pass http://fdn2.production.webserver/;
|
proxy_pass http://fdn2.production.webserver/;
|
||||||
@@ -42,10 +61,26 @@ http {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name test.ifattidinapoli.it;
|
server_name test.ifattidinapoli.it;
|
||||||
|
|
||||||
|
location /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/certbot;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name test.ifattidinapoli.it;
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/www.ifattidinapoli.it/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/www.ifattidinapoli.it/privkey.pem;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
proxy_pass http://fdn2.test.webserver/;
|
proxy_pass http://fdn2.test.webserver/;
|
||||||
proxy_redirect off;
|
proxy_redirect off;
|
||||||
|
|||||||
Executable
+7
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Rinnova il certificato SSL e ricarica nginx.
|
||||||
|
# Schedulato via crontab sull'host (vedi README). certbot renew rinnova solo se
|
||||||
|
# il certificato scade entro 30 giorni, quindi e' sicuro lanciarlo ogni giorno.
|
||||||
|
|
||||||
|
/usr/local/bin/docker-compose run --rm --entrypoint "certbot renew" certbot
|
||||||
|
/usr/local/bin/docker-compose exec -T nginx nginx -s reload
|
||||||
Reference in New Issue
Block a user