Come generare certificati punto stella gratis con acme.sh
Certificati del tipo *.domain.com
Aggiornata a giugno 2026
Un certificato "punto stella" (wildcard) è quello del tipo *.dominio.com: copre con un colpo solo tutti i sottodomini (www, mail, blog, cloud, quello che vi pare) senza doverne emettere uno per ciascuno. Comodissimo quando si hanno tanti servizi sotto lo stesso dominio.
La fregatura è che per un wildcard non si può usare la classica validazione via HTTP (quella in cui Let's Encrypt scarica un file dal vostro sito): l'unico modo è la validazione DNS, cioè dimostrare di controllare il dominio creando un record TXT su _acme-challenge. E per farlo in automatico serve che acme.sh sappia parlare con le API del vostro provider DNS.
Uso acme.sh invece di certbot perché è un solo script in shell, senza dipendenze Python, e supporta un'enormità di provider DNS già pronti (ovviamente non il mio, ma vabbè).
Installazione
# Scarica e installa acme.sh registrando l'account ACME con la vostra email
# (serve per gli avvisi di scadenza di Let's Encrypt)
curl https://get.acme.sh | sh -s email=email@email.com
L'installer si mette in /root/.acme.sh/, si aggiunge da solo al .bashrc e crea un cron giornaliero: da qui in poi i rinnovi sono automatici, non dovrete più ripensarci (i certificati Let's Encrypt durano 90 giorni, acme.sh li rinnova quando mancano ~30 giorni alla scadenza).
Notifiche Telegram
Comodo farsi avvisare su Telegram quando un certificato viene rinnovato (o se qualcosa va storto). Servono il token del bot e l'id della chat:
export TELEGRAM_BOT_APITOKEN="..." # token del bot, da @BotFather
export TELEGRAM_BOT_CHATID="-..." # id della chat/gruppo (negativo se è un gruppo)
acme.sh --set-notify --notify-hook telegram
Certificato singolo (validazione HTTP)
Per un dominio normale (non wildcard) va benissimo la validazione HTTP: acme.sh con --apache configura al volo Apache, mette il file di verifica sotto la webroot e lo rimette com'era a fine emissione. Mi sono fatto una funzione per non ricordare ogni volta tutti i parametri:
function installCert(){
# --issue --apache: emissione con validazione HTTP-01 gestita via Apache
/root/.acme.sh/acme.sh --issue --apache -d "$1"
# --install-cert: copia i file dove dico io e, a ogni rinnovo automatico,
# esegue il reloadcmd per far ricaricare i certificati ad Apache
/root/.acme.sh/acme.sh --install-cert -d "$1" \
--cert-file /etc/acme/$1_cert.pem \
--key-file /etc/acme/$1_privkey.pem \
--fullchain-file /etc/acme/$1_fullchain.pem \
--reloadcmd "service apache2 force-reload"
}
Si usa così: installCert dominio.com.
Certificato punto-stella (validazione DNS)
Qui sta il bello. Per il wildcard si passa alla validazione DNS1 col plugin del proprio provider nel mio caso dns_gidinet. Chiedo due nomi nello stesso certificato: il dominio nudo (dominio.com) e il wildcard ( *.dominio.com), così copro sia il dominio sia tutti i sottodomini.
function installCertStella(){
# --dns dns_gidinet: crea il record TXT _acme-challenge via API di Gidinet.
# --dnssleep 5: aspetta 5s che il record si propaghi prima di far verificare
# Let's Encrypt (se la verifica fallisce per propagazione lenta, alzatelo:
# --dnssleep 60 o più, dipende da quanto è pigro il vostro DNS)
acme.sh --dnssleep 5 --issue --dns dns_gidinet -d "$1" -d "*.$1"
/root/.acme.sh/acme.sh --install-cert -d "$1" \
--cert-file "/etc/acme/$1_cert.pem" \
--key-file "/etc/acme/$1_privkey.pem" \
--fullchain-file "/etc/acme/$1_fullchain.pem" \
--reloadcmd "apache2ctl configtest && apache2ctl restart"
}
Anche questa si usa col dominio come argomento: installCertStella dominio.com.
Una piccola accortezza nel reloadcmd: apache2ctl configtest && apache2ctl restart fa il configtest prima del riavvio, così se la configurazione di Apache è rotta il restart non parte nemmeno — meglio un certificato non ricaricato che un web server web fermo.
Importante: perché dns_gidinet (o qualunque altro plugin DNS) funzioni, acme.sh deve avere le credenziali API del provider, esportate come variabili d'ambiente prima di lanciare l'emissione. Ogni provider ha le sue: l'elenco completo con i nomi esatti delle variabili è nel wiki di acme.sh, sezione dnsapi. acme.sh le salva poi in /root/.acme.sh/account.conf e le riusa da solo a ogni rinnovo, quindi le esportate una volta sola.
E se non ci fosse il plugin per il mio provider?
Se il provider non dispone delle API per modificare i DNS, è possibile creare una chiave TXT permanente, da inserire una sola volta manualmente, tramite il comando:
acme.sh --make-dns-persist-value -d example.com --dns-persist-wildcard
Se dispone di api, si può creare il proprio plugin, come ho fatto io. Infatti gidinet non è presente tra i plugin di acme.sh
Per crare un api custom basta creare il file ~/.acme.sh/dns_nomeprovider.sh in cui ci devono essere 2 funzioni:
dns_nomeprovider_adddns_nomeprovider_rm
Tutte e due hanno come parametri il dominio e il valore da inserire nel TXT.
Ecco per esempio il mio file per gidinet dns_gidinet.sh che utilizza SOAP:
#!/usr/bin/env sh
# shellcheck disable=SC2034
dns_myapi_info='Gidinet
Site: gidinet.com
Docs: blog.chalda.it
Options:
USERNAME. utente di gidinet gdnXXXXX
APIKEY. Impostata su https://www.gidinet.com/modules/private/account_password/
Issues: github.com/acmesh-official/acme.sh
Author: Chalda Pnuzig
'
USERNAME="gdnXXXXX"
APIKEY="XXXX"
APIKEY_B64=$(echo -n "$APIKEY" | base64)
dns_contabo_add() {
fulldomain="${1?Errore: manca il sito}"
txtvalue="${2?Errore: manca il txt}"
_dns_contabo_send_soap "recordAdd" "$fulldomain" "$txtvalue"
}
dns_contabo_rm() {
fulldomain="${1?Errore: manca il sito}"
txtvalue="${2?Errore: manca il txt}"
_dns_contabo_send_soap "recordDelete" "$fulldomain" "$txtvalue"
}
_dns_contabo_send_soap() {
fn=$1
fulldomain=$2
txtvalue=$3
domain=$(tldextract -j "$fulldomain" | jq -r '.domain + "." + .suffix')
_info "Using contabo"
_debug fulldomain "$fulldomain"
_debug domain "$domain"
_debug txtvalue "$txtvalue"
_debug fn "$fn"
URL="https://api.quickservicebox.com/API/Beta/DNSAPI.asmx"
SOAP_BODY=$(cat <<EOF
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<${fn} xmlns="https://api.quickservicebox.com/DNS/DNSAPI">
<accountUsername>${USERNAME}</accountUsername>
<accountPasswordB64>${APIKEY_B64}</accountPasswordB64>
<record>
<DomainName>${domain}</DomainName>
<HostName>${fulldomain}</HostName>
<RecordType>TXT</RecordType>
<Data>${txtvalue}</Data>
<TTL>3600</TTL>
<Priority>0</Priority>
</record>
</${fn}>
</soap12:Body>
</soap12:Envelope>
EOF
)
RESPONSE=$(curl -s -X POST "$URL" \
-H "Content-Type: application/soap+xml; charset=utf-8" \
-d "$SOAP_BODY")
_debug response "$RESPONSE"
}
Richiede l'installazione dei comandi jq e tldextract
Rinnovo
Non c'è niente da fare a mano: il cron creato in fase di installazione gira ogni giorno, controlla le scadenze, rinnova ciò che serve (riusando provider DNS e credenziali memorizzati) ed esegue il reloadcmd per ricaricare Apache. Se avete configurato Telegram, vi arriva pure il messaggio. Per vedere lo stato:
acme.sh --list # certificati gestiti e date di scadenza
acme.sh --renew -d dominio.com --force # forzare un rinnovo subito, per provare
Commenti
Nessun commento ancora. Sii il primo!
Lascia un commento