Oggi il traffico web è virtualmente impossibile senza crittografia. La necessità di proteggere crittograficamente i dati in transito, siano essi reali o meno, è diventata una norma e un requisito per qualsiasi tipo di servizio da implementare correttamente. Da un semplice sito web di portfolio che viene declassato dai motori di ricerca fino ai gateway API pubblici che trasportano dati sensibili. Tutto deve essere verificato e crittografato.
Questo aumento di utilizzo deve però fare i conti con la complessità dell’implementazione tecnologica. SSL e successivamente TLS, con certificati firmati da CA pubbliche e implementazioni di PKI private con firma incrociata, sono sempre stati qualcosa che molti professionisti IT hanno faticato a comprendere e utilizzare correttamente. Sembravano aggiungere un sovraccarico difficilmente giustificabile.
Poi è arrivata l’automazione. Con l’approccio “automatizzare tutto”, i certificati TLS hanno ricevuto un’ulteriore spinta con tutti i tipi di API e script che consentivano la creazione, la distribuzione e la manutenzione dinamica dei certificati e delle autorità di certificazione interne complete.
Ma come sempre accade con l’automazione, lo strumento che risolve un problema non è sempre adatto a risolverne un altro. Quindi gli script e i servizi devono essere scelti per soddisfare le esigenze specifiche. Esiste tuttavia un caso semplice che copre la maggior parte degli usi, ovvero un umile certificato HTTPS. Se si crea un sito web, un’API REST o un endpoint per il download dei pacchetti di installazione, è necessario un certificato. Se si tratta di un servizio pubblico, è necessario che sia firmato da una CA pubblica. E se è nel cloud, dovete gestirlo dinamicamente. E se lo si fa, è meglio gestirlo “as code”.
Qui in Bitrock, quando si tratta di automazione, iniziamo prima con Terraform e valutiamo cosa possiamo aggiungere per raggiungere l’obiettivo di avere tutto come “infrastructure as code”. Ed è qui che iniziamo anche con i certificati. Una volta identificato, analizzato e risolto il caso d’uso, possiamo facilmente riutilizzarlo con Terraform in altri progetti. Il che, data la flessibilità dello strumento e le similitudini tra le piattaforme cloud, funziona il più delle volte. Questo articolo illustra il nostro approccio per automatizzare la gestione dei certificati come codice nel caso specifico di un servizio HTTP pubblico dietro un bilanciatore di carico nel cloud.
Un pò di contesto
Prima di iniziare l’implementazione del processo, è necessario un aggiornamento sui dettagli.
Cominciamo con l’Autorità di Certificazione (CA) che, per semplificare, è un fornitore di certificati digitali. Ci sono molti componenti in una CA, ma a noi interessa solo uno. Come consumatori di servizi, chiedete alla CA di certificare che siete proprietari di un qualcosa su Internet. Nella maggior parte dei casi si tratta di un nome di dominio. Ad esempio “bitrock.best”. Il risultato di questa certificazione è un certificato TLS firmato, di solito un file che tenete a portata del vostro server web. Il processo standard si svolge in tre iterazioni:
- Il consumatore genera una chiave privata e una richiesta di firma del certificato.
- Il consumatore invia la richiesta di firma del certificato alla CA
- La CA verifica la proprietà della cosa descritta nelle richieste e rilascia il certificato al consumatore.
Al richiedente vengono consegnati due elementi: la chiave privata e il certificato. Il certificato può essere letto da chiunque, ma può essere utilizzato per la crittografia solo dal proprietario della chiave privata. La chiave privata è ciò che deve essere mantenuto privato.
Let’s Encrypt
Poi è arrivata la gratuità
Mentre per le banche o le grandi aziende di e-commerce ha ancora senso, per un semplice sito web o servizio tutto è cambiato qualche anno fa, quando il progetto Let’s Encrypt è diventato pubblico. Il progetto ha costruito un protocollo e un fornitore di servizi che insieme permettono di avere un certificato firmato da una CA di fiducia pubblica con un paio di chiamate API.
Avere un certificato emesso e firmato da Let’s Encrypt sul proprio server è estremamente facile. Basta installare il pacchetto “certbot” utilizzando il proprio gestore di pacchetti ed eseguirlo. Se si utilizza un server web supportato, come apache o nginx, certbot lo imposterà per voi. Altrimenti, potete ottenere il certificato semplicemente indicando a certbot dove si trova la vostra web root e poi puntando la configurazione del server web al vostro certificato appena firmato e alla sua chiave privata.
L’uso “standard” di certbot, tuttavia, implica un server “standard” che non corrisponde al modello “cattle vs pets” dell’infrastruttura moderna. In un’architettura moderna, il nodo su cui gira il server web dovrebbe essere un elemento immutabile e monouso dell’architettura. Il certificato e la chiave dovrebbero essere configurati su un’entità esterna. Pensate a un’istanza di calcolo nel cloud e a un bilanciatore di carico nel cloud. Il bilanciatore di carico accetta le richieste dei clienti, esegue tutte le operazioni di terminazione TLS e inoltra la richiesta a qualsiasi istanza di calcolo disponibile.
Il caso d’uso sopra descritto elimina la possibilità di utilizzare certbot con la stessa facilità di un server “standard”. Il processo di verifica è più difficile da implementare utilizzando i file del server web e il processo certbot non ha accesso ai file dei certificati e delle chiavi. Questo costringe a un approccio diverso all’uso della verifica, basato sul DNS. Nel caso dei file, la verifica della proprietà si basa sul fatto che l’utente possiede il server web responsabile di servire il contenuto del nome di dominio. Un file con contenuto crittografico viene memorizzato dal certbot sul server e i server Let’s Encrypt lo cercano per verificare che il certbot sia effettivamente in esecuzione sul server web del nome di dominio. La verifica DNS utilizza la stessa crittografia, ma l’utente deve pubblicare un record TXT per il nome di dominio che sarà verificato da Let’s Encrypt per certificare la proprietà del nome di dominio.
Hashicorp Terraform, GCP e … Let’s Encrypt
Quanto sopra assomiglia molto ai requisiti tecnici: implementare un servizio web nel cloud per fornire servizi pubblici utilizzando HTTPS. Il certificato TLS deve essere emesso da Let’s Encrypt utilizzando la verifica DNS e la terminazione deve essere gestita dal bilanciatore di carico del cloud provider. Il deployment deve essere eseguito con terraform senza operazioni manuali che interrompano il processo.
Per soddisfare i requisiti, utilizzeremo i servizi di GCP e il provider google di HashiCorp per il provisioning dell’infrastruttura. Poi useremo il Cloud DNS di GCP per configurare i record utilizzando un eccellente provider di protocollo ACME di terraform. Terraform Cloud si occuperà dello stato in modo da tenerlo separato dall’infrastruttura che descrive.
La società di registrazione dei nomi di dominio utilizzata ha una propria API implementata, ma non sembra esistere un provider terraform per essa. Quindi possiamo usare uno script bash che sfrutta curl per configurare i nameserver del nome di dominio in modo che puntino a una zona appena creata nel Cloud DNS di GCP.
Il codice terraform risultante e tutti gli script sono disponibili sul github di Bitrock.
[enlighter lang=”generic”]./
├── cert-gcp.tf
├── domain.tf
├── gcp.tf
├── LICENSE
├── providers.tf
├── README.md
├── scripts
│ └── startup-script.sh
├── terraform.tfvars
└── variables.tf
1 directory, 9 files
[/enlighter]
Cosa abbiamo fatto
Abbiamo separato l’infrastruttura cloud in un semplice file Terraform che contiene tutte le risorse specifiche di Google. Questo avvicina la soluzione al modello multi-cloud, rendendo l’infrastruttura facilmente sostituibile. Il layout esatto dovrebbe essere costruito secondo lo schema dei moduli. Il che non dovrebbe essere un problema da rifattorizzare e integrare. Per riassumere l’infrastruttura, ecco cosa viene fornito come risorse nel nostro progetto GCP:
- rete, subnet e firewall
- un gestore di gruppi di istanze con un modello di istanza e uno script di avvio che prepara il nostro servizio web
- una zona DNS gestita
- un bilanciatore di carico che utilizza il gruppo di istanze come backend
- la risorsa di certificato utilizzata dal bilanciatore
[enlighter lang=”shell”]
# terraform.tfvars
# Domain name
domainname = “your-domain-name”
# GCP access
project_id = “GCP project id”
google_account_file = “path to the GCP credentials json”
# Registrar login
domain_user = “login”
domain_password = “password”
# Let’s encrypt registration and production endpoint
email_address = “you+acme@gmail.com”
le_endpoint = “https://acme-v02.api.letsencrypt.org/directory”
[/enlighter]
Quando si registra un nome di dominio si devono fornire dei nameserver validi che si suppone siano autorevoli per esso. Con GCP e alcuni altri provider cloud può essere un problema, poiché ogni zona creata ha i propri server autoritativi assegnati. Quindi, dopo la creazione della zona, i suoi server autoritativi devono essere impostati tramite la società di registrazione e il tutto deve attendere la propagazione della modifica. Lo gestiamo con una singola richiesta HTTP e un test di risoluzione DNS in un ciclo di controllo. Entrambi sono implementati come provisioner local-exec di una “null resource” nel file domain.tf.
[enlighter lang=”shell”]
# This is how our registrar can be called to update the nameservers. YMMV
curl ‘https://coreapi.1api.net/api/call.cgi?s_login=login&s_pw=password&command=ModifyDomain&domain=your-domain-name&nameservers’
# And now we wait
while true; do
dig +trace ns your-domain-name | grep ‘^your-domain-name\.’ | grep your-new-namserver && exit 0
echo Waiting for nameservers to be updated …
sleep 15
done
# Checkhout domain.tf to see the complete usage
[/enlighter]
Una volta che la zona è attiva e i nameserver sono stati aggiornati, il provider ACME può procedere con la richiesta del certificato. La generazione del certificato è descritta nel file gcp-cert.tf che include la creazione di due chiavi, una per la registrazione di Let’s encrypt e l’altra per il certificato stesso. Essendo risorse e passate come argomenti, le chiavi saranno mantenute nello stato remoto sicuro su Terraform Cloud. Piccoli dettagli da tenere a mente:
- il TTL dei record creati (SOA, NS, A, ecc.) dovrebbe essere basso per evitare attese di propagazione e rendere raggiungibile il servizio il più velocemente possibile.
- Let’s Encrypt ha dei limiti di utilizzo, per cui bisogna prima giocare con l’endpoint di staging prima di effettivamente generare il certificato nell’ambiente di produzione
- per configurare correttamente il TLS di un LB, non dimenticare di aggiungere la catena di certificati (i certificati dell’emittente).
Una volta che tutto è a posto, puntate il browser su https://your-domain-name e vedrete l’icona di un lucchetto felice e la vostra faccia sorridente.
Authors: Michael Tabolsky & Francesco Bartolini, DevOps @ Bitrock