Les trois environnements passent sur enervision-g3.duckdns.org, rec. et dev. : noms publics qui visent l'IP privée de la VM, donc résolus sans /etc/hosts sur le réseau de l'école et injoignables ailleurs (ADR 0018). - infra/front : nginx sur le réseau de l'hôte, seul exposé en 80 et 443. Aiguille par SNI vers la stack visée sans déchiffrer le TLS, et lui transmet l'IP du client en PROXY protocol. - Proxy de stack : écouteur 4443 en PROXY protocol, real_ip_header ; sans lui, limit_req et get_client_ip() compteraient tous les postes comme un seul. PROXY_FRONT_PORT le publie sur 127.0.0.1. - make tls-duckdns : Let's Encrypt par défi DNS-01 via l'API DuckDNS (acme.sh 3.1.6), rejouable, rejoué à chaque déploiement et chaque nuit. - provision-host.sh fait foi pour l'adressage et les secrets : un .env existant garde ses secrets, reçoit ceux qui manquent (supervision) et voit hôte et ports réalignés. Planifie le renouvellement des certificats. - deploy.yml : nouvelles URL, sonde prod sur 10443, front-up en prod. - Terraform : variable domaine. CI : validation du frontal.
140 lines
5.1 KiB
Terraform
140 lines
5.1 KiB
Terraform
# Pourquoi : Terraform provisionne la machine, GitHub Actions la deploie (ADR 0010). Rien ici ne
|
|
# construit d'image ni ne lance de conteneur : la livraison reste portee par `deploy.yml` et
|
|
# `make stack-up`, et un `apply` n'interrompt pas la stack qui tourne.
|
|
# Piege : seul le bloc `triggers` d'un `null_resource` atterrit dans le state. Ni le jeton du
|
|
# runner ni la cle SSH n'y figurent, et ne doivent jamais y etre ajoutes pour forcer un rejeu.
|
|
# Contrainte : pas de provisioner `destroy` sur le runner. Il imposerait une connexion ne lisant
|
|
# que `self`, donc le chemin de la cle SSH dans le state, et `svc.sh uninstall` ne desinscrit pas
|
|
# le runner cote GitHub : le retrait reste manuel, depuis les parametres du depot.
|
|
# Ref : ADR 0009 et 0017 pour les trois environnements, `scripts/provision-host.sh` pour leur contenu.
|
|
|
|
locals {
|
|
sudo = var.ssh_user == "root" ? "" : "sudo "
|
|
en_tant_que = "${var.ssh_user == "root" ? "" : "sudo "}runuser -u ${var.proprietaire} --"
|
|
provisionneur = "${path.root}/../../../../scripts/provision-host.sh"
|
|
runner_archive = "actions-runner-linux-x64-${var.runner_version}.tar.gz"
|
|
# Substitution shell, evaluee par le sh -c distant : un nom de runner doit etre unique dans
|
|
# le depot, le nom d'hote l'est deja et le reste si cette racine sert a une autre machine.
|
|
runner_nom = var.runner_nom != "" ? var.runner_nom : "$(hostname -s)"
|
|
}
|
|
|
|
resource "null_resource" "docker_engine" {
|
|
triggers = {
|
|
hote = var.ssh_host
|
|
user = var.proprietaire
|
|
}
|
|
|
|
connection {
|
|
type = "ssh"
|
|
host = var.ssh_host
|
|
port = var.ssh_port
|
|
user = var.ssh_user
|
|
private_key = file(pathexpand(var.ssh_private_key_path))
|
|
timeout = "5m"
|
|
}
|
|
|
|
provisioner "remote-exec" {
|
|
inline = [
|
|
<<-EOT
|
|
set -eu
|
|
id ${var.proprietaire} >/dev/null 2>&1 || {
|
|
echo "l'utilisateur ${var.proprietaire} n'existe pas sur la machine" >&2
|
|
exit 1
|
|
}
|
|
command -v docker >/dev/null || ${local.sudo}sh -c 'curl -fsSL https://get.docker.com | sh'
|
|
${local.sudo}systemctl enable --now docker
|
|
${local.sudo}usermod -aG docker ${var.proprietaire}
|
|
${local.sudo}docker compose version
|
|
EOT
|
|
]
|
|
}
|
|
}
|
|
|
|
# `provision-host.sh` verifie lui-meme docker, compose et la sortie HTTPS, puis prepare un clone
|
|
# par environnement, son `.env` et son certificat. Il est rejouable : un `.env` existant n'est
|
|
# jamais reecrit, un certificat present jamais regenere.
|
|
resource "null_resource" "environnements" {
|
|
depends_on = [null_resource.docker_engine]
|
|
|
|
triggers = {
|
|
script = filesha256(local.provisionneur)
|
|
racine = var.racine
|
|
depot = var.depot_url
|
|
domaine = var.domaine
|
|
}
|
|
|
|
connection {
|
|
type = "ssh"
|
|
host = var.ssh_host
|
|
port = var.ssh_port
|
|
user = var.ssh_user
|
|
private_key = file(pathexpand(var.ssh_private_key_path))
|
|
timeout = "5m"
|
|
}
|
|
|
|
provisioner "file" {
|
|
source = local.provisionneur
|
|
destination = "/tmp/provision-host.sh"
|
|
}
|
|
|
|
provisioner "remote-exec" {
|
|
inline = [
|
|
<<-EOT
|
|
set -eu
|
|
${local.sudo}env RACINE='${var.racine}' \
|
|
REPO_URL='${var.depot_url}' \
|
|
PROPRIETAIRE='${var.proprietaire}' \
|
|
DOMAINE='${var.domaine}' \
|
|
PUBLIC_IP='${var.adresse_publique}' \
|
|
bash /tmp/provision-host.sh
|
|
rm -f /tmp/provision-host.sh
|
|
EOT
|
|
]
|
|
}
|
|
}
|
|
|
|
# Piege : le jeton d'enregistrement expire en une heure. Un `apply` rejoue cette ressource des
|
|
# que `runner_version`, `runner_labels` ou `runner_nom` change, et redemande donc un jeton frais.
|
|
resource "null_resource" "runner_github" {
|
|
depends_on = [null_resource.environnements]
|
|
|
|
triggers = {
|
|
version = var.runner_version
|
|
labels = var.runner_labels
|
|
nom = local.runner_nom
|
|
dossier = var.runner_dossier
|
|
}
|
|
|
|
connection {
|
|
type = "ssh"
|
|
host = var.ssh_host
|
|
port = var.ssh_port
|
|
user = var.ssh_user
|
|
private_key = file(pathexpand(var.ssh_private_key_path))
|
|
timeout = "5m"
|
|
}
|
|
|
|
provisioner "remote-exec" {
|
|
inline = [
|
|
<<-EOT
|
|
set -eu
|
|
${local.sudo}install -d -o ${var.proprietaire} -g ${var.proprietaire} ${var.runner_dossier}
|
|
if [ ! -x ${var.runner_dossier}/config.sh ]; then
|
|
curl -fsSL -o /tmp/${local.runner_archive} \
|
|
https://github.com/actions/runner/releases/download/v${var.runner_version}/${local.runner_archive}
|
|
${local.sudo}tar -xzf /tmp/${local.runner_archive} -C ${var.runner_dossier}
|
|
${local.sudo}chown -R ${var.proprietaire}:${var.proprietaire} ${var.runner_dossier}
|
|
rm -f /tmp/${local.runner_archive}
|
|
fi
|
|
if [ ! -f ${var.runner_dossier}/.runner ]; then
|
|
${local.en_tant_que} sh -c 'cd ${var.runner_dossier} && ./config.sh --unattended --replace \
|
|
--url ${var.runner_url} --token ${var.runner_token} \
|
|
--labels ${var.runner_labels} --name ${local.runner_nom} --work _work'
|
|
${local.sudo}${var.runner_dossier}/svc.sh install ${var.proprietaire}
|
|
fi
|
|
${local.sudo}${var.runner_dossier}/svc.sh start
|
|
EOT
|
|
]
|
|
}
|
|
}
|