Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69ab1c94b1 | ||
|
|
b550cbda4a | ||
|
|
2ef1efe1c9 |
@@ -4,4 +4,4 @@ DDNINJA_IS_PRODUCTION=false
|
||||
DDNINJA_MONGO_DBNAME=fdn2_test
|
||||
DDNINJA_MONGO_LOCATION=mongo
|
||||
|
||||
DDNINJA_PUBLIC_URL=http://test.ifattidinapoli.it
|
||||
DDNINJA_PUBLIC_URL=https://test.ifattidinapoli.it
|
||||
|
||||
@@ -4,4 +4,4 @@ DDNINJA_IS_PRODUCTION=false
|
||||
DDNINJA_MONGO_DBNAME=fdn2_test
|
||||
DDNINJA_MONGO_LOCATION=mongo
|
||||
|
||||
DDNINJA_PUBLIC_URL=http://test.ifattidinapoli.it
|
||||
DDNINJA_PUBLIC_URL=https://test.ifattidinapoli.it
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# Dati Let's Encrypt gestiti da certbot: contengono chiavi private, NON committare.
|
||||
certbot/
|
||||
@@ -0,0 +1,63 @@
|
||||
# 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:
|
||||
- Docker con il plugin Compose v2 (comando `docker compose`, non `docker-compose`).
|
||||
- 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:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./ssl:/etc/ssl:ro
|
||||
- ./certbot/conf:/etc/letsencrypt
|
||||
- ./certbot/www:/var/www/certbot
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-file: 4
|
||||
max-size: 100m
|
||||
compress: "true"
|
||||
certbot:
|
||||
image: certbot/certbot
|
||||
volumes:
|
||||
- ./certbot/conf:/etc/letsencrypt
|
||||
- ./certbot/www:/var/www/certbot
|
||||
|
||||
|
||||
|
||||
|
||||
Executable
+114
@@ -0,0 +1,114 @@
|
||||
#!/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.
|
||||
#
|
||||
# NB: usa il plugin "docker compose" (v2). Con questo plugin --entrypoint accetta
|
||||
# solo un eseguibile: i comandi con piu' token o con && vanno wrappati in
|
||||
# `--entrypoint sh <service> -c "..."`, altrimenti gli argomenti oltre il primo
|
||||
# vengono scartati silenziosamente.
|
||||
|
||||
set -e
|
||||
|
||||
if ! docker compose version >/dev/null 2>&1; then
|
||||
echo 'Error: "docker compose" (plugin v2) is not available.' >&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+=(-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 sh certbot -c "\
|
||||
openssl req -x509 -nodes -newkey rsa:$rsa_key_size -days 1 \
|
||||
-keyout '$path/privkey.pem' \
|
||||
-out '$path/fullchain.pem' \
|
||||
-subj '/CN=localhost'"
|
||||
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 sh certbot -c "\
|
||||
rm -Rf /etc/letsencrypt/live/$primary_domain && \
|
||||
rm -Rf /etc/letsencrypt/archive/$primary_domain && \
|
||||
rm -Rf /etc/letsencrypt/renewal/$primary_domain.conf"
|
||||
echo
|
||||
|
||||
email_arg=()
|
||||
case "$email" in
|
||||
"") email_arg=(--register-unsafely-without-email) ;;
|
||||
*) email_arg=(--email "$email") ;;
|
||||
esac
|
||||
|
||||
staging_arg=()
|
||||
if [ "$staging" != "0" ]; then staging_arg=(--staging); fi
|
||||
|
||||
echo "### Requesting Let's Encrypt certificate for: ${domains[*]} ..."
|
||||
# L'immagine certbot/certbot ha gia' "certbot" come entrypoint: passiamo solo
|
||||
# gli argomenti come comando (niente --entrypoint da sovrascrivere).
|
||||
docker compose run --rm certbot certonly --webroot -w /var/www/certbot \
|
||||
"${staging_arg[@]}" \
|
||||
"${email_arg[@]}" \
|
||||
"${domain_args[@]}" \
|
||||
--rsa-key-size "$rsa_key_size" \
|
||||
--agree-tos \
|
||||
--force-renewal
|
||||
echo
|
||||
|
||||
echo "### Reloading nginx ..."
|
||||
docker compose exec nginx nginx -s reload
|
||||
+47
-12
@@ -5,34 +5,53 @@ events { worker_connections 1024; }
|
||||
http {
|
||||
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 {
|
||||
listen 80;
|
||||
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 {
|
||||
listen 443 ssl;
|
||||
server_name ifattidinapoli.it;
|
||||
ssl_certificate /etc/ssl/2025/www_ifattidinapoli_it.chained.crt;
|
||||
ssl_certificate_key /etc/ssl/2025/www_ifattidinapoli_it.key;
|
||||
ssl_certificate /etc/letsencrypt/live/www.ifattidinapoli.it/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/www.ifattidinapoli.it/privkey.pem;
|
||||
return 301 https://www.ifattidinapoli.it$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
|
||||
listen 80;
|
||||
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;
|
||||
server_name www.ifattidinapoli.it preproduction.ifattidinapoli.it default_server;
|
||||
|
||||
ssl_certificate /etc/ssl/2025/www_ifattidinapoli_it.chained.crt;
|
||||
ssl_certificate_key /etc/ssl/2025/www_ifattidinapoli_it.key;
|
||||
ssl_certificate /etc/letsencrypt/live/www.ifattidinapoli.it/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/www.ifattidinapoli.it/privkey.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://fdn2.production.webserver/;
|
||||
@@ -42,10 +61,26 @@ http {
|
||||
|
||||
}
|
||||
|
||||
server {
|
||||
server {
|
||||
listen 80;
|
||||
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 / {
|
||||
proxy_pass http://fdn2.test.webserver/;
|
||||
proxy_redirect off;
|
||||
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#!/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.
|
||||
#
|
||||
# NB: usa il plugin "docker compose" (v2). L'immagine certbot/certbot ha gia'
|
||||
# "certbot" come entrypoint, quindi passiamo "renew" come comando.
|
||||
|
||||
docker compose run --rm certbot renew
|
||||
docker compose exec -T nginx nginx -s reload
|
||||
Reference in New Issue
Block a user