Skip to content

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