Usare certificati Let's Encrypt con Azure Application Gateway

di Cristian Civera,

Nello script precedente #160 abbiamo visto come utilizzare certificati SSL in maniera del tutto gratuita e gestita con Azure Front Door (FD). Abbiamo già anticipato che Application Gateway (AG) per certi versi si sovrappone a FD, ma, oltre ad avere un costo inferiore, si distingue per il suo utilizzo all'interno di una region e principalmente è nato per fare il routing delle richieste.

E' quasi d'obbligo ormai esporre gli endpoint con la cifratura TLS e sebbene AG supporti listener su HTTPS, purtroppo non è presente una funzionalità automatica di gestione automatizzata dei certificati SSL: dobbiamo manualmente rinnovarli e provvedere a caricarli su AG.

In questo script proponiamo una soluzione che ci permetta di creare e rinnovare automaticamente i certificati usando Let's Encrypt come authority. Questo servizio prevede la generazione gratuita di certificati, espone API per la loro creazione e la community è molto attiva nella realizzazione di tool di automazione. Nel caso di AG, l'idea prevede l'uso di uno script PowerShell che andremo ad eseguire direttamente in Azure Automation, in modo da programmare nel tempo la sua esecuzione. L'uso di un container blob ci permette inoltre di supportare la validazione del dominio.

Per iniziare il nostro ambiente Azure dev'essere così composto:
- Uno Storage Account avente un container con accesso pubblico;
- Un'istanza di Automation Account;
- Un'istanza di Application Gateway;
- Un dominio il cui CNAME punti a AG.

Il gateway deve disporre di una configurazione minima: un listener HTTP (quello HTTPS non è ancora necessario), un backend pool e una rule. Quest'ultima dev'essere di tipo Path based, poiché il primo passo da compiere è quello di configurare una redirection. Andando sulla rule troviamo il pulsante apposito.

