Aggiunto letsencrypt

This commit is contained in:
2026-07-12 23:00:46 +02:00
parent 58962bb97e
commit 2ef1efe1c9
6 changed files with 231 additions and 12 deletions
+2
View File
@@ -0,0 +1,2 @@
# Dati Let's Encrypt gestiti da certbot: contengono chiavi private, NON committare.
certbot/
+62
View File
@@ -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.
+7
View File
@@ -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
+106
View File
@@ -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
+47 -12
View File
@@ -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;
}
server { location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
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;
+7
View File
@@ -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