Keys

Certificati SSL gratuiti con Certbot e AWS Lambda

5 min

Grazie al progetto Certbot e alla Electronic Frontier Foundation è possibile dotare il proprio sito web di un certificato SSL totalmente gratuito. Certbot è un tool a linea di comando che consente di richiedere un certificato SSL valido per il proprio dominio, a seguito di un processo di verifica della proprietà dello stesso. Il tool, grazie all’ausilio di diversi plug-in, si può inoltre occupare dell’installazione e dell’aggiornamento dello stesso sul proprio server web. In questo post vediamo come richiedere e rinnovare automaticamente i propri certificati SSL gratuiti con Certbot e AWS Lambda.

Perché gestire i certificati con AWS Lambda?

Diverse applicazioni web di cui mi occupo utilizzano CloudFront per la distribuzione dei contenuti, associato ad un bucket S3 di origine. Ho così deciso di realizzare una semplice Lambda function che si occupi di ottenere i certificati SSL con Certbot e di verificare periodicamente la data di scadenza degli stessi. Se necessario, questa provvede automaticamente al rinnovo ed all’importazione su AWS Certificate Manager del nuovo certificato.

Risultato? Mai più certificati SSL scaduti! L’automazione del processo è particolarmente importante tenendo conto della breve vita (90 giorni) dei certificati emessi da Let’s Encrypt CA.

Overview della soluzione

Al centro della soluzione c’è ovviamente la funzione Lambda, invocata periodicamente da un evento CloudWatch. La funzione gestisce un elenco di domini (specificato nella variabile d’ambiente DOMAINS_LIST – valori separati da virgola) e per ciascuno di essi:

  • viene verificata la presenza del certificato in ACM
  • se non presente, lo stesso viene richiesto tramite Certbot. Per completare correttamente il processo di verifica della proprietà del dominio, la funzione si occupa di copiare il token di validazione nel relativo bucket S3 che ospita le relative risorse statiche
  • per i certificati esistenti, viene verificata la data di scadenza e, qualora necessario, si procede al rinnovo

Al termine del processo, la configurazione, i certificati e le private key vengono memorizzate in un bucket S3 privato in modo che siano utilizzabili alla prossima esecuzione.

Utilizzo di Certbot in AWS Lambda

Certbot è scritto in Python ed è facilmente utilizzabile per automatizzare quindi i processi di richiesta, rinnovo e revoca dei certificati. Utilizzarlo però in ambiente AWS Lambda richiede uno step di preparazione aggiuntivo, in modo che tutti i pacchetti e le dipendenze necessarie siano correttamente installate. Questa fantastica guida spiega in maniera dettagliata come utilizzare un’istanza EC2 a tal scopo. Ho preparato il pacchetto contenente la versione 1.3.0 di Certbot che è disponibile nel repository relativo a questo post.

La configurazione di Certbot è collocata nella directory /tmp dell’istanza lambda e rimossa per ragioni di sicurezza al termine dell’esecuzione, in quanto include anche le private key dei certificati.

Per procedere alle operazioni di rinnovo è però necessario preservare la configurazione di Certbot. L’albero di directory contenente la configurazione è compresso in un file zip e copiato nel bucket S3 indicato dalla variabile d’ambiente CERTBOT_BUCKET. Al successivo avvio la configurazione viene ripristinata dal file zip nella directory /tmp dell’istanza.

Il lato oscuro dei Symlinks

Certbot verifica che il proprio albero di configurazione sia valido. Viene verificata, tra le altre cose, la presenza di link simbolici nella directory live relativa a ciascun dominio.

Il processo di backup e restore dell’albero di configurazione in formato zip rimuove tali link (sostituiti dai file effettivi). Per ripristinare la situazione iniziale, viene utilizzato questo metodo.

Superare la challenge di verifica con AWS S3

