Phase 2 — OpenTofu
Provisionnement des 10 VMs via OpenTofu et le provider telmate/proxmox.
Logique de déploiement
Le code OpenTofu se trouve dans le dossier opentofu/ du dépôt homelab-proxmox.
Principe
OpenTofu provisionne les VMs (création, réseau, disque), mais ne les configure pas. Le cloud-init est volontairement minimal :
| Étape | Outil | Ce qui est fait |
|---|---|---|
| Création des VMs | OpenTofu | clone du template, CPU/RAM/disk, IP statique, clé SSH |
| Post-install minimal | Cloud-init | hostname, user mounik, qemu-guest-agent |
| Sécurisation | Ansible (Phase 3) | UFW, SSH port 2222, Fail2Ban, swap off |
| Services | Ansible (Phases 4+) | Vault, Traefik, Keycloak, GitLab… |
Arbre des fichiers
opentofu/
├── versions.tf # Versions Terraform & providers
├── providers.tf # Configuration du provider Proxmox
├── variables.tf # Variables d'entrée (API, token, clé SSH)
├── locals.tf # Locaux communs (clé SSH)
├── terraform.tfvars.example # Exemple de variables
├── .gitignore # Fichiers terraform à ignorer
│
├── modules/
│ └── vm/ # Module standardisé de VM
│ ├── main.tf # Ressource proxmox_vm_qemu
│ ├── variables.tf # 14 variables (avec valeurs par défaut)
│ └── outputs.tf # IP, vmid, name, target_node
│
├── traefik.tf # Reverse proxy (pve01, .20)
├── gitlab.tf # GitLab CE (pve01, .21)
├── vault.tf # Vault (pve01, .22)
├── harbor.tf # Registry Docker (pve02, .30)
├── monitoring.tf # Prometheus/Grafana/Loki (pve02, .31)
├── keycloak.tf # SSO OIDC (pve02, .32)
├── defectdojo.tf # Vuln management (pve02, .33)
├── k3s-master.tf # Kubernetes master (pve03, .40)
├── k3s-worker01.tf # Worker 1 (pve03, .41)
├── k3s-worker02.tf # Worker 2 (pve03, .42)
│
└── outputs.tf # IPs de toutes les VMs
Module VM standardisé
Le module modules/vm encapsule la ressource proxmox_vm_qemu avec des valeurs par défaut sensibles :
module "vault" {
source = "./modules/vm"
name = "vault"
target_node = "pve01"
vmid = 103
cores = 1
memory = 2048
balloon = 1024
disk_size = "20G"
ip = "192.168.1.22"
ssh_key = local.ssh_key
}
Paramètres avec valeur par défaut (surchargeables si besoin) :
| Variable | Défaut | Description |
|---|---|---|
clone |
debian-13-cloud |
Template Proxmox |
gateway |
192.168.1.254 |
Passerelle |
cidr |
/24 |
Préfixe CIDR |
network_bridge |
vmbr0 |
Bridge réseau |
disk_storage |
ssd-vms |
Stockage |
disk_type |
scsi |
Type de disque |
Chaque appel de module produit les outputs ip, vmid, name, target_node.
Ordre et dépendances
Les VMs sont chaînées par depends_on pour respecter l'ordre logique :
vault (autonome, premier service)
└→ keycloak (dépend de Vault pour les secrets)
└→ gitlab (dépend de Keycloak pour l'OIDC)
└→ harbor (dépend de GitLab pour le registry mirror)
└→ monitoring (doit pointer Harbor comme registry)
└→ defectdojo (dépend de Monitoring)
└→ k3s-master (dernier, après tous les services)
└→ k3s-worker01
└→ k3s-worker02
traefik est autonome (pas de depends_on) car il peut démarrer sans dépendre d'un service en amont.
Plan d'adressage
| Nœud | VMs | Plage IP |
|---|---|---|
| pve01 (Control Plane) | traefik, gitlab, vault | .20 – .22 |
| pve02 (Data/Security) | harbor, monitoring, keycloak, defectdojo | .30 – .33 |
| pve03 (Kubernetes) | k3s-master, k3s-worker01, k3s-worker02 | .40 – .42 |
Voir sizing.md pour les specs détaillées (CPU/RAM/disk).
Variables sensibles
Les tokens Proxmox ne sont jamais commités. Ils sont passés via les variables CI/CD GitLab :
| Variable CI/CD | Usage |
|---|---|
PROXMOX_API_URL |
https://pve01:8006/api2/json |
PROXMOX_TOKEN_ID |
root@pam!opentofu |
PROXMOX_TOKEN_SECRET |
Secret du token |
Pipeline CI/CD
# .gitlab-ci.yml du dépôt homelab-proxmox
stages:
- plan
- apply
variables:
TF_ROOT: ${CI_PROJECT_DIR}
opentofu-plan:
stage: plan
image: alpine:latest
before_script:
- apk add --no-cache opentofu
script:
- cd ${TF_ROOT}
- tofu init
- tofu plan -out=tfplan
artifacts:
paths:
- tfplan
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
opentofu-apply:
stage: apply
image: alpine:latest
before_script:
- apk add --no-cache opentofu
script:
- cd ${TF_ROOT}
- tofu init
- tofu apply -auto-approve tfplan
rules:
- if: $CI_COMMIT_BRANCH == "main"
tofu plan s'exécute sur chaque MR, tofu apply sur la branche main uniquement.
Cloud-init
Le provider telmate/proxmox gère cloud-init nativement via les paramètres de la ressource :
| Paramètre | Rôle |
|---|---|
name |
Hostname de la VM |
sshkeys |
Clé SSH publique injectée dans authorized_keys |
ipconfig0 |
IP statique, CIDR, passerelle |
Les paramètres additionnels (paquets, utilisateur) ne sont pas nécessaires ici — Ansible les applique immédiatement après le provisionnement. La VM est volontairement minimale.
La sécurisation (UFW, SSH, Fail2Ban) et les services sont appliqués par Ansible dans les phases suivantes.
Pour aller plus loin
- Voir
opentofu/*.tf— 10 fichiers, un par VM - Voir
opentofu/modules/vm/— module réutilisable de VM Proxmox - Phase 3 — Hardening — sécurisation SSH/UFW/Fail2Ban
- Phase 1 — Proxmox — cluster et template Debian 13