# 02 — Provisioning Hetzner **Durée : environ 1 h.** Création des serveurs, du réseau privé, des firewalls et du volume de données, entièrement décrite en Terraform. > Le fichier de prévisions de coûts note explicitement le risque > « bus factor DevOps — Terraform/IaC obligatoire ». C'est la raison d'être de > ce dossier : l'infrastructure est reconstructible par quelqu'un d'autre, à > partir du dépôt seul. --- ## 1. Token API Hetzner Console Hetzner → projet `xpeditis-prod` → **Security → API tokens** → *Generate API token*. - Description : `terraform-prod` - Permissions : **Read & Write** Le token n'est affiché **qu'une fois**. Copiez-le dans votre gestionnaire de mots de passe immédiatement. Créez un **second token** nommé `github-actions-cicd`, également Read & Write. Il servira uniquement au firewall temporaire de la CI, ce qui permet de le révoquer sans casser Terraform. --- ## 2. Configuration ```bash cd infra/prod/terraform cp terraform.tfvars.example terraform.tfvars $EDITOR terraform.tfvars ``` À renseigner : ```hcl hcloud_token = "" ssh_public_key = "ssh-ed25519 AAAA... xpeditis-prod-admin" # cat ~/.ssh/xpeditis_prod.pub admin_ip_allowlist = ["/32"] # curl -s https://ifconfig.me ``` Le reste des valeurs par défaut correspond au dimensionnement de la phase 1 : `cpx41` pour l'application, `cpx31` pour les données, volume de 50 Go, sauvegardes Hetzner activées. > `terraform.tfvars` est ignoré par Git (`infra/prod/.gitignore`). Vérifiez-le > avant tout commit : `git status` ne doit pas le mentionner. --- ## 3. Vérifier avant d'appliquer ```bash make -C .. tf-init # ou : terraform init make -C .. tf-plan # ou : terraform plan ``` Le plan doit annoncer **8 ressources à créer** : ``` hcloud_network.main hcloud_network_subnet.main hcloud_ssh_key.admin hcloud_placement_group.spread hcloud_firewall.app hcloud_firewall.db hcloud_firewall.cicd hcloud_server.app hcloud_server.db hcloud_volume.pgdata ``` Trois points à relire dans le plan : 1. `hcloud_firewall.app` — les règles 22 et 6443 portent **uniquement** votre IP. Si vous y voyez `0.0.0.0/0`, arrêtez tout. 2. `hcloud_firewall.db` — **seul le port 22** est ouvert. Ni 5432, ni 6379. 3. `hcloud_firewall.cicd` — **aucune règle**. C'est normal : la CI les injecte puis les retire. --- ## 4. Appliquer ```bash make -C .. tf-apply ``` Environ 90 secondes. Puis : ```bash terraform output ``` Notez précieusement : ``` app_public_ipv4 = "..." → tous les enregistrements DNS Cloudflare db_public_ipv4 = "..." → SSH d'administration UNIQUEMENT, jamais en DNS db_private_ip = "10.10.1.20" pgdata_volume_device = "/dev/disk/by-id/scsi-0HC_Volume_..." ``` --- ## 5. Vérifier l'accès ```bash ssh -i ~/.ssh/xpeditis_prod deploy@ 'hostname; uptime' ssh -i ~/.ssh/xpeditis_prod deploy@ 'hostname; uptime' ``` cloud-init met une à deux minutes après la création du serveur. Si la connexion est refusée, attendez puis vérifiez : ```bash ssh -i ~/.ssh/xpeditis_prod deploy@ 'ls -l /var/log/cloud-init-xpeditis-done' ``` Le réseau privé doit fonctionner dans les deux sens : ```bash ssh deploy@ 'ping -c2 10.10.1.20' ``` --- ## 6. Confirmer que la base est bien inaccessible **Depuis un autre réseau que votre IP d'administration** (partage de connexion mobile, par exemple) : ```bash nmap -Pn -p 22,80,443,5432,6379,6443 ``` Attendu : **tous les ports `filtered`**, y compris le 22 — puisque vous testez depuis une IP non autorisée. ```bash nmap -Pn -p 22,80,443,5432,6379,6443 ``` Attendu à ce stade : `80` et `443` ouverts uniquement si `restrict_http_to_cloudflare = false`. Avec la valeur par défaut `true`, ils apparaissent aussi `filtered` tant que la requête ne vient pas de Cloudflare — c'est exactement l'effet recherché. --- ## 7. Storage Box La Storage Box se commande séparément (Robot Hetzner, pas la console Cloud). 1. Commandez une **BX11** (1 To, 3,90 €/mois). 2. Dans son interface : activez **SSH** et **désactivez** Samba/CIFS et WebDAV, inutiles ici et exposés sur Internet. 3. Déposez la clé publique dédiée : ```bash # Depuis votre poste ssh-copy-id -p 23 -i ~/.ssh/xpeditis_storagebox.pub uXXXXXX@uXXXXXX.your-storagebox.de # Créez l'arborescence ssh -p 23 -i ~/.ssh/xpeditis_storagebox uXXXXXX@uXXXXXX.your-storagebox.de \ 'mkdir -p xpeditis-prod/dumps' ``` La clé **privée** correspondante devra être déposée sur db-01 en `/root/.ssh/storagebox` ([04 — Nœud de données](./04-noeud-donnees.md)). > **Activez les *snapshots* de la Storage Box** (gratuits, dans son interface). > Ils protègent contre le cas où un rançongiciel présent sur db-01 chiffrerait > ou effacerait aussi les sauvegardes distantes via la connexion SSH existante. --- ## 8. Sauvegarder l'état Terraform `terraform.tfstate` décrit toute votre infrastructure. Il est gitignoré à dessein (il contient des données sensibles), mais **le perdre signifie que Terraform ne reconnaîtra plus vos ressources** et proposera de tout recréer. ```bash # Sauvegarde chiffrée dans le dépôt sops -e terraform.tfstate > ../terraform-state-backup.sops.json ``` Ou, mieux, migrez vers un backend S3 sur Hetzner Object Storage : le bloc `backend "s3"` est prêt, en commentaire, dans `versions.tf`. --- ## 9. Contrôle ``` [ ] terraform apply passé sans erreur [ ] SSH fonctionne sur app-01 et db-01 avec la clé d'administration [ ] Le ping app-01 → 10.10.1.20 répond [ ] nmap depuis une IP non autorisée : tout est filtered [ ] Storage Box commandée, SSH activé, Samba/WebDAV désactivés [ ] Snapshots de la Storage Box activés [ ] État Terraform sauvegardé [ ] Les deux IP publiques notées dans le gestionnaire de mots de passe ``` → **Suite : [03 — Durcissement des serveurs](./03-durcissement-serveurs.md)**