Configuriamo una redirection temporanea il cui nome è libero, ma il path è /.well-known/acme-challenge/*. Questo è il percorso nel quale Let's Encrypt si aspetta di trovare il file necessario alla validazione del dominio. Il target è esterno e lo facciamo puntare al container pubblico indicato nei prerequisiti.

Salviamo e attendiamo che AG sia pronto. Per verificare che la configurazione sia corretta, possiamo caricare un file di test nel container e provare a raggiungerlo tramite AG. Quindi per esempio interroghiamo da browser http://test.cristiancivera.com/.well-known/acme-challenge/test.txt. Anche se il file non esiste dovremmo comunque vedere la redirection e la risposta 404 di Azure.

Passiamo ora a Automation Account sul quale dobbiamo prima di tutto abilitare Run as accounts accedendo all'apposita sezione. Accedendo poi alla sezione Modules gallery procediamo all'importazione dei moduli Az.Accounts, Az.Network, Az.Storage e Posh-ACME, in quest'ordine. Dobbiamo attendere qualche minuto prima che ognuno di essi possa essere installato grazie al precedente.

L'ultimo modulo è di terze parti e ci fornisce i comandi necessari all'utilizzo delle API di Let's Encrypt. Procediamo alla creazione di un run book nuovo ed inseriamo il seguente script.

Param(
[Parameter(mandatory=$true)]
[string]$domain,
[Parameter(mandatory=$true)]
[string]$contact,
[Parameter(mandatory=$true)]
[string]$storageResourceGroupName,
[Parameter(mandatory=$true)]
[string]$storageName,
[Parameter(mandatory=$true)]
[string]$storageContainer,
[Parameter(mandatory=$true)]
[string]$agResourceGroupName,
[Parameter(mandatory=$true)]
[string]$agName
)

# Ottiene accesso con l'utente che ha schedulato il runbook
$connection = Get-AutomationConnection -Name AzureRunAsConnection
Login-AzAccount -ServicePrincipal -Tenant $connection.TenantID -ApplicationID $connection.ApplicationID -CertificateThumbprint $connection.CertificateThumbprint

# Imposta l'ambiente di produzione
# LE_STAGE per lo staging ed effettuare test
Set-PAServer LE_PROD

# Controlla l'esistenza di un account
$accounts = Get-PAAccount -List -Contact $contact -Refresh
if ($accounts.length -eq 0) {
    $account = New-PAAccount -AcceptTOS -Contact $contact -Force
}
else {
    $account = $accounts[0]
}
Set-PAAccount $account.Id

# Crea l'ordine
New-PAOrder $domain

# Ottieni i dati sull'ordine
$auths = Get-PAOrder | Get-PAAuthorizations

$toPublish = $auths | Select @{L='Name';E={$_.HTTP01Token}}, `
                             @{L='Body';E={Get-KeyAuthorization $_.HTTP01Token (Get-PAAccount)}}

# Crea un file contenente il token
$filePath = $env:TEMP + "\" + $toPublish.Name
$toPublish.Body | Out-File -Encoding ascii $filePath

# Copia il file sullo storage
$blobName = ".well-known\acme-challenge\" + $toPublish.Name
$storageAccount = Get-AzStorageAccount -ResourceGroupName $storageResourceGroupName -Name $storageName
$ctx = $storageAccount.Context
Set-AzStorageBlobContent -File $filePath -Container $storageContainer -Context $ctx -Blob $blobName

# Chiedo la validazione del token
$auths.HTTP01Url | Send-ChallengeAck

# Controllo lo stato dell'ordine
Do
{
    $order = Get-PAOrder -Refresh
} While ($order.status -ne 'ready')

# Rimuovo il file dallo storage
Remove-AzStorageBlob -Container "public" -Context $ctx -Blob $blobName

# Ottengo il certificato del dominio
$cert = New-PACertificate $domain
$certPassword = 'poshacme'

# Recupero i riferimenti al gateway
$appgw = Get-AzApplicationGateway -ResourceGroupName $agResourceGroupName -Name $agName
$password = ConvertTo-SecureString -String $certPassword -Force -AsPlainText
# Cerco il vecchio certificato
$old = Get-AzApplicationGatewaySSLCertificate -Name $domain -ApplicationGateway $appgw -ErrorAction silentlycontinue
# Aggiorno o aggiungo il certificato
if ($old) {
    Set-AzApplicationGatewaySSLCertificate -Name $domain -ApplicationGateway $appgw -CertificateFile $cert.PfxFile -Password $password
} else {
    Add-AzApplicationGatewaySslCertificate -Name $domain -ApplicationGateway $appgw -CertificateFile $cert.PfxFile -Password $password
}
# Aggiorno l'ambiente
Set-AzApplicationGateway -ApplicationGateway $appgw -AsJob

Il codice è commentato nelle sue parti e fondamentalmente esegue i seguenti passaggi:
- Crea l'ordine di un nuovo certificato su Let's Encrypt;
- Prepara il file di validazione e lo carica sul container;
- Richiede la validazione del dominio;
- Recupera il certificato generato;
- Aggiunge o aggiorna il certificato tra quelli disponibili in AG.

Preparato lo script possiamo programmare la sua esecuzione o procedere immediatamente ad un primo lancio. I parametri da impostare sono volti ad indicare il resource group e il nome dello storage e del gateway.

Lo script viene eseguito piuttosto velocemente, ma dobbiamo attendere che AG non riporti più l'indicazione Updating. Fatta questa operazione possiamo procedere alla creazione di un nuovo listener di AG dedicato a HTTPS. Troveremo nella finestra di configurazione il certificato da noi creato che possiamo ad andare a selezionare.

Creato il listener possiamo di conseguenza creare la rule relativa e provare ad interrogare il nostro dominio tramite HTTPS, dove risponderà il nostro nuovo certificato.

E' fondamentale a questo punto eseguire lo script almeno ogni tre mesi, meglio una volta a settimana, al fine di evitare di trovarsi senza certificato valido. Con un ritmo così basso di esecuzioni, praticamente questa soluzione non ha costi per la creazione e il mantenimento del certificato.

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

I più letti di oggi