Per richiede un certificato è necessario superare una challenge, che dimostri la proprietà del relativo dominio. Poiché la Lambda function di questo progetto possa correttamente gestire tale challenge, è richiesto che:

  • esista un bucket S3 dello stesso nome del dominio
  • il bucket S3 sia già configurato come sorgente di una distribuzione CloudFront per il dominio oppure, in alternativa, che la funzionalità di static website hosting (HTTP) del bucket S3 sia – temporaneamente – attiva
  • la configurazione DNS per il dominio sia corretta
  • il ruolo IAM assegnato alla lambda function consenta le operazioni di PutObject e DeleteObject sul bucket S3

La challenge consiste nella creazione di uno specifico file contenente un token fornito da Certbot. Tale file deve essere posizionato temporaneamente sul sito web in modo che possa essere verificato dai server di Let’s Encrypt. Al termine del processo di verifica il file viene rimosso. Tali operazioni sono svolte da due script Python (auth-hook.py e cleanup-hook.py).

L’accesso al bucket S3 del dominio è richiesto solo per il superamento della challenge e l’ottenimento del primo certificato SSL. Non sarà più richiesto durante i successivi rinnovi.

Importare il certificato su ACM

Una volta ottenuto il certificato, la lambda function si occupa dell’importazione dello stesso in ACM. Qualora esista già un certificato per il medesimo dominio, questo viene sostituito specificando durante l’importazione il relativo ARN.

Nota importante: per essere utilizzato in CloudFront, è necessario importare il certificato nella region US East (N. Virginia). Per comodità ho realizzato l’intero stack in questa region AWS.

Schedulazione con CloudWatch

CloudWatch è utilizzato per eseguire la lambda function una volta al giorno. Tra i parametri di configurazione della lambda, è prevista una variabile d’ambiente (CERTS_RENEW_DAYS_BEFORE_EXPIRATION) che determina quanti giorni prima della scadenza provvedere al rinnovo del singolo certificato. La scadenza viene ottenuta da ACM. In questo modo l’esecuzione della funzione non comporta necessariamente il rinnovo e quindi può essere schedulata quotidianamente senza preoccupazioni.

Il rinnovo tramite CertBot è forzato, garantendo l’ottenimento di un nuovo certificato 30 (valore di default) giorni prima della scadenza.

Deployment tramite CloudFormation

Nel repository di questo progetto è disponibile il template CloudFormation per la creazione dello stack. Questo comprende la funzione, il relativo ruolo, l’evento CloudWatch e i relativi permessi.

Per effettuare il deployment, la prima operazione da svolgere dopo aver clonato il repository, è la build della funzione.

make lambda-build

Per creare lo stack è necessario indicare il bucket S3 da utilizzare per la memorizzazione della configurazione di Certbot.

make BUCKET=your_bucket_name create-stack 

Il bucket indicato viene utilizzato anche per memorizzare temporaneamente i sorgenti della funzione lambda per il deployment.

Una volta creato lo stack, è necessario impostare le variabili di ambiente DOMAINS_LIST con l’elenco dei domini da gestire separati da virgola e DOMAINS_EMAIL con l’indirizzo email da utilizzare durante la richiesta dei certificati. Per ciascun nuovo dominio è necessario fornire la corretta policy di accesso al relativo bucket S3 che ospita le risorse statiche.

Conclusioni

Ottenere certificati SSL gratuiti per i propri progetti è davvero utile; automatizzarne la gestione tramite questo progetto mi ha dato la possibilità di dimenticarmene e vivere felice.

Consiglio di effettuare i propri test sfruttando l’ambiente di staging messo a disposizione da Certbot. La variabile d’ambiente CERTBOT_ENV consente di definire se utilizzare l’endpoint di produzione (production) o quello di staging.

Attenzione alle quote ACM: il numero di certificati che è possibile importare in ACM è limitato nel tempo (quantità nell’ultimo anno). Durante i miei test mi sono scontrato con limite molto basso: 20! Come riportato in questo post è necessario contattare il supporto AWS per la rimozione.

Error: you have reached your limit of 20 certificates in the last year.

Ulteriori sviluppi? Molti! Il requisito di avere un bucket S3 con lo stesso nome del dominio può, per esempio, essere superato prevedendo una configurazione più avanzata, magari memorizzata in un DB. Sentiti libero di migliorare il codice sorgente originale e di farmelo sapere!

Ci siamo divertiti? Alla prossima!

Leave a Comment