1.0.4 — contournement Cloudflare reellement applique
Terraform - plan / plan (push) Failing after 5s

This commit is contained in:
hcornet committed 2026-09-13 17:13:29 +02:00
1 parent 06f77d0e09
commit b8b0d26f6f
11 files changed
+340 -115

No files matched your search

+21
View File
@@ -66,6 +66,27 @@ jobs:
echo "SSH : $(terraform output -raw ssh_command)"
echo "cloud-init tourne encore 3 à 6 minutes après la fin de ce job."
- name: Extraire le mot de passe root
if: ${{ inputs.action == 'apply' }}
run: |
set -euo pipefail
umask 077
terraform output -raw root_password > mot-de-passe-root.txt
echo "Mot de passe root publie en artefact : a deposer dans Vaultwarden, puis supprimer l artefact."
- name: Publier le mot de passe root
if: ${{ inputs.action == 'apply' }}
continue-on-error: true
uses: actions/upload-artifact@v3
with:
name: mot-de-passe-root
path: mot-de-passe-root.txt
retention-days: 1
- name: Effacer la copie locale du mot de passe
if: ${{ always() }}
run: rm -f mot-de-passe-root.txt
- name: Publier l'inventaire Ansible
if: ${{ inputs.action == 'apply' }}
continue-on-error: true
+71 -75
View File
@@ -1,124 +1,120 @@
# CI/CD — `terraform-proxmox-serveur-debian-12`
# CI/CD — dépôt `centreon-terraform`
Trois workflows Gitea Actions, un script de lecture des secrets, un backend S3.
Aucun secret ne vit dans Gitea en dehors de quoi ouvrir OpenBao.
La chaîne s'arrête à la création du serveur. Ansible n'est pas déclenché par la CI :
il est joué depuis Semaphore, indépendamment.
| Workflow | Fichier | Déclencheur | Ce qu'il fait |
|---|---|---|---|
| plan | `.gitea/workflows/terraform-plan.yml` | pull request et push sur `main` | `fmt -check`, `validate`, `plan` — ne modifie rien |
| deploy | `.gitea/workflows/deploy.yml` | manuel, `action` = `plan` ou `apply` | plan enregistré puis appliqué si demandé |
| destroy | `.gitea/workflows/destroy.yml` | manuel, confirmation `DETRUIRE` | supprime la VM et son disque |
## Répartition
Les trois partagent un `concurrency` commun : deux exécutions ne peuvent pas se
disputer le verrou de l'état S3.
| Étape | Outil | Déclencheur |
|---|---|---|
| `fmt`, `validate`, `plan` | Gitea Actions | pull request et push sur `main` |
| `apply` | Gitea Actions | lancement manuel, `action=apply` |
| `destroy` | Gitea Actions | lancement manuel, confirmation littérale `DETRUIRE` |
| Installation Centreon | Semaphore | manuel, après la fin de cloud-init |
## 1. Préparer OpenBao
## Préalables
Le dépôt du snippet cloud-init passe par SSH, il faut donc le mot de passe du nœud
dans le secret Proxmox existant :
### 1. OpenBao
Une clé est à **ajouter au secret existant** `secret/proxmox` : le dépôt du snippet
cloud-init passe par SSH, le mot de passe du nœud est donc nécessaire.
```bash
bao kv patch secret/proxmox ssh_password='<mot de passe root du noeud pve>'
bao kv patch secret/proxmox ssh_password='<mot de passe root du nœud pve>'
```
Le secret doit contenir au final `api_url`, `api_token`, `node_name`, `ssh_password`.
L'adresse du nœud est déduite de `api_url`, il n'y a rien de plus à saisir.
Le secret doit finir par contenir : `api_url`, `api_token`, `node_name`, `ssh_password`.
L'adresse du nœud est déduite de `api_url`, il n'y a rien d'autre à saisir.
Politique et AppRole dédiés, en lecture seule sur les deux secrets utiles :
Politique et AppRole dédiés au projet, en lecture seule sur les deux secrets utiles :
```bash
bao policy write terraform-proxmox-debian-12 - <<'POLICY'
bao policy write centreon-lab - <<'POLICY'
path "secret/data/proxmox" { capabilities = ["read"] }
path "secret/metadata/proxmox" { capabilities = ["read"] }
path "secret/data/aws" { capabilities = ["read"] }
path "secret/metadata/aws" { capabilities = ["read"] }
POLICY
bao write auth/approle/role/terraform-proxmox-debian-12 \
token_policies=terraform-proxmox-debian-12 token_ttl=1h token_max_ttl=4h
bao write auth/approle/role/centreon-lab \
token_policies=centreon-lab token_ttl=1h token_max_ttl=4h
bao read auth/approle/role/terraform-proxmox-debian-12/role-id
bao write -f auth/approle/role/terraform-proxmox-debian-12/secret-id
bao read auth/approle/role/centreon-lab/role-id
bao write -f auth/approle/role/centreon-lab/secret-id
```
## 2. Renseigner Gitea
Le mot de passe admin de Centreon n'apparaît pas ici : il sert à Ansible, pas à
Terraform. Le stocker à part, par exemple `secret/centreon-lab` avec la clé
`centreon_admin_password`, et le récupérer côté Semaphore.
**Paramètres → Actions → Secrets** :
### 2. Secrets et variables Gitea
Gitea ne détient que de quoi ouvrir OpenBao.
**Secrets du dépôt** (Paramètres → Actions → Secrets) :
| Nom | Valeur |
|---|---|
| `OPENBAO_ROLE_ID` | sortie de `role-id` |
| `OPENBAO_SECRET_ID` | sortie de `secret-id` |
**Paramètres → Actions → Variables** :
**Variables du dépôt** (Paramètres → Actions → Variables) :
| Nom | Valeur |
| Nom | Exemple |
|---|---|
| `OPENBAO_ADDR` | `https://openbao.tips-of-mine.com` |
| `TF_BACKEND_BUCKET` | le bucket déjà utilisé par `lab-ad` |
| `TF_BACKEND_KEY` | `terraform-proxmox-serveur-debian-12/terraform.tfstate` |
| `TF_BACKEND_REGION` | la région du bucket |
| `TF_VERSION` | facultatif, `1.9.8` sinon |
| `TF_BACKEND_BUCKET` | nom du bucket S3 déjà utilisé par `lab-ad` |
| `TF_BACKEND_KEY` | `centreon-lab/terraform.tfstate` |
| `TF_BACKEND_REGION` | région du bucket |
| `TF_VERSION` | facultatif, `1.9.8` par défaut |
`TF_BACKEND_KEY` doit être différente de celle de `lab-ad` : deux projets sur la même
clé écraseraient mutuellement leur état.
`TF_BACKEND_KEY` doit être **différente** de celle de `lab-ad`, sans quoi les deux
projets se partageraient le même état.
Si votre version de Gitea n'expose pas encore les variables de dépôt, déclarez les
cinq entrées en secrets et remplacez `vars.` par `secrets.` dans les trois workflows —
ce sont des valeurs de configuration, pas des secrets, mais ça fonctionne pareil.
### 3. Bit d'exécution du script
## 3. Pousser le code
Le piège déjà rencontré sur `proxmox-create-vm` : le transfert depuis Windows perd le
bit d'exécution, et le job échoue sur un « Permission denied ».
```powershell
cd D:\Users\thedj\git\terraform-proxmox-serveur-debian-12
git add -A
git update-index --chmod=+x scripts/openbao-env.sh
git commit -m "CI/CD : plan, deploy, destroy"
git push
git commit -m "Rendre openbao-env.sh executable"
```
Le `--chmod=+x` n'est pas optionnel : le bit d'exécution se perd au transfert depuis
Windows et le job s'arrête sur un « Permission denied ». Le `.gitattributes` fige les
fins de ligne en LF pour la même raison — un script en CRLF échoue avec un
`bad interpreter` peu parlant.
Le fichier `.gitattributes` livré fige les fins de ligne en LF, pour la même raison.
## 4. Tester, dans cet ordre
### 4. Clé publique
**a. Le plan automatique.** Le push déclenche `Terraform - plan`. C'est le test le plus
utile : il valide la chaîne complète — AppRole, lecture des secrets, backend S3,
provider Proxmox — sans rien créer.
`lab.tfvars` est versionné et contient la clé publique d'administration : y remplacer
le `ssh-ed25519 AAAA...` d'exemple avant le premier `apply`. Sans quoi la VM sera
créée avec une clé inutilisable et il faudra la recréer — cloud-init ne rejoue rien.
Le journal doit afficher `Secrets chargés depuis OpenBao : Proxmox (4 valeurs),
backend S3 (2 valeurs)` puis un plan annonçant **4 ressources à ajouter** : l'image
Debian téléchargée, le snippet cloud-init, la VM, le fichier d'inventaire.
## Fonctionnement des workflows
**b. Le deploy en mode plan.** Lancer `Terraform - deployer la VM Debian` avec
`action=plan`. Même résultat, plus l'étape « Résumé du plan ». Cela vérifie le
déclenchement manuel et la construction du fichier `tfplan`.
`scripts/openbao-env.sh` s'authentifie en AppRole, lit les deux secrets et pousse dans
l'environnement du job : `TF_VAR_pve_endpoint`, `TF_VAR_pve_api_token`,
`TF_VAR_pve_node`, `TF_VAR_pve_node_address`, `TF_VAR_pve_ssh_password`,
`AWS_ACCESS_KEY_ID` et `AWS_SECRET_ACCESS_KEY`. Chaque valeur sensible est passée en
`::add-mask::` avant d'être exportée : elle apparaît en `***` si un outil l'affiche.
**c. Le deploy en mode apply.** Relancer avec `action=apply`. Compter 2 à 4 minutes,
dont le téléchargement de l'image Debian au premier passage. À la fin, le job affiche
l'adresse et la commande SSH.
Le script s'arrête avec un message explicite quand une clé manque dans OpenBao, plutôt
que de laisser Terraform échouer sur une authentification vide.
Puis, depuis votre poste :
`deploy.yml` calcule toujours un plan, et ne l'applique que si `action=apply` — c'est
le plan enregistré qui est appliqué, pas un second calcul. À la fin, l'inventaire
Ansible est publié en artefact (`inventaire-ansible`), utile surtout comme trace :
l'adresse étant fixe, Semaphore peut se contenter d'un inventaire statique.
```bash
ssh [email protected] "cloud-init status --wait && sudo nft list chain inet filter input"
```
## Côté Semaphore
La sortie ne doit montrer que `lo`, `established,related`, l'ICMP et le port 22.
La configuration Semaphore est décrite dans le dépôt qu'elle concerne :
`ansible/CICD.md`.
**d. Le destroy.** Lancer `Terraform - detruire la VM Debian` avec une confirmation
erronée d'abord : le job doit s'arrêter immédiatement sans rien toucher. Recommencer
avec `DETRUIRE`.
## Enchaînement d'un test complet
## Les pannes les plus probables au premier essai
| Symptôme | Cause |
|---|---|
| `Secret manquant dans OpenBao : secret/proxmox → ssh_password` | l'étape 1 n'a pas été faite |
| `permission denied` sur `./scripts/openbao-env.sh` | bit d'exécution perdu, voir étape 3 |
| `403` du provider Proxmox au plan | rôle du jeton incomplet : il faut `SDN.Use`, et `Datastore.AllocateTemplate` pour le téléchargement de l'image |
| `unable to create file ... snippets` | contenu `snippets` absent du stockage `local` : `pvesm set local --content images,rootdir,vztmpl,backup,iso,snippets` |
| `Error acquiring the state lock` | un job précédent est mort en cours ; lever le verrou avec `terraform force-unlock <id>` |
| La VM démarre mais SSH refuse la clé | `ssh_public_keys` de `lab.tfvars` non remplacé. cloud-init ne rejoue rien : détruire et recréer |
1. `deploy.yml` avec `action=apply` — environ 3 minutes.
2. Attendre la fin de cloud-init : `ssh [email protected] 'cloud-init status --wait'`.
3. Lancer le modèle Semaphore `site.yml` — 10 à 20 minutes.
4. Déposer les exports d'Enclume, relancer le modèle `--tags clapi`.
5. Pour repartir propre : `destroy.yml` avec la confirmation `DETRUIRE`, puis reprendre
à l'étape 1.
+74
View File
@@ -0,0 +1,74 @@
# Modèle de comptes
La machine n'a **pas** la commande `sudo` : elle est purgée par cloud-init et épinglée
en priorité négative dans APT, pour qu'aucune dépendance ne la réinstalle en douce.
L'escalade de privilèges passe donc par `su -`.
| Compte | Accès SSH | Privilèges | Usage |
|---|---|---|---|
| `hcornet` | clé | aucun | administration humaine, puis `su -` |
| `invite` | clé | aucun | consultation |
| `ansible` | clé | aucun | compte de service, sans droits |
| `root` | **clé uniquement** | tous | Ansible s'y connecte directement |
`PermitRootLogin prohibit-password` : root est joignable en SSH par clé, jamais par mot
de passe. Le mot de passe root ne sert donc qu'en local — `su -` depuis un compte
humain, ou la console Proxmox quand le réseau est cassé.
`AllowUsers` est généré à partir de la liste des comptes : un compte absent de
`local_accounts` ne peut pas ouvrir de session, même s'il existe sur le système.
## Les clés à produire
Deux paires restent à créer, la clé `hcornet` étant déjà la vôtre.
```powershell
ssh-keygen -t ed25519 -f $env:USERPROFILE\.ssh\id_ed25519_invite -C "invite@centreon"
ssh-keygen -t ed25519 -f $env:USERPROFILE\.ssh\id_ed25519_ansible -C "ansible@automatisation"
Get-Content $env:USERPROFILE\.ssh\id_ed25519_invite.pub
Get-Content $env:USERPROFILE\.ssh\id_ed25519_ansible.pub
```
Recopier ces deux lignes dans `lab.tfvars`, à la place des `REMPLACER par...`. La clé
d'automatisation apparaît **deux fois** : sur le compte `ansible` et dans
`root_ssh_public_keys`. C'est voulu — le compte `ansible` permet de tester la liaison
sans privilège, tandis que la connexion root sert aux configurations.
La clé privée d'automatisation ira dans le magasin de clés de Semaphore. Elle ne doit
pas rester sur votre poste une fois Semaphore configuré.
## Le mot de passe root
Il est généré par Terraform à la création de la VM, en 24 caractères. Il n'est **jamais
affiché dans les journaux** : le workflow `deploy` le publie en artefact
`mot-de-passe-root`, avec une rétention d'un jour.
Marche à suivre après un `apply` :
1. télécharger l'artefact depuis la page du job ;
2. déposer la valeur dans Vaultwarden ;
3. supprimer l'artefact depuis Gitea.
À savoir : ce mot de passe est conservé en clair dans l'état Terraform, sur S3. C'est la
contrepartie de la génération automatique. Si cela devient gênant, l'alternative est que
vous le choisissiez vous-même et ne fournissiez que son empreinte — le code passerait
alors d'un `random_password` à une variable `root_password_hash` produite par
`openssl passwd -6`, et plus rien de sensible ne transiterait.
## Conséquence pour Ansible
Le dépôt `centreon-ansible` doit être repris : ses rôles utilisent `become: true` avec
`sudo`, qui n'existe plus. La bascule consiste à se connecter en root et à retirer
l'escalade — `remote_user: root` et `become: false` dans le playbook, l'inventaire
généré par Terraform pointant déjà sur `root`.
## Vérifications après création
```bash
ssh [email protected] "id && command -v sudo || echo 'sudo absent, conforme'"
ssh -i ~/.ssh/id_ed25519_ansible [email protected] "hostname -f"
```
La première commande doit montrer un compte sans groupe `sudo` et confirmer l'absence
de la commande. La seconde valide la voie d'accès d'Ansible.
+26 -7
View File
@@ -1,5 +1,22 @@
locals {
fqdn = "${var.vm_hostname}.${var.dns_domain}"
# root d'abord, puis les comptes humains et de service : c'est la liste
# exhaustive des comptes autorisés à ouvrir une session SSH.
allow_users = join(" ", concat(["root"], [for compte in var.local_accounts : compte.name]))
}
# Mot de passe root : sert à « su - » et à la console Proxmox, jamais en SSH.
# Il est généré une fois puis conservé dans l'état Terraform, à récupérer par
# le workflow de déploiement pour être déposé dans Vaultwarden.
resource "random_password" "root" {
length = var.root_password_length
special = true
min_upper = 2
min_lower = 2
min_numeric = 2
min_special = 2
override_special = "!@#%^&*()-_=+[]{}:,.?"
}
# Le fichier est déposé dans le contenu "snippets" du stockage, via SSH.
@@ -14,13 +31,15 @@ resource "proxmox_virtual_environment_file" "user_data" {
file_name = "${var.vm_hostname}-user-data.yaml"
data = templatefile("${path.module}/templates/user-data.yaml.tftpl", {
hostname = var.vm_hostname
fqdn = local.fqdn
timezone = var.timezone
locale = var.locale
ansible_user = var.ansible_user
ssh_keys = var.ssh_public_keys
admin_cidrs = var.ssh_allowed_cidrs
hostname = var.vm_hostname
fqdn = local.fqdn
timezone = var.timezone
locale = var.locale
comptes = var.local_accounts
cles_root = var.root_ssh_public_keys
allow_users = local.allow_users
admin_cidrs = var.ssh_allowed_cidrs
root_password_hash = random_password.root.bcrypt_hash
})
}
}
+33 -7
View File
@@ -1,5 +1,5 @@
# Valeurs non sensibles, versionnées : c'est ce fichier que la CI passe en -var-file.
# Les secrets (jeton d'API, mot de passe SSH du nœud) arrivent par TF_VAR_*, depuis OpenBao.
# Valeurs non sensibles, versionnées : c'est ce fichier que la CI passe en -var-file.
# Les secrets (jeton d'API, mot de passe SSH du nœud) arrivent par TF_VAR_*, depuis OpenBao.
vm_id = 120
vm_hostname = "centreon"
@@ -14,11 +14,37 @@ vm_cores = 2
vm_memory = 4096
vm_disk_size = 40
ansible_user = "ansible"
ssh_allowed_cidrs = ["10.0.4.0/24", "10.0.5.0/24"]
# Clé publique d'administration. Ce n'est pas un secret : elle reste ici plutôt que
# dans OpenBao, sinon un -var-file l'emporterait sur la variable d'environnement.
ssh_public_keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFDIXkfS5dM3po9Jz7NKgd3t1H4vqh0/cVoxrAP42ivd thedj@ZEU",
# Comptes de la machine. Aucun n'a de privilège : sudo est absent du système.
# Pour administrer, ouvrir une session avec son compte puis « su - ».
# Une clé publique n'est pas un secret, sa place est bien ici.
local_accounts = [
{
name = "hcornet"
gecos = "Hubert Cornet - administration"
ssh_keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFDIXkfS5dM3po9Jz7NKgd3t1H4vqh0/cVoxrAP42ivd thedj@ZEU",
]
},
{
name = "invite"
gecos = "Compte de consultation - aucun privilege"
ssh_keys = [
"REMPLACER par la cle publique du compte invite",
]
},
{
name = "ansible"
gecos = "Compte de service - sans privilege"
ssh_keys = [
"REMPLACER par la cle publique d automatisation",
]
},
]
# Clés autorisées à ouvrir une session directement en root : c'est par là
# qu'Ansible obtient ses droits, sudo n'existant pas. Rien d'humain ici.
root_ssh_public_keys = [
"REMPLACER par la cle publique d automatisation",
]
+1 -1
View File
@@ -112,7 +112,7 @@ resource "local_file" "ansible_inventory" {
hosts = {
(var.vm_hostname) = {
ansible_host = var.vm_ipv4_address
ansible_user = var.ansible_user
ansible_user = var.ansible_remote_user
}
}
}
+12 -1
View File
@@ -13,9 +13,20 @@ output "vm_fqdn" {
value = local.fqdn
}
output "comptes" {
description = "Comptes ouverts sur la machine, hors root."
value = [for compte in var.local_accounts : compte.name]
}
output "ssh_command" {
description = "Commande de connexion une fois cloud-init terminé."
value = "ssh ${var.ansible_user}@${var.vm_ipv4_address}"
value = "ssh ${var.local_accounts[0].name}@${var.vm_ipv4_address}"
}
output "root_password" {
description = "Mot de passe root, à déposer dans Vaultwarden puis à ne plus consulter."
value = random_password.root.result
sensitive = true
}
output "ansible_inventory_file" {
+1 -3
View File
@@ -11,9 +11,7 @@
#
set -euo pipefail
# Adresse d'OpenBao : valeur par défaut du homelab, surchargeable par
# une variable d'environnement si besoin.
OPENBAO_ADDR="${OPENBAO_ADDR:-https://openbao.tips-of-mine.com}"
: "${OPENBAO_ADDR:?variable OPENBAO_ADDR absente}"
: "${OPENBAO_ROLE_ID:?variable OPENBAO_ROLE_ID absente}"
: "${OPENBAO_SECRET_ID:?variable OPENBAO_SECRET_ID absente}"
: "${GITHUB_ENV:?ce script est prévu pour un job Gitea Actions}"
+46 -11
View File
@@ -9,25 +9,37 @@ manage_etc_hosts: true
timezone: ${timezone}
# La clé "users" remplace la liste par défaut : le compte "debian" livré avec
# l'image cloud n'est donc jamais créé. Un seul compte existe sur la machine,
# sans mot de passe (lock_passwd), accessible uniquement par clé.
# l'image cloud n'est donc jamais créé.
#
# Aucun compte n'appartient au groupe sudo, et la commande sudo est purgée du
# système plus bas. Pour administrer : ouvrir une session avec son compte, puis
# « su - » avec le mot de passe root.
users:
- name: ${ansible_user}
gecos: Compte d administration (Ansible)
groups: [sudo]
%{ for compte in comptes ~}
- name: ${compte.name}
gecos: "${compte.gecos}"
shell: /bin/bash
lock_passwd: true
sudo: "ALL=(ALL) NOPASSWD:ALL"
ssh_authorized_keys:
%{ for key in ssh_keys ~}
- "${key}"
%{ for cle in compte.ssh_keys ~}
- "${cle}"
%{ endfor ~}
%{ endfor ~}
disable_root: true
# Root garde un mot de passe, utilisable par « su - » et sur la console Proxmox.
# En SSH, seule la clé est acceptée (prohibit-password plus bas).
disable_root: false
ssh_pwauth: false
ssh_deletekeys: true
ssh_genkeytypes: [ed25519, rsa]
chpasswd:
expire: false
users:
- name: root
password: "${root_password_hash}"
type: hash
package_update: true
package_upgrade: true
@@ -48,18 +60,28 @@ packages:
- apt-transport-https
write_files:
- path: /root/.ssh/authorized_keys
owner: root:root
permissions: "0600"
content: |
%{ for cle in cles_root ~}
${cle}
%{ endfor ~}
- path: /etc/ssh/sshd_config.d/99-durcissement.conf
owner: root:root
permissions: "0600"
content: |
# Durcissement SSH — cloud-init
PermitRootLogin no
# root n'est joignable que par clé : le mot de passe root ne sert qu'en
# local, par « su - » ou sur la console.
PermitRootLogin prohibit-password
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitEmptyPasswords no
PubkeyAuthentication yes
AuthenticationMethods publickey
AllowUsers ${ansible_user}
AllowUsers ${allow_users}
MaxAuthTries 3
LoginGraceTime 30
X11Forwarding no
@@ -124,6 +146,17 @@ write_files:
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
- path: /etc/apt/preferences.d/99-pas-de-sudo
owner: root:root
permissions: "0644"
content: |
# sudo est retiré volontairement de cette machine : l'escalade de
# privilèges passe par « su - ». Cette épingle empêche une dépendance
# de le réinstaller sans qu'on s'en aperçoive.
Package: sudo
Pin: release *
Pin-Priority: -1
- path: /etc/sysctl.d/99-durcissement.conf
owner: root:root
permissions: "0644"
@@ -141,6 +174,7 @@ write_files:
fs.protected_symlinks = 1
runcmd:
- chmod 700 /root/.ssh
- sed -i "s|^# *${locale} UTF-8|${locale} UTF-8|" /etc/locale.gen
- locale-gen
- update-locale LANG=${locale}
@@ -149,6 +183,7 @@ runcmd:
- systemctl enable --now nftables
- systemctl enable --now unattended-upgrades
- systemctl restart ssh
- apt-get -y purge sudo
- apt-get -y autoremove --purge
- apt-get -y clean
+51 -10
View File
@@ -190,20 +190,61 @@ variable "locale" {
default = "fr_FR.UTF-8"
}
variable "ansible_user" {
description = "Compte unix créé par cloud-init, utilisé ensuite par Ansible."
type = string
default = "ansible"
}
variable "local_accounts" {
description = <<-EOT
Comptes unix créés par cloud-init. Aucun n'a de privilège : la commande sudo
est retirée du système. Pour administrer, on ouvre une session avec son
compte puis on bascule par « su - » avec le mot de passe root.
Le champ ssh_keys ne doit contenir que des clés publiques.
EOT
variable "ssh_public_keys" {
description = "Clés publiques autorisées pour le compte d'administration. Aucun mot de passe n'est défini."
type = list(string)
type = list(object({
name = string
gecos = optional(string, "")
ssh_keys = list(string)
}))
validation {
condition = length(var.ssh_public_keys) > 0
error_message = "Au moins une clé publique est nécessaire : l'authentification par mot de passe est désactivée."
condition = length(var.local_accounts) > 0
error_message = "Au moins un compte est nécessaire pour accéder à la machine."
}
validation {
condition = alltrue([for c in var.local_accounts : length(c.ssh_keys) > 0])
error_message = "Chaque compte doit avoir au moins une clé publique : l'authentification par mot de passe est désactivée en SSH."
}
validation {
condition = !contains([for c in var.local_accounts : c.name], "root")
error_message = "Le compte root se configure par root_ssh_public_keys, pas par local_accounts."
}
}
variable "root_ssh_public_keys" {
description = <<-EOT
Clés autorisées à ouvrir une session SSH directement en root. C'est la voie
d'accès d'Ansible, sudo n'existant pas sur la machine. À réserver à
l'automatisation : une clé humaine n'a rien à faire ici.
EOT
type = list(string)
validation {
condition = length(var.root_ssh_public_keys) > 0
error_message = "Sans clé root, aucune automatisation ne pourra configurer la machine."
}
}
variable "ansible_remote_user" {
description = "Compte utilisé par Ansible dans l'inventaire généré."
type = string
default = "root"
}
variable "root_password_length" {
description = "Longueur du mot de passe root généré. Il sert à « su - » et à la console Proxmox."
type = number
default = 24
}
variable "ssh_allowed_cidrs" {
+4
View File
@@ -6,6 +6,10 @@ terraform {
source = "bpg/proxmox"
version = ">= 0.69.0"
}
random = {
source = "hashicorp/random"
version = ">= 3.6.0"
}
local = {
source = "hashicorp/local"
version = ">= 2.4.0"