Gestione dei domini e certificati in AWS con Terraform

di Andrea Zani, in terraform,

A riguardo questo post in cui, con Terraform, ho pubblicato una web application in Vue.js e una Lambda in Net6 in AWS, una persona - un amico - mi ha contestato che mi sono fermato sul più bello, e che il post è incompleto, perché non avevo spiegato come gestire eventualmente un dominio collegandolo automaticamente alle risorse web. Lo avevo scritto in quel post che non ero andato oltre perché non avevo sottomano un dominio utilizzabile per quel test. Ora l'ho, quindi è ora di completare quel post.

Passo numero 1: di cosa si ha bisogno

Per eseguire i passi di questo posto è necessario:

  • Account AWS
  • Dominio
  • Terraform cli installato
  • NPM
  • Dotnet versione 6
  • AWS .NET Core CLI

Rimando a quel post dove avevo spiegato dove e come procurarsi i vari tool (per l'account di AWS e del dominio ci si deve arrangiare).

Passo numero 2: collegare un dominio

Sono due alternative: utilizzare un dominio acquistato da altri siti oppure acquistarlo direttamente su AWS. Nel mio caso utilizzerò un dominio acquistato su un altro servizio, l'importante è che sia configurabile (modifica dei DNS name). Non serve altro. Per tutto questo blog io farò riferimento al dominio example.com che ovviamente non è quello che ho usato per i test che farò da qui in avanti. L'utilizzo da AWS del dominio comporta questi semplici passaggi: nella console di AWS si entra nella pagina principale del servizio Route 53 a questo link:

https://us-east-1.console.aws.amazon.com/route53/v2/hostedzones

Quindi cliccando su Create hosted zone si potrà inserire il nome del proprio dominio:

Selezionato come tipo Public hosted zone. Accettando viene creata la nuova Hosted zone per il dominio:

Ora è necessario configurare il dominio esterno in modo che punti ai server di AWS. E' sufficiente prendere i value della prima riga e inserire quei DNS nel pannello di configurazione che teoricamente il gestore del dominio mette a disposizione, per esempio:

Fine. Ora il dominio potrà essere gestito da AWS.

Passo numero 3: SSL

Facendo le cose per bene voglio che le risorse web che metterò online siano protette da un certificato SSL su protocollo TLS. Anche in questo caso AWS ha un pannello di configurazione apposito - AWS Certificate Manager - che permette di fare il tutto con pochi click del mouse. La sezione è disponibile a questa pagina:

https://us-east-1.console.aws.amazon.com/acm/home?region=us-east-1#/certificates/list

Per lo scopo di questo post è necessario creare il certificato nella Region us-east-1 (Virginia) perché dovendolo utilizzare con CloudFront solo da questa Region i cartificati sono accettati e visibili - non ho mai capito perché ma non si può fare nulla.

Da qui è sufficiente cliccare su Request. Nel passo successivo selezionare Request a public certificate, e proseguendo si avrà questa schermata:

Come Domain Name inserire sia entrambe le voci, example.com e *.example.com per permettere l'uso anche dei sotto domini. Lasciando come validazione la voce DNS validation, confermare il tutto - è quasi finita. Ora per validare il dominio sarà sufficiente aggiungere dei nuovi record di tipo CNAME nel dominio prima creato. Tornati alla sezione della lista dei certificati si vedrà che la richiesta è in Pending valitation. Cliccato sul Certificate Id appena creato nella schermata apparirà il record da inserire ma anche il pulsante Create records in Route 53 che esegue l'operazione senza dover copiare/incollare a mano i codici:

Ora rimane che aspettare qualche minuto per tornare nella lista dei certificati e vedere la colonna prima in Pengind Validation diventare Issued.

Passo numero 4: impostare manualmente il tutto

E' il momento di riprendere il progetto precedente e rieseguire tutte le operazioni presenti anche nell'MD file. Se si è fatto tutto correttamente alla fine verrà mostrato un link come questo:

https://d3lokx2nkx16nd.cloudfront.net/

Che utilizzato nel browser visualizzerà la web application in Vue.js. Ora voglio poter richiamare la stessa pagina ma con il mio dominio example.com. Il primo passo è andare in CloudFront ed eseguire una modifica nella configurazione:

Ho aggiunto un Alternate domain name (CNAME) quindi in Custom SSL Certificate ho selezionato dal dropdownlist il certificato creato precedentemente.

Secondo passo: dalla pagina di Route 53 in AWS è sufficiente entrare nella hosted zone creata nei passi precedenti e aggiungere  un nuovo record:

In record name lascio vuoto, come l'immagine, quindi selezionato il record type A, seleziono l'Alias che permetterà di selezionare dal dropdownlist l'Alias to CloudFront Distribution. Qui si può vedere perché è stato creato il certificato precedentemente in Virginia, quindi nella Textbox sottostante ho inserito l'Id della distribuzione in CloudFront della web application. Cliccato su Create Record posso provare che tutto funzioni dal browser richiamando il link:

https://example.com

Ho ovviamente nascosto il dominio originale, in ogni caso la pagina risulta protetta in HTTPS e il certificato risulta valido. Ma è giunto il momento di fare queste ultime operazione con Terraform.

Passo numero 5: Terraform

I passi per l'aggiunta del dominio e la creazione del certificato mostrati nei passi due e tre li ho fatti a mano e non con Terraform perché è un'operazione che si fa una sola volta e non si tocca più nulla. Altra storia configurare le risorse create perché utilizzino il dominio e il certificato. Innanzitutto dovrò fare in modo che Terraform possa accedervi. L'argomento lo avevo già trattato in uno dei post precedenti, ed è semplice. Per esempio, per avere le informazioni del dominio posso scrivere questo script in Terraform:

data "aws_route53_zone" "myzone" {
  name = "example.com"
}

output "route53-1" {
  description = "Route 53"
  value = data.aws_route53_zone.myzone
}

Per avere queste informazioni in output:

route53-1 = {
  "arn" = "arn:aws:route53:::hostedzone/Z0780463214RLPAW5X5JH"
  "caller_reference" = "063fe79c-5bb5-49b3-bf42-9d9c630e4e59"
  "comment" = "test with my domain"
  "id" = "Z0953413214RLPAW5X5ZZ"
  "linked_service_description" = tostring(null)
  "linked_service_principal" = tostring(null)
  "name" = "example.com"
  "name_servers" = tolist([
    "ns-1355.awsdns-41.org",
    "ns-1007.awsdns-61.net",
    "ns-1990.awsdns-56.co.uk",
    "ns-181.awsdns-22.com",
  ])
  "private_zone" = false
  "resource_record_set_count" = 3
  "tags" = tomap({})
  "vpc_id" = tostring(null)
  "zone_id" = "Z0953413214RLPAW5X5ZZ"
}

La stessa cosa per il certificato:

data "aws_acm_certificate" "cert" {
  domain   = "example.com"
  provider = aws.us-east-1
}

output "certificate" {
  description = "Certificate"
  value = data.aws_acm_certificate.cert
}

Che darà come output:

certificate = {
  "arn" = "arn:aws:acm:us-east-1:############:certificate/cb45d03e-####-####-####-43242302b735"
  "domain" = "example.com"
  "id" = "arn:aws:acm:us-east-1:############:certificate/cb45d03e-####-####-####-43242302b735"
  "key_types" = toset(null) /* of string */
  "most_recent" = false
  "status" = "ISSUED"
  "statuses" = tolist(null) /* of string */
  "tags" = tomap({})
  "types" = tolist(null) /* of string */
}

Ora ho tutte le informazioni per creare i nuovi record in Route 53 dalle Distribution di CloudFront come ho fatto prima, ma da codice. Riprendo ancora l'esempio del post precedente dedicato a Terraform e farò piccoli cambiamenti per l'aggiunta della gestione dei record del dominio e del certificato. Inoltre semplificherò il nome della API Rest che non sarà più dinamica (l'url lo prendevo da CloudFront) ma sarà statico visto che utilizzerò il Domain creato sopra. Alla fine delle nuove modifiche avrò:

  • https://example.com che mostrerà la pagina html con la web application in Vue.js.
  • https://www.example.com che mostrerà la stessa pagina del punto precedente.
  • https://api.example.com che sarà l'url per l'API Rest che eseguirà il calcolo.

Creo quindi un nuovo file nella directory dove sono presenti i file .tf creati precedentemente dal nome route53.tf. Innanzitutto devo fare un'altra modifica, perché, come scritto sopra, devo accedere al certificato nella Region us-east-1 (Virginia), mentre i miei script in Terraform sono impostati per la Region eu-south-1 (Milan). Al file providers.tf che contiene queste righe:

provider "aws" {
  region = "eu-south-1"
}

Aggiungo:

provider "aws" {
  region = "us-east-1"
  alias = "us-east-1"
}

L'alias mi permetterà di scegliere quale Region utilizzare. Se una risorsa dovrà essere creata nella Region us-east-1 dovrò scrivere:

resource "resource_type" "name" {
  ...
  provider = aws.us-east-1
}

Altrimenti sarà usata la Region di default. Ora posso aggiungere al file route53.tf il codice già visto prima per l'accesso alle informazioni del dominio e del certificato in AWS:

data "aws_route53_zone" "myzone" {
  name = "example.com"
}

data "aws_acm_certificate" "cert" {
  domain   = "example.com"
  provider = aws.us-east-1
}

Ora creo il primo record in Route 53 come ho mostrato sopra. Da codice:

resource "aws_route53_record" "www" {
  zone_id = data.aws_route53_zone.myzone.zone_id
  name    = "example.com"
  type    = "A"

  alias {
    name                   = aws_cloudfront_distribution.s3_distribution.domain_name
    zone_id                = aws_cloudfront_distribution.s3_distribution.hosted_zone_id
    evaluate_target_health = false
  }

  provider = aws.us-east-1
}

Qui in zone_id ho preso i dati dall'oggetto aws_route53_zone, quindi nella sezione per l'alias ho inserito le informazioni create nella sezione per CloudFront.

Ora creo un altro alias in modo che da browser si possa richiamare anche il dominio www.example.com:

resource "aws_route53_record" "www2" {
  zone_id = data.aws_route53_zone.myzone.zone_id
  name    = "www.example.com"
  type    = "A"

  alias {
    name                   = aws_route53_record.www.fqdn
    zone_id                = aws_route53_record.www.zone_id
    evaluate_target_health = false
  }

  provider = aws.us-east-1
}

E questa volta i ferimenti all'alias li prendo dalla risorsa creata per il dominio principale. Mi rimane l'ultimo passo: aggiungere il sotto dominio api.example.com per l'API che esegue il calcolo:

resource "aws_route53_record" "api" {
  zone_id = data.aws_route53_zone.myzone.zone_id
  name    = "api.example.com"
  type    = "A"

  alias {
    name                   = aws_cloudfront_distribution.distribution.domain_name
    zone_id                = aws_cloudfront_distribution.distribution.hosted_zone_id
    evaluate_target_health = false
  }

  provider = aws.us-east-1
}

Così come per il dominio principale ho creato il riferimento al CloudFront creato per la Lambda. La sezione in Route 53 è sistemata, ora devo fare delle piccole modifiche alle due configurazioni per CloudFront per l'aggiunta del dominio e del certificato così come ho fatto prima dalla console di AWS. Nel file 1-Lambda.tf ho aggiunto in coda al file:

  aliases = ["${var.apisubdomain}.${var.domain}"] # <- api.example.com
  viewer_certificate {
    ssl_support_method = "sni-only"
    acm_certificate_arn = data.aws_acm_certificate.cert.arn
  }

Come fatto precedentemente, ho inserito un nuovo alias con le informazioni sul dominio da usare e il certificato. La stessa cosa la faccio nel file 2-site.tf;

  aliases = [var.domain, "www.${var.domain}"] # <- example.com, www.example.com
  viewer_certificate {
    ssl_support_method = "sni-only"
    acm_certificate_arn = data.aws_acm_certificate.cert.arn
  }

Manca l'ultimo passo: l'url inserito dinamicamente della API all'interno della web application in Vue.js. Ora inserirò direttamente il dominio in modo statico nel file url.json da Terraform.

Ok ho finito. Richiamando i comandi per la creazione delle risorse in Terraform ora potrò visualizzare la web application con il dominio e con la protezione del protocollo HTTPS, così come per l'API. Alla fine avrò un risultato come il seguente:

cloudfront-site-url = "https://d1i3333wfmkh3h.cloudfront.net"
cloudfront_lambda_url = "https://d3lt9xfmkycdkh.cloudfront.net/api/calc/add?x=5&y=3"
site-domain-url = "https://example.com"
site-domain-url-alternative = "https://www.example.com"
site-domain-url-api = "https://api.example.com/api/calc/add?x=5&y=3"

Conclusioni

Qui il codice sorgente (è lo stesso repository del post precedente ma ho creato un altro Branch). Oltre al codice per la Lambda in Net6 (da compilare prima di usare terraform) è presente anche il codice della web application in Vue.js (anch'esso da buildare). Nel file README.md nel Repository sono presenti tutti i passi per queste operazioni. Nella directory Terraform sono presenti tutti i file per la costruzione delle risorse in AWS. Nel file variables.tf sono presenti le variabili da modificare dove inserire il dominio in proprio possesso e altre piccole personalizzazioni. Ora si può dire completo?

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.

Nella stessa categoria
I più letti del mese