Runbook opérationnel — Procédure complète pour provisionner une nouvelle VM sur l'infrastructure LORVA (Proxmox
LORVA-PVE-01).
https://10.10.40.10:8006 (VLAN 40 management)https://10.255.255.1:8006reference_lorva_secretsLocal → vps-bastion (213.32.19.68) → lorva-bastion (10.10.30.20) → VM cibleAvant de toucher à Proxmox, répondre à ces questions :
| Question | Valeurs possibles |
|---|---|
| Quel est le rôle de la VM ? | Infra interne / Prod Taskori / Service public / Bastion |
| Quel VLAN ? | 10 (infra), 20 (prod), 30 (public), 40 (management) |
| Quelle IP fixe ? | Voir tableau réseau ci-dessous |
| Quelle RAM/disk ? | Bastion : 512MB/8GB minimum — Services : 7-11GB/100-150GB |
| La VM expose-t-elle des services HTTP/S ? | Si oui → rejoindre le réseau Traefik (étape 6) |
| Faut-il un runner GitLab dédié ? | Si oui → étape 9 |
| Rôle VM | VLAN | Subnet | Gateway | DNS | Exemple existant |
|---|---|---|---|---|---|
| Services infra (GitLab, NetBox…) | 10 | 10.10.10.0/24 | 10.10.10.1 | 10.10.10.1 | lorva-infra |
| Services prod Taskori | 20 | 10.10.20.0/24 | 10.10.20.1 | 10.10.20.1 | taskori-prod |
| Services publics (Traefik, mail…) | 30 | 10.10.30.0/24 | 10.10.30.1 | 10.10.30.1 | lorva-public, lorva-bastion |
| Management Proxmox | 40 | 10.10.40.0/24 | 10.10.40.1 | 10.10.40.1 | Proxmox host (10.10.40.10) |
| VMID | Nom | RAM | Disk | VLAN | IP | Startup order |
|---|---|---|---|---|---|---|
| 100 | PfSense | 2 GB | 32 GB | vmbr1 + vmbr2 | — | 1 |
| 101 | lorva-infra | 7 GB | 100 GB | 10 | 10.10.10.10 | 4 |
| 102 | taskori-prod | 11 GB | 150 GB | 20 | 10.10.20.10 | 5 |
| 103 | lorva-public | 7 GB | 150 GB | 30 | 10.10.30.10 | 3 |
| 104 | lorva-bastion | 0.5 GB | 8 GB | 30 | 10.10.30.20 | 2 |
Prochain VMID disponible : 105 (incrémenter à chaque nouvelle VM)
https://10.10.40.10:8006)LORVA-PVE-01lorva-monitoring, taskori-worker…)q35VirtIO SCSI PCIsocket (nécessaire pour la console série)localqcow2hostvmbr2VirtIOonboot: 1
startup: order=6 # (ou ordre logique : après prod=5)
agent: enabled=1
# Créer la VM (squelette vide — le clone de snapshot est préférable, voir étape 2)
curl -sk -X POST \
-H "Authorization: PVEAPIToken=netbox-sync@pve!sync=<TOKEN>" \
"https://10.255.255.1:8006/api2/json/nodes/LORVA-PVE-01/qemu" \
-d "vmid=105&name=nouveau-serveur&memory=7168&cores=2&cpu=host" \
-d "net0=virtio,bridge=vmbr2,tag=10" \
-d "scsi0=local:100,format=qcow2" \
-d "scsihw=virtio-scsi-pci&agent=enabled=1&serial0=socket&vga=serial0" \
-d "onboot=1&startup=order=6"
Le token API est en lecture seule par défaut (
netbox-sync@pve!sync). Pour les opérations d'écriture, utiliser un token admin — voir Flatnotesreference_lorva_secrets.
base-configuredNe pas installer un OS from scratch. Le snapshot base-configured de la VM 101 (lorva-infra) fournit Debian 13 + Docker + Docker Compose + QEMU guest agent préconfigurés. Les VMs 102 et 103 ont été créées ainsi.
base-configuredlocalcurl -sk -X POST \
-H "Authorization: PVEAPIToken=<ADMIN_TOKEN>" \
"https://10.255.255.1:8006/api2/json/nodes/LORVA-PVE-01/qemu/101/clone" \
-d "newid=105&name=nouveau-serveur&full=1&snapname=base-configured&storage=local"
Surveiller la progression :
# Lister les tâches en cours
curl -sk \
-H "Authorization: PVEAPIToken=<TOKEN>" \
"https://10.255.255.1:8006/api2/json/nodes/LORVA-PVE-01/tasks?vmid=105" \
| python3 -m json.tool
Après clonage, mettre à jour les paramètres Cloud-Init avant le premier démarrage.
Aller dans VM 105 → Cloud-Init :
| Champ | Valeur |
|---|---|
| User | root |
| Password | (laisser vide — auth par clé SSH uniquement) |
| DNS domain | lorva.dev |
| DNS servers | gateway du VLAN (ex. 10.10.10.1) |
| IP Config (net0) | ip=10.10.XX.YY/24,gw=10.10.XX.1 |
| SSH public keys | Coller les clés autorisées (voir ci-dessous) |
curl -sk -X PUT \
-H "Authorization: PVEAPIToken=<ADMIN_TOKEN>" \
"https://10.255.255.1:8006/api2/json/nodes/LORVA-PVE-01/qemu/105/config" \
-d "name=nouveau-serveur" \
-d "ipconfig0=ip=10.10.10.15/24,gw=10.10.10.1" \
-d "nameserver=10.10.10.1" \
-d "searchdomain=lorva.dev"
#cloud-config
hostname: nouveau-serveur
manage_etc_hosts: true
fqdn: nouveau-serveur.lorva.dev
user: root
disable_root: False
ssh_authorized_keys:
- ssh-rsa AAAAB3... # benjamin@workstation
- ssh-rsa AAAAB3... # root@LORVA-PVE-01
chpasswd:
expire: False
package_upgrade: true
Les clés SSH exactes sont dans Flatnotes
reference_lorva_secrets. Ne jamais mettre de mots de passe en clair ici.
Via WebUI : bouton Start sur la VM 105.
Via API :
curl -sk -X POST \
-H "Authorization: PVEAPIToken=<ADMIN_TOKEN>" \
"https://10.255.255.1:8006/api2/json/nodes/LORVA-PVE-01/qemu/105/status/start"
# Depuis la machine locale, via la chaîne de jump complète
ssh -J vps-bastion,lorva-bastion [email protected]
# Ou via alias SSH une fois l'entrée ajoutée (étape 5)
ssh nouveau-serveur
# Identité
hostname
cat /etc/os-release | grep PRETTY_NAME
# Réseau
ip a show eth0
ip route show default
ping -c 3 10.10.10.1 # gateway pfSense
ping -c 3 1.1.1.1 # accès internet (via pfSense)
# Docker (doit être présent — inclus dans base-configured)
docker --version
docker compose version
systemctl status docker
# QEMU Guest Agent (nécessaire pour Proxmox)
systemctl status qemu-guest-agent
# Espace disque
df -h /
Si qemu-guest-agent est absent :
apt-get install -y qemu-guest-agent
systemctl enable --now qemu-guest-agent
Ajouter l'entrée dans ~/.ssh/netbox-hosts-lorva.conf sur la machine locale :
Host nouveau-serveur
Hostname 10.10.XX.YY
ProxyJump lorva-bastion
ForwardAgent Yes
User root
Ce fichier est normalement généré automatiquement par netbox-sync. Si la prochaine sync automatique tarde, ajouter l'entrée manuellement et la retirer ensuite pour éviter le doublon.
Vérifier que ~/.ssh/config inclut bien le fichier :
Include ~/.ssh/netbox-hosts-lorva.conf
Include ~/.ssh/netbox-proxies-lorva.conf
Tester :
ssh nouveau-serveur
Le snapshot base-configured désactive déjà l'authentification par mot de passe. Vérifier et compléter :
# Sur la nouvelle VM
grep -E "^(PasswordAuthentication|PermitRootLogin|PubkeyAuthentication)" /etc/ssh/sshd_config
Résultat attendu :
PasswordAuthentication no
PermitRootLogin prohibit-password
PubkeyAuthentication yes
Si différent, corriger :
sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
systemctl restart sshd
# Vérifier si présent (inclus dans base-configured)
systemctl status fail2ban
# Sinon installer
apt-get install -y fail2ban
systemctl enable --now fail2ban
Ajouter dans /root/.ssh/authorized_keys les clés nécessaires (voir Flatnotes reference_lorva_secrets). Ne jamais coller de mots de passe en clair.
Le snapshot base-configured inclut Docker et Docker Compose. Vérifier les versions :
docker --version # Docker 27.x ou supérieur
docker compose version # Docker Compose v2.x
Si absent ou trop ancien, réinstaller :
# Supprimer ancienne version
apt-get remove -y docker docker-engine docker.io containerd runc
# Installer depuis le dépôt officiel Docker
apt-get update
apt-get install -y ca-certificates curl gnupg lsb-release
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg \
| gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/debian $(lsb_release -cs) stable" \
> /etc/apt/sources.list.d/docker.list
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
systemctl enable --now docker
Convention LORVA : tous les stacks Docker sont dans /docker/<nom-du-service>/ :
mkdir -p /docker
Traefik tourne sur lorva-public (10.10.30.10). Deux cas de figure :
Les services peuvent joindre directement le réseau Docker Traefik :
# Sur lorva-public — vérifier que le réseau existe
docker network ls | grep traefik-public
# Si absent
docker network create traefik-public
# /docker/mon-service/docker-compose.yml
services:
mon-service:
image: exemple/image:latest
restart: unless-stopped
networks:
- traefik-public
labels:
- "traefik.enable=true"
- "traefik.http.routers.mon-service.rule=Host(`mon-service.lorva.dev`)"
- "traefik.http.routers.mon-service.entrypoints=websecure"
- "traefik.http.routers.mon-service.tls.certresolver=letsencrypt"
- "traefik.http.services.mon-service.loadbalancer.server.port=8080"
networks:
traefik-public:
external: true
Traefik sur lorva-public doit pouvoir joindre le service via le réseau interne. Exposer le port sur l'IP du VLAN (pas 0.0.0.0) :
# Sur la VM interne (ex. lorva-infra 10.10.10.10)
services:
mon-service:
image: exemple/image:latest
restart: unless-stopped
ports:
- "10.10.10.10:8080:8080" # IP de la VM, jamais 0.0.0.0
Puis sur lorva-public, créer un fichier de configuration Traefik dynamique :
# /docker/traefik/config/mon-service.yml
http:
routers:
mon-service:
rule: "Host(`mon-service.lorva.dev`)"
entryPoints:
- websecure
tls:
certResolver: letsencrypt
service: mon-service
services:
mon-service:
loadBalancer:
servers:
- url: "http://10.10.10.10:8080"
Le cloudflare-companion sur lorva-public crée automatiquement les entrées DNS Cloudflare dès qu'un container avec label traefik.http.routers.*.rule=Host(...) démarre.
Domaines gérés automatiquement :
*.lorva.dev → srv.lorva.dev → 192.99.35.93 (proxied Cloudflare)*.taskori.app → idemAucune action DNS manuelle n'est nécessaire.
Important : Tous les certificats TLS sont obtenus via DNS-01 challenge uniquement (Cloudflare orange cloud, proxy actif). Jamais de HTTP-01 challenge.
Chaque VM qui a besoin d'exposer son socket Docker (pour Traefik, Portainer, etc.) utilise un socket-proxy. Ne jamais exposer /var/run/docker.sock directement sur le réseau.
mkdir -p /docker/socket-proxy
# /docker/socket-proxy/docker-compose.yml
services:
socket-proxy:
image: tecnativa/docker-socket-proxy:latest
restart: unless-stopped
environment:
CONTAINERS: 1
SERVICES: 1
NETWORKS: 1
TASKS: 1
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- "10.10.XX.YY:2375:2375" # IP de la VM — jamais 0.0.0.0
networks:
- socket-proxy-net
networks:
socket-proxy-net:
driver: bridge
cd /docker/socket-proxy && docker compose up -d
Si la VM héberge une application déployée via CI/CD LORVA :
Sur gitlab.lorva.dev (tourne sur lorva-infra) — créer le projet dans le groupe approprié.
# Sur la nouvelle VM
apt-get install -y gitlab-runner
# Enregistrer le runner (token dans les Settings CI/CD du projet GitLab)
gitlab-runner register \
--url "https://gitlab.lorva.dev" \
--registration-token "<TOKEN_CI_VOIR_FLATNOTES>" \
--executor "shell" \
--description "runner-nouveau-serveur" \
--tag-list "nouveau-serveur,deploy"
Vérifier :
gitlab-runner status
gitlab-runner list
Le token de registration GitLab est dans Flatnotes
ref-gitlab. Ne jamais le committer.
Ajouter dans GitLab → Project → Settings → CI/CD → Variables :
SSH_PRIVATE_KEY — clé de déploiementUne fois la VM configurée, fonctionnelle et testée :
# Arrêter proprement Docker avant le snapshot
docker stop $(docker ps -q) 2>/dev/null || true
Via WebUI Proxmox :
base-configured (si c'est un nouveau point de départ) ou nom descriptif (services-installed, gitlab-runner-ready…)Redémarrer les services :
cd /docker && for d in */; do (cd "$d" && docker compose up -d 2>/dev/null); done
Workstation locale
└─► vps-bastion (213.32.19.68 — OVH VPS)
└─► lorva-bastion (10.10.30.20 — VLAN 30)
└─► VM cible (10.10.X.Y — VLAN 10/20/30)
Alias opérationnels (via ~/.ssh/netbox-hosts-lorva.conf) :
ssh lorva-infra # 10.10.10.10
ssh taskori-prod # 10.10.20.10
ssh lorva-public # 10.10.30.10
ssh lorva-bastion # 10.10.30.20
ssh nouveau-serveur # 10.10.XX.YY (après étape 5)
[ ] VMID choisi (105+), nom en kebab-case lorva-*
[ ] VLAN correct selon le rôle (10/20/30)
[ ] IP fixe attribuée, pas de conflit dans le subnet
[ ] Clone depuis snapshot base-configured (VM 101)
[ ] Cloud-Init configuré (IP, gateway, DNS, SSH keys)
[ ] VM démarrée et joignable via SSH
[ ] hostname, ip a, docker --version vérifiés
[ ] qemu-guest-agent actif (visible dans Proxmox)
[ ] PasswordAuthentication no dans sshd_config
[ ] fail2ban actif
[ ] Alias SSH ajouté dans netbox-hosts-lorva.conf
[ ] Socket proxy créé si Docker exposé
[ ] Traefik labels configurés si service HTTP/S (DNS auto)
[ ] Runner GitLab enregistré si CI/CD nécessaire
[ ] Variables CI/CD ajoutées dans GitLab (jamais en clair)
[ ] Snapshot post-install créé
[ ] onboot=1 et startup order défini dans Proxmox
| Interdit | Raison |
|---|---|
| Challenge HTTP-01 | Orange cloud Cloudflare bloque HTTP-01 — uniquement DNS-01 |
Exposer Docker socket sur 0.0.0.0 |
Toujours lier au socket-proxy sur l'IP VLAN |
| SSH direct par IP depuis la machine locale | Toujours passer par les aliases et la chaîne de jump |
| Mots de passe en clair dans les fichiers | Utiliser Flatnotes ou variables CI/CD GitLab |
DROP DATABASE |
Interdit sur toute infra LORVA |