Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d58320273 | ||
|
|
f30994a40b |
@@ -4,4 +4,4 @@ DDNINJA_IS_PRODUCTION=false
|
|||||||
DDNINJA_MONGO_DBNAME=fdn2_test
|
DDNINJA_MONGO_DBNAME=fdn2_test
|
||||||
DDNINJA_MONGO_LOCATION=mongo
|
DDNINJA_MONGO_LOCATION=mongo
|
||||||
|
|
||||||
DDNINJA_PUBLIC_URL=https://test.ifattidinapoli.it
|
DDNINJA_PUBLIC_URL=http://test.ifattidinapoli.it
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ DDNINJA_IS_PRODUCTION=false
|
|||||||
DDNINJA_MONGO_DBNAME=fdn2_test
|
DDNINJA_MONGO_DBNAME=fdn2_test
|
||||||
DDNINJA_MONGO_LOCATION=mongo
|
DDNINJA_MONGO_LOCATION=mongo
|
||||||
|
|
||||||
DDNINJA_PUBLIC_URL=https://test.ifattidinapoli.it
|
DDNINJA_PUBLIC_URL=http://test.ifattidinapoli.it
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
# Dati Let's Encrypt gestiti da certbot: contengono chiavi private, NON committare.
|
|
||||||
certbot/
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
# 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,19 +11,12 @@ 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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,114 +0,0 @@
|
|||||||
#!/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
|
|
||||||
+5
-40
@@ -5,53 +5,34 @@ 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;
|
||||||
|
|
||||||
location /.well-known/acme-challenge/ {
|
|
||||||
root /var/www/certbot;
|
|
||||||
}
|
|
||||||
|
|
||||||
location / {
|
|
||||||
return 301 https://www.ifattidinapoli.it$request_uri;
|
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/letsencrypt/live/www.ifattidinapoli.it/fullchain.pem;
|
ssl_certificate /etc/ssl/2025/www_ifattidinapoli_it.chained.crt;
|
||||||
ssl_certificate_key /etc/letsencrypt/live/www.ifattidinapoli.it/privkey.pem;
|
ssl_certificate_key /etc/ssl/2025/www_ifattidinapoli_it.key;
|
||||||
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;
|
||||||
|
|
||||||
location /.well-known/acme-challenge/ {
|
|
||||||
root /var/www/certbot;
|
|
||||||
}
|
|
||||||
|
|
||||||
location / {
|
|
||||||
return 301 https://$host$request_uri;
|
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/letsencrypt/live/www.ifattidinapoli.it/fullchain.pem;
|
ssl_certificate /etc/ssl/2025/www_ifattidinapoli_it.chained.crt;
|
||||||
ssl_certificate_key /etc/letsencrypt/live/www.ifattidinapoli.it/privkey.pem;
|
ssl_certificate_key /etc/ssl/2025/www_ifattidinapoli_it.key;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
proxy_pass http://fdn2.production.webserver/;
|
proxy_pass http://fdn2.production.webserver/;
|
||||||
@@ -65,22 +46,6 @@ http {
|
|||||||
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;
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
#!/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