Terraform, Hetzner cloud, Route 53 e Let's Encrypt

August 05, 2018 0 Comments HTTPS, Terraform, Route53, Hetzner

Benvenuti in questa nuova guida!

Parlerò brevemente di un esempio di provisioning automatico di una macchina virtuale su Hetzner cloud (cloud hosting molto conveniente, link: https://www.hetzner.com/cloud) utilizzando:

  • il tool di Hashicorp Terraform
  • i DNS di amazon Route53 per il puntamento del record A
  • acme.sh per l'emissione del certificato ssl tramite let's encrypt utilizzando il sistema di issue tramite creazione di un record DNS ad hoc, appoggiandomi sempre al servizio di amazon Route53.

Il progetto di esempio lo trovate a questo link: https://github.com/nutellinoit/terraform-hcloud-route53-letsencrypt

Perché usare Terraform?

Terraform è un tool molto potente di Hashicorp che permette tramite un linguaggio dichiarativo di creare e gestire infrastrutture complesse. Riduce di molto le operazioni ripetitive che un sysadmin si troverebbe ad eseguire per ottenere sempre ambienti riproducibili.

Perché usare Route53?

Route53 è un servizio molto potente di amazon che permette di gestire la propria zona DNS tramite API. Ci sono anche altri provider DNS che permettono di fare la stessa cosa, come per esempio dnsimple. Ho scelto Route53 perché secondo me è molto conveniente quando si devono gestire pochi domini, oltre al fatto che permette anche di registrare e trasferire il proprio dominio direttamente su amazon.

Cominciamo vedendo il file main.tf

/* general */

provider "hcloud" {  
  token = "${var.hcloud_token}"
}

resource "hcloud_server" "host_nginxdemo" {  
  name        = "${format(var.hostname_format_nginxdemo, count.index + 1)}"
  location    = "${var.hcloud_location}"
  image       = "${var.image}"
  server_type = "${var.hcloud_type_nginxdemo}"
  ssh_keys    = ["${var.hcloud_ssh_keys}"]

  count = "${var.hosts_nginxdemo}"

  provisioner "remote-exec" {
    inline = [
      "while fuser /var/lib/apt/lists/lock >/dev/null 2>&1; do sleep 1; done",
      "apt-get update",
      "apt-get install -yq ufw ${join(" ", var.apt_packages_nginxdemo)}",
      "curl https://releases.rancher.com/install-docker/17.03.2.sh | sh",
      "curl https://get.acme.sh | sh",
      "docker run -d --restart=unless-stopped -p 81:2368 ghost"

    ]
  }


}
......

In questa prima parte, utilizziamo il provider hcloud per la creazione delle macchine virtuali.

Utilizziamo il token del progetto di hetzner cloud (scritto sul file di variabili terraform.tfvars) per creare una macchina virtuale sulla quale eseguiamo dei comandi con il provisioner remote_exec. Con questi comandi installiamo Nginx, Acme.sh, Docker e un container di Ghost per una demo.

Proseguamo nella lettura del file:

...

resource "null_resource" "add-conf-nginx-demo" {  
  count = "${var.hosts_nginxdemo}"

  connection {
    host = "${element(hcloud_server.host_nginxdemo.*.ipv4_address, count.index)}"
    type = "ssh"
    user = "root"
    private_key = "${file("~/.ssh/id_rsa")}"
  }

  provisioner "file" {
    source     = "template.vhosts.conf"
    destination = "/etc/nginx/conf.d/${var.hostame-demo}.conf"
  }

  provisioner "remote-exec" {
    inline = [
      "export  AWS_ACCESS_KEY_ID=${var.access_key}",
      "export  AWS_SECRET_ACCESS_KEY=${var.secret_key}",
      "sed -i 's/HOSTNAME/${var.hostame-demo}/g' /etc/nginx/conf.d/${var.hostame-demo}.conf",
      "sed -i 's#REVERSEURL#${var.reverse-demo}#g' /etc/nginx/conf.d/${var.hostame-demo}.conf",
      "~/.acme.sh/acme.sh --issue ${var.lets-staging} --dns dns_aws -d ${var.hostame-demo}",
      "service nginx reload",
    ]
  }

}
...

Utilizziamo quindi una risorsa di tipo null_resource, che ci permette di lanciare comandi arbitrari.

  1. Iniziamo impostando la connessione alla macchina utilizzando l'ip che ci darà in output la risorsa hcloud_server
  2. Copiamo all'interno il file template.vhosts.conf rinominandolo con la variabile del nostro nome dominio
  3. Impostiamo quindi le variabili d'ambiente che verranno utilizzate da acme.sh per l'emissione del certificato con l'access key e la secret key di amazon
  4. Eseguiamo dei sed al file template di nginx per sistemare le configurazioni secondo le variabili impostate
  5. Lanciamo acme.sh per l'emissione del certificato
  6. Riavviamo nginx

L'ultimo pezzo che manca da esaminare è il seguente

...
resource "aws_route53_record" "demo_nutellino_it" {  
  zone_id = "${var.zone_id}"
  name    = "demo"
  type    = "A"
  ttl     = "${var.ttl}"
  records = ["${hcloud_server.host_nginxdemo.*.ipv4_address}"]
}


output "hostnames_nginxdemo" {  
  value = ["${hcloud_server.host_nginxdemo.*.name}"]
}

output "public_ips_nginxdemo" {  
  value = ["${hcloud_server.host_nginxdemo.*.ipv4_address}"]
}

In quest'ultima parte, utilizziamo una risorsa aws_route53_record per creare il nostro record A demo.ilvostrodominio.tld (il dominio ilvostrodominio.it dipende dalla variabile impostata con la zone_id) puntandolo all'ip della macchina appena creata tramite il provider Hetzner.

Infine chiediamo a terraform di mandarci in output l'ip pubblico e l'hostname interno della macchina appena creata.

Possiamo quindi passare all'esecuzione di terraform

#Inizializziamo Terraform
terraform init  
#Avviamo la creazione delle macchine
terraform apply  

Di seguito un video di asciinema dell'esecuzione di questa configurazione terraform:

asciicast

Esempio del sito in ghost appena creato con questa procedura (ovviamente il certificato di let's encrypt usato è quello di staging per non superare i limiti di issue di veri certificati):

Questo conclude la guida! Se avete domande commentate qui sotto!

:)

Samuele Chiocca
Padova, italy Website