Proxmox lancelot : compte API dédié, VLAN 200 LAB en DHCP
Clonage par l'API (community.proxmox) avec bwpoc@pve limité au pool bwpoc ; modèle créé une fois en root avec l'agent QEMU, qui sert à lire les IP DHCP écrites dans inventory/host_vars/<vm>/dhcp.yml. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
099584c95c
commit
5846487f07
15 files changed
+502
-348
No files matched your search
@@ -3,3 +3,4 @@ collections/
|
|||||||
build/
|
build/
|
||||||
*.retry
|
*.retry
|
||||||
.vault_pass
|
.vault_pass
|
||||||
|
inventory/host_vars/*/dhcp.yml
|
||||||
@@ -3,8 +3,9 @@ INV ?= inventory/hosts.yml
|
|||||||
|
|
||||||
.PHONY: deps deploy vms base apps waf client tests destroy creds
|
.PHONY: deps deploy vms base apps waf client tests destroy creds
|
||||||
|
|
||||||
deps: ## Collections Ansible
|
deps: ## Collections Ansible + client Python de l'API Proxmox
|
||||||
ansible-galaxy collection install -r requirements.yml -p ./collections
|
ansible-galaxy collection install -r requirements.yml -p ./collections
|
||||||
|
python3 -m pip install --user proxmoxer requests
|
||||||
deploy: ## Déploiement complet
|
deploy: ## Déploiement complet
|
||||||
ansible-playbook -i $(INV) playbooks/site.yml
|
ansible-playbook -i $(INV) playbooks/site.yml
|
||||||
vms:
|
vms:
|
||||||
|
|||||||
@@ -33,25 +33,70 @@ Le domaine par défaut est `bwpoc.internal` (TLD réservé par l'ICANN pour l'us
|
|||||||
|
|
||||||
## Prérequis
|
## Prérequis
|
||||||
|
|
||||||
- Un nœud Proxmox VE 8 joignable en SSH **root** depuis le poste Ansible, avec accès Internet (image Debian, images Docker, listes noires communautaires).
|
- Un nœud Proxmox VE 8 ou 9 (ici `lancelot`, 172.16.16.7) avec accès Internet (image Debian, images Docker, listes noires communautaires).
|
||||||
- 3 IP libres sur le réseau des VM (1 en mode tout-en-un). Le playbook vérifie par ping qu'elles sont libres avant de créer les VM.
|
- Un accès SSH **root** au nœud, utilisé **une seule fois** pour créer le modèle de VM et le pool `bwpoc`. Tout le reste passe par l'API avec un compte dédié (voir plus bas).
|
||||||
- Sur le poste Ansible : `ansible-core` 2.16 ou plus, une paire de clés SSH.
|
- Un réseau de VM avec DHCP : ici le VLAN 200 "LAB", qui fournit IP, passerelle et DNS. Le poste Ansible doit pouvoir joindre ce VLAN en SSH.
|
||||||
|
- Sur le poste Ansible : `ansible-core` 2.17 ou plus (exigé par `community.proxmox`), `proxmoxer` et `requests` en Python, une paire de clés SSH.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone git@gitea.vlab.bzh:vLab-BZH/bunkerwebpoc.git && cd bunkerwebpoc
|
git clone git@gitea.vlab.bzh:vLab-BZH/bunkerwebpoc.git && cd bunkerwebpoc
|
||||||
make deps # installe community.docker et ansible.posix dans ./collections
|
make deps # collections community.docker et community.proxmox, puis proxmoxer
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Tout ce qui dépend de votre infra est marqué `A ADAPTER` :
|
Tout ce qui dépend de votre infra est marqué `A ADAPTER` :
|
||||||
|
|
||||||
1. [`inventory/hosts.yml`](inventory/hosts.yml) : IP du nœud Proxmox, IP et VMID des 3 VM, CPU, RAM, disque.
|
1. [`inventory/hosts.yml`](inventory/hosts.yml) : nœud Proxmox, VMID des 3 VM, CPU, RAM, disque. Pas d'IP : elles viennent du DHCP.
|
||||||
2. [`inventory/group_vars/all.yml`](inventory/group_vars/all.yml) : domaine, passerelle, DNS, stockage Proxmox, pont, VLAN, clé SSH publique.
|
2. [`inventory/group_vars/all.yml`](inventory/group_vars/all.yml) : domaine, nœud, stockage, pont et VLAN, compte API, clé SSH publique.
|
||||||
3. [`inventory/group_vars/bunkerweb.yml`](inventory/group_vars/bunkerweb.yml) : les réglages BunkerWeb, globaux et par site. C'est le fichier à modifier pour jouer avec les fonctionnalités.
|
3. [`inventory/group_vars/bunkerweb.yml`](inventory/group_vars/bunkerweb.yml) : les réglages BunkerWeb, globaux et par site. C'est le fichier à modifier pour jouer avec les fonctionnalités.
|
||||||
|
|
||||||
Les mots de passe (MariaDB, Redis, admin de l'UI) sont générés au premier lancement dans `credentials/` côté poste Ansible (ignoré par git). Pour les mettre sous `ansible-vault`, remplacez les `lookup('password')` de `bunkerweb.yml` par vos variables chiffrées.
|
Les mots de passe (MariaDB, Redis, admin de l'UI) sont générés au premier lancement dans `credentials/` côté poste Ansible (ignoré par git). Pour les mettre sous `ansible-vault`, remplacez les `lookup('password')` de `bunkerweb.yml` par vos variables chiffrées.
|
||||||
|
|
||||||
|
## Compte Proxmox dédié
|
||||||
|
|
||||||
|
Le clonage et le pilotage des VM passent par l'API avec l'utilisateur `bwpoc@pve` et un jeton, limité à un pool `bwpoc`. Seule la création du modèle (import d'une image disque par chemin, réservé à `root@pam`) se fait en SSH root, une fois.
|
||||||
|
|
||||||
|
Privilèges nécessaires, regroupés dans un rôle `BWPoC` :
|
||||||
|
|
||||||
|
| Privilège | Pour quoi faire |
|
||||||
|
|---|---|
|
||||||
|
| `VM.Allocate`, `VM.Clone` | créer les clones dans le pool, les supprimer |
|
||||||
|
| `VM.Config.CPU`, `VM.Config.Memory`, `VM.Config.Disk`, `VM.Config.Network`, `VM.Config.Cloudinit`, `VM.Config.Options`, `VM.Config.HWType`, `VM.Config.CDROM` | régler CPU, RAM, disque, cloud-init, démarrage auto, tags |
|
||||||
|
| `VM.PowerMgmt` | démarrer et arrêter les VM |
|
||||||
|
| `VM.Audit`, `Pool.Audit` | lire l'état des VM et du pool |
|
||||||
|
| `VM.GuestAgent.Audit` (PVE 9) ou `VM.Monitor` (PVE 8) | lire l'IP DHCP par l'agent QEMU |
|
||||||
|
|
||||||
|
Et les ACL, au plus juste :
|
||||||
|
|
||||||
|
| Chemin | Rôle | Pourquoi |
|
||||||
|
|---|---|---|
|
||||||
|
| `/pool/bwpoc` | `BWPoC` | les VM du PoC et le modèle 9100, placé dans le pool |
|
||||||
|
| `/storage/local-lvm` | `PVEDatastoreUser` | allouer les disques des clones |
|
||||||
|
| `/sdn/zones/localnetwork/vmbr0/200` | `PVESDNUser` | brancher les VM sur le VLAN 200 (et seulement lui) |
|
||||||
|
| `/nodes/lancelot` | `PVEAuditor` | lecture du nœud par le module Ansible |
|
||||||
|
|
||||||
|
En root sur `lancelot` (sur PVE 8, remplacer `VM.GuestAgent.Audit` par `VM.Monitor`) :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pveum pool add bwpoc --comment "PoC BunkerWeb"
|
||||||
|
pveum role add BWPoC --privs "VM.Allocate,VM.Clone,VM.Audit,VM.PowerMgmt,VM.Config.CPU,VM.Config.Memory,VM.Config.Disk,VM.Config.Network,VM.Config.Cloudinit,VM.Config.Options,VM.Config.HWType,VM.Config.CDROM,VM.GuestAgent.Audit,Pool.Audit"
|
||||||
|
pveum user add bwpoc@pve --comment "PoC BunkerWeb (Ansible)"
|
||||||
|
pveum user token add bwpoc@pve ansible --privsep 0 # affiche le secret une seule fois
|
||||||
|
pveum acl modify /pool/bwpoc --users bwpoc@pve --roles BWPoC
|
||||||
|
pveum acl modify /storage/local-lvm --users bwpoc@pve --roles PVEDatastoreUser
|
||||||
|
pveum acl modify /sdn/zones/localnetwork/vmbr0/200 --users bwpoc@pve --roles PVESDNUser
|
||||||
|
pveum acl modify /nodes/lancelot --users bwpoc@pve --roles PVEAuditor
|
||||||
|
```
|
||||||
|
|
||||||
|
Si le réseau "LAB" est un VNet SDN plutôt qu'un tag sur `vmbr0`, l'ACL réseau devient `/sdn/zones/<zone>/LAB` et, dans `all.yml`, `pve_bridge: LAB` avec `pve_vlan_tag: ""`.
|
||||||
|
|
||||||
|
Le secret du jeton reste hors du dépôt, dans l'environnement :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export PVE_TOKEN_SECRET='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
|
||||||
|
```
|
||||||
|
|
||||||
## Déploiement
|
## Déploiement
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -60,7 +105,7 @@ make deploy # = ansible-playbook playbooks/site.yml
|
|||||||
|
|
||||||
| Étape | Playbook | Ce qu'elle fait |
|
| Étape | Playbook | Ce qu'elle fait |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| 1 | `00-proxmox.yml` | télécharge l'image Debian 13 genericcloud, crée le modèle cloud-init (VMID 9100), clone les VM, injecte IP et clé SSH, agrandit les disques, démarre |
|
| 1 | `00-proxmox.yml` | en root (une fois) : télécharge l'image Debian 13 genericcloud, y ajoute l'agent QEMU, crée le modèle cloud-init (VMID 9100) et le pool `bwpoc`. Puis par l'API avec `bwpoc@pve` : clone les VM dans le pool, cloud-init en DHCP + clé SSH, agrandit les disques, démarre, lit l'IP obtenue par l'agent QEMU et l'écrit dans `inventory/host_vars/<vm>/dhcp.yml` |
|
||||||
| 2 | `10-base.yml` | paquets de base, agent QEMU, résolution locale des noms du PoC vers le WAF, Docker CE depuis le dépôt officiel |
|
| 2 | `10-base.yml` | paquets de base, agent QEMU, résolution locale des noms du PoC vers le WAF, Docker CE depuis le dépôt officiel |
|
||||||
| 3 | `20-apps.yml` | stack des applis, filtrage `DOCKER-USER` (mode split) |
|
| 3 | `20-apps.yml` | stack des applis, filtrage `DOCKER-USER` (mode split) |
|
||||||
| 4 | `30-bunkerweb.yml` | stack BunkerWeb, attente qu'`api` réponde au travers du WAF, écriture de `credentials/ACCES.txt` |
|
| 4 | `30-bunkerweb.yml` | stack BunkerWeb, attente qu'`api` réponde au travers du WAF, écriture de `credentials/ACCES.txt` |
|
||||||
@@ -76,12 +121,18 @@ ansible-playbook playbooks/render.yml # écrit build/split/... ou build/single
|
|||||||
|
|
||||||
Un exemple de rendu (secrets remplacés par `CHANGEME`) est versionné dans [`examples/split/`](examples/split/) (`bunkerweb.compose.yml`, `apps.compose.yml`, `bwpoc-firewall.sh`).
|
Un exemple de rendu (secrets remplacés par `CHANGEME`) est versionné dans [`examples/split/`](examples/split/) (`bunkerweb.compose.yml`, `apps.compose.yml`, `bwpoc-firewall.sh`).
|
||||||
|
|
||||||
|
### Adresses IP et DHCP
|
||||||
|
|
||||||
|
Les VM démarrent sur le VLAN 200 "LAB" en DHCP. Le modèle embarque l'agent QEMU : le playbook lit l'IP de chaque VM par l'API Proxmox, l'enregistre dans `inventory/host_vars/<vm>/dhcp.yml` (ignoré par git) et recharge l'inventaire. Les playbooks suivants, et vos relances (`make waf`...), utilisent ces fichiers.
|
||||||
|
|
||||||
|
Comme l'IP du WAF sert au filtrage `DOCKER-USER` et aux `/etc/hosts`, **réservez les baux DHCP** des trois VM (adresses MAC visibles dans Proxmox). Si une IP change quand même, relancez `make vms base apps waf client` : les fichiers `dhcp.yml` et les règles sont régénérés.
|
||||||
|
|
||||||
### Accéder aux sites depuis votre poste
|
### Accéder aux sites depuis votre poste
|
||||||
|
|
||||||
Les VM du PoC résolvent déjà les noms (bloc dans `/etc/hosts`). Sur votre poste, ajoutez la ligne suivante à `/etc/hosts`, ou déclarez les enregistrements dans votre DNS de labo :
|
Les VM du PoC résolvent déjà les noms (bloc dans `/etc/hosts`). Sur votre poste, ajoutez la ligne suivante à `/etc/hosts`, ou déclarez les enregistrements dans votre DNS de labo :
|
||||||
|
|
||||||
```
|
```
|
||||||
192.168.200.61 www.bwpoc.internal shop.bwpoc.internal api.bwpoc.internal bwui.bwpoc.internal
|
<IP de bw-waf> www.bwpoc.internal shop.bwpoc.internal api.bwpoc.internal bwui.bwpoc.internal
|
||||||
```
|
```
|
||||||
|
|
||||||
Puis ouvrez `https://bwui.bwpoc.internal/` (certificat auto-signé à accepter). Identifiants : `make creds`.
|
Puis ouvrez `https://bwui.bwpoc.internal/` (certificat auto-signé à accepter). Identifiants : `make creds`.
|
||||||
@@ -115,7 +166,7 @@ Depuis `bw-client` (ou n'importe quel poste avec `curl` et `jq`, le script utili
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
make tests # depuis le poste Ansible
|
make tests # depuis le poste Ansible
|
||||||
ssh debian@192.168.200.63 bwpoc-tests # ou directement sur le client
|
ssh debian@<IP de bw-client> bwpoc-tests # ou directement sur le client
|
||||||
```
|
```
|
||||||
|
|
||||||
Le script enchaîne une vingtaine de contrôles et affiche `OK`, `KO` ou `INFO` :
|
Le script enchaîne une vingtaine de contrôles et affiche `OK`, `KO` ou `INFO` :
|
||||||
@@ -143,7 +194,7 @@ Le script enchaîne une vingtaine de contrôles et affiche `OK`, `KO` ou `INFO`
|
|||||||
Les derniers tests **bannissent volontairement votre IP** du site `api` pendant 5 minutes (`bw_ban_time`). Pour lever le ban : menu *Bans* de l'UI, ou sur `bw-waf` :
|
Les derniers tests **bannissent volontairement votre IP** du site `api` pendant 5 minutes (`bw_ban_time`). Pour lever le ban : menu *Bans* de l'UI, ou sur `bw-waf` :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker exec bw-scheduler bwcli unban 192.168.200.63
|
docker exec bw-scheduler bwcli unban <IP du client>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Avec et sans WAF
|
### Avec et sans WAF
|
||||||
@@ -152,12 +203,12 @@ Pour mesurer l'apport du WAF, autorisez le client à joindre les applis en direc
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# inventory/group_vars/all.yml
|
# inventory/group_vars/all.yml
|
||||||
apps_direct_access_sources: [192.168.200.63]
|
apps_direct_access_sources: ["<IP de bw-client>"]
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make apps
|
make apps
|
||||||
ssh debian@192.168.200.63 bwpoc-tests --direct
|
ssh debian@<IP de bw-client> bwpoc-tests --direct
|
||||||
```
|
```
|
||||||
|
|
||||||
La SQLi de Juice Shop renvoie alors la liste complète des produits (200) au lieu d'un 403, et le `PUT` passe.
|
La SQLi de Juice Shop renvoie alors la liste complète des produits (200) au lieu d'un 403, et le `PUT` passe.
|
||||||
@@ -175,7 +226,7 @@ La SQLi de Juice Shop renvoie alors la liste complète des produits (200) au lie
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
make waf # réapplique la conf BunkerWeb après modification de bunkerweb.yml
|
make waf # réapplique la conf BunkerWeb après modification de bunkerweb.yml
|
||||||
make destroy # supprime les VM (confirmation demandée), modèle conservé
|
make destroy # supprime les VM en root sur le nœud (confirmation demandée), modèle conservé
|
||||||
ansible-playbook playbooks/99-destroy.yml -e destroy_template=true # supprime aussi le modèle
|
ansible-playbook playbooks/99-destroy.yml -e destroy_template=true # supprime aussi le modèle
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -194,8 +245,8 @@ Sur `bw-waf`, la stack vit dans `/opt/bwpoc/bunkerweb` (`docker compose ps`, `lo
|
|||||||
inventory/ hosts.yml (3 VM), single.yml (tout-en-un), group_vars/
|
inventory/ hosts.yml (3 VM), single.yml (tout-en-un), group_vars/
|
||||||
playbooks/ 00 à 50 + site.yml, render.yml, 99-destroy.yml
|
playbooks/ 00 à 50 + site.yml, render.yml, 99-destroy.yml
|
||||||
roles/
|
roles/
|
||||||
proxmox_template/ modèle Debian 13 cloud-init via qm
|
proxmox_template/ modèle Debian 13 cloud-init + agent QEMU, pool (root, une fois)
|
||||||
proxmox_vm/ clonage, cloud-init, démarrage
|
proxmox_vm/ clonage par l'API (bwpoc@pve), DHCP, lecture des IP
|
||||||
common/ paquets, agent QEMU, /etc/hosts
|
common/ paquets, agent QEMU, /etc/hosts
|
||||||
docker/ Docker CE + compose (dépôt officiel)
|
docker/ Docker CE + compose (dépôt officiel)
|
||||||
apps/ compose des applis, vitrine, filtrage DOCKER-USER
|
apps/ compose des applis, vitrine, filtrage DOCKER-USER
|
||||||
|
|||||||
+266
-266
File diff suppressed because it is too large.
Load diff
Binary file not shown.
|
Before Width: | Height: | Size: 718 KiB After Width: | Height: | Size: 740 KiB |
@@ -9,16 +9,20 @@
|
|||||||
# ".internal" est réservé par l'ICANN pour l'usage privé : pas de collision possible.
|
# ".internal" est réservé par l'ICANN pour l'usage privé : pas de collision possible.
|
||||||
poc_domain: bwpoc.internal # A ADAPTER si vous avez une zone DNS de labo
|
poc_domain: bwpoc.internal # A ADAPTER si vous avez une zone DNS de labo
|
||||||
|
|
||||||
# --- Réseau des VM (cloud-init) --------------------------------------
|
|
||||||
poc_net_prefix: 24 # A ADAPTER
|
|
||||||
poc_net_gateway: 192.168.200.1 # A ADAPTER
|
|
||||||
poc_net_dns: [192.168.200.1] # A ADAPTER
|
|
||||||
poc_net_searchdomain: "{{ poc_domain }}"
|
|
||||||
|
|
||||||
# --- Proxmox ----------------------------------------------------------
|
# --- Proxmox ----------------------------------------------------------
|
||||||
|
pve_node: lancelot
|
||||||
pve_storage: local-lvm # A ADAPTER : stockage des disques VM
|
pve_storage: local-lvm # A ADAPTER : stockage des disques VM
|
||||||
|
# Réseau "LAB", VLAN 200, adressage par DHCP (passerelle et DNS fournis).
|
||||||
|
# Si LAB est un VNet SDN : pve_bridge: LAB et pve_vlan_tag: ""
|
||||||
pve_bridge: vmbr0 # A ADAPTER
|
pve_bridge: vmbr0 # A ADAPTER
|
||||||
pve_vlan_tag: "" # vide = pas de VLAN, sinon ex. "200"
|
pve_vlan_tag: "200"
|
||||||
|
pve_pool: bwpoc # pool délégué au compte dédié
|
||||||
|
|
||||||
|
# Compte dédié pour le clonage par l'API (voir README, "Compte Proxmox dédié")
|
||||||
|
pve_api_user: bwpoc@pve
|
||||||
|
pve_api_token_id: ansible
|
||||||
|
pve_api_token_secret: "{{ lookup('ansible.builtin.env', 'PVE_TOKEN_SECRET') }}"
|
||||||
|
pve_validate_certs: false # certificat auto-signé du nœud
|
||||||
pve_template_vmid: 9100
|
pve_template_vmid: 9100
|
||||||
pve_template_name: debian13-cloud
|
pve_template_name: debian13-cloud
|
||||||
pve_image_url: https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-amd64.qcow2
|
pve_image_url: https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-amd64.qcow2
|
||||||
|
|||||||
+4
-5
@@ -1,13 +1,15 @@
|
|||||||
---
|
---
|
||||||
# Inventaire du PoC BunkerWeb.
|
# Inventaire du PoC BunkerWeb.
|
||||||
|
# Les VM prennent leur IP par DHCP (VLAN 200 "LAB") : 00-proxmox.yml la lit
|
||||||
|
# via l'agent QEMU et l'écrit dans inventory/host_vars/<vm>/dhcp.yml.
|
||||||
# Mode "split" (défaut) : 3 VM distinctes (WAF, applis, client).
|
# Mode "split" (défaut) : 3 VM distinctes (WAF, applis, client).
|
||||||
# Mode "single" : voir inventory/single.yml (tout sur une seule VM).
|
# Mode "single" : voir inventory/single.yml (tout sur une seule VM).
|
||||||
all:
|
all:
|
||||||
children:
|
children:
|
||||||
proxmox:
|
proxmox:
|
||||||
hosts:
|
hosts:
|
||||||
pve01:
|
lancelot:
|
||||||
ansible_host: 192.168.200.10 # A ADAPTER : nœud Proxmox qui héberge les VM
|
ansible_host: 172.16.16.7
|
||||||
ansible_user: root
|
ansible_user: root
|
||||||
poc_vms:
|
poc_vms:
|
||||||
vars:
|
vars:
|
||||||
@@ -17,7 +19,6 @@ all:
|
|||||||
bunkerweb:
|
bunkerweb:
|
||||||
hosts:
|
hosts:
|
||||||
bw-waf:
|
bw-waf:
|
||||||
ansible_host: 192.168.200.61 # A ADAPTER
|
|
||||||
vm_id: 9101
|
vm_id: 9101
|
||||||
vm_cores: 2
|
vm_cores: 2
|
||||||
vm_memory: 4096
|
vm_memory: 4096
|
||||||
@@ -25,7 +26,6 @@ all:
|
|||||||
apps:
|
apps:
|
||||||
hosts:
|
hosts:
|
||||||
bw-apps:
|
bw-apps:
|
||||||
ansible_host: 192.168.200.62 # A ADAPTER
|
|
||||||
vm_id: 9102
|
vm_id: 9102
|
||||||
vm_cores: 2
|
vm_cores: 2
|
||||||
vm_memory: 4096
|
vm_memory: 4096
|
||||||
@@ -33,7 +33,6 @@ all:
|
|||||||
client:
|
client:
|
||||||
hosts:
|
hosts:
|
||||||
bw-client:
|
bw-client:
|
||||||
ansible_host: 192.168.200.63 # A ADAPTER
|
|
||||||
vm_id: 9103
|
vm_id: 9103
|
||||||
vm_cores: 1
|
vm_cores: 1
|
||||||
vm_memory: 1024
|
vm_memory: 1024
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ all:
|
|||||||
children:
|
children:
|
||||||
proxmox:
|
proxmox:
|
||||||
hosts:
|
hosts:
|
||||||
pve01:
|
lancelot:
|
||||||
ansible_host: 192.168.200.10 # A ADAPTER
|
ansible_host: 172.16.16.7
|
||||||
ansible_user: root
|
ansible_user: root
|
||||||
poc_vms:
|
poc_vms:
|
||||||
vars:
|
vars:
|
||||||
@@ -16,7 +16,6 @@ all:
|
|||||||
bunkerweb:
|
bunkerweb:
|
||||||
hosts:
|
hosts:
|
||||||
bw-poc:
|
bw-poc:
|
||||||
ansible_host: 192.168.200.61 # A ADAPTER
|
|
||||||
vm_id: 9101
|
vm_id: 9101
|
||||||
vm_cores: 4
|
vm_cores: 4
|
||||||
vm_memory: 6144
|
vm_memory: 6144
|
||||||
|
|||||||
@@ -1,9 +1,27 @@
|
|||||||
---
|
---
|
||||||
# Crée le modèle Debian 13 cloud-init puis clone les VM du PoC.
|
# 1) Modèle Debian 13 cloud-init : une seule fois, en SSH root sur le nœud
|
||||||
# A sauter si vous fournissez vos propres VM (Debian 12/13 avec accès SSH).
|
# (l'import d'une image disque par chemin absolu est réservé à root@pam).
|
||||||
- name: Provisionner les VM sur Proxmox
|
- name: Modèle cloud-init et pool sur le nœud Proxmox
|
||||||
hosts: proxmox
|
hosts: proxmox
|
||||||
gather_facts: false
|
gather_facts: false
|
||||||
roles:
|
roles:
|
||||||
- proxmox_template
|
- proxmox_template
|
||||||
|
|
||||||
|
# 2) Clonage des VM par l'API, avec le compte dédié bwpoc@pve (jeton API).
|
||||||
|
# A sauter si vous fournissez vos propres VM (Debian 12/13 avec accès SSH).
|
||||||
|
- name: Clonage des VM par l'API Proxmox
|
||||||
|
hosts: proxmox
|
||||||
|
connection: local
|
||||||
|
gather_facts: false
|
||||||
|
vars:
|
||||||
|
ansible_become: false
|
||||||
|
ansible_python_interpreter: "{{ ansible_playbook_python }}"
|
||||||
|
module_defaults:
|
||||||
|
group/community.proxmox.proxmox:
|
||||||
|
api_host: "{{ ansible_host }}"
|
||||||
|
api_user: "{{ pve_api_user }}"
|
||||||
|
api_token_id: "{{ pve_api_token_id }}"
|
||||||
|
api_token_secret: "{{ pve_api_token_secret }}"
|
||||||
|
validate_certs: "{{ pve_validate_certs }}"
|
||||||
|
roles:
|
||||||
- proxmox_vm
|
- proxmox_vm
|
||||||
@@ -31,3 +31,11 @@
|
|||||||
register: tpl_destroy
|
register: tpl_destroy
|
||||||
changed_when: "'destroyed' in tpl_destroy.stdout"
|
changed_when: "'destroyed' in tpl_destroy.stdout"
|
||||||
when: destroy_template | default(false) | bool
|
when: destroy_template | default(false) | bool
|
||||||
|
|
||||||
|
- name: Oublier les IP DHCP mémorisées
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ inventory_dir }}/host_vars/{{ item }}/dhcp.yml"
|
||||||
|
state: absent
|
||||||
|
loop: "{{ groups['poc_vms'] | unique }}"
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
+2
-1
@@ -2,4 +2,5 @@
|
|||||||
collections:
|
collections:
|
||||||
- name: community.docker
|
- name: community.docker
|
||||||
version: ">=3.10.0"
|
version: ">=3.10.0"
|
||||||
- name: ansible.posix
|
- name: community.proxmox
|
||||||
|
version: ">=1.3.0"
|
||||||
@@ -6,6 +6,13 @@
|
|||||||
changed_when: false
|
changed_when: false
|
||||||
failed_when: false
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Créer le pool {{ pve_pool }} (délégation au compte dédié)
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
pveum pool list --output-format json | grep -q '"poolid":"{{ pve_pool }}"' && exit 0
|
||||||
|
pveum pool add {{ pve_pool }} --comment "PoC BunkerWeb" && echo created
|
||||||
|
register: pool_add
|
||||||
|
changed_when: "'created' in pool_add.stdout"
|
||||||
|
|
||||||
- name: Créer le modèle
|
- name: Créer le modèle
|
||||||
when: tpl_status.rc != 0
|
when: tpl_status.rc != 0
|
||||||
block:
|
block:
|
||||||
@@ -14,6 +21,20 @@
|
|||||||
url: "{{ pve_image_url }}"
|
url: "{{ pve_image_url }}"
|
||||||
dest: "{{ pve_image_path }}"
|
dest: "{{ pve_image_path }}"
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
|
force: true # image vierge à chaque création du modèle
|
||||||
|
|
||||||
|
- name: Installer libguestfs-tools (personnalisation de l'image)
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: libguestfs-tools
|
||||||
|
state: present
|
||||||
|
update_cache: true
|
||||||
|
|
||||||
|
- name: Ajouter l'agent QEMU à l'image (indispensable pour lire l'IP DHCP)
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
virt-customize -a {{ pve_image_path }}
|
||||||
|
--install qemu-guest-agent
|
||||||
|
--truncate /etc/machine-id
|
||||||
|
changed_when: true
|
||||||
|
|
||||||
- name: Créer la VM modèle
|
- name: Créer la VM modèle
|
||||||
ansible.builtin.command: >-
|
ansible.builtin.command: >-
|
||||||
@@ -43,3 +64,10 @@
|
|||||||
- name: Convertir en modèle
|
- name: Convertir en modèle
|
||||||
ansible.builtin.command: qm template {{ pve_template_vmid }}
|
ansible.builtin.command: qm template {{ pve_template_vmid }}
|
||||||
changed_when: true
|
changed_when: true
|
||||||
|
|
||||||
|
- name: Placer le modèle dans le pool (le compte dédié peut le cloner)
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
pvesh get /pools/{{ pve_pool }} --output-format json | grep -q '"vmid":{{ pve_template_vmid }}[,}]' && exit 0
|
||||||
|
pveum pool modify {{ pve_pool }} --vms {{ pve_template_vmid }} && echo added
|
||||||
|
register: pool_tpl
|
||||||
|
changed_when: "'added' in pool_tpl.stdout"
|
||||||
@@ -1,13 +1,24 @@
|
|||||||
---
|
---
|
||||||
# Clone et configure les VM du PoC à partir du modèle (exécuté sur le nœud Proxmox).
|
# Clone et configure les VM du PoC par l'API Proxmox (compte dédié, voir README).
|
||||||
- name: Déposer la clé SSH publique pour cloud-init
|
# Exécuté sur le contrôleur Ansible : connection local dans le playbook.
|
||||||
ansible.builtin.copy:
|
- name: Contrôler la présence du jeton API
|
||||||
content: "{{ lookup('ansible.builtin.file', poc_ssh_pubkey_file) }}\n"
|
ansible.builtin.assert:
|
||||||
dest: /root/bwpoc_authorized_keys
|
that: pve_api_token_secret | length > 0
|
||||||
mode: "0600"
|
fail_msg: "Exportez PVE_TOKEN_SECRET (secret du jeton {{ pve_api_user }}!{{ pve_api_token_id }})"
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Créer le répertoire des host_vars par VM
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ inventory_dir }}/host_vars/{{ item }}"
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
loop: "{{ groups['poc_vms'] | unique }}"
|
||||||
|
|
||||||
- name: Créer chaque VM
|
- name: Créer chaque VM
|
||||||
ansible.builtin.include_tasks: vm.yml
|
ansible.builtin.include_tasks: vm.yml
|
||||||
loop: "{{ groups['poc_vms'] | unique }}"
|
loop: "{{ groups['poc_vms'] | unique }}"
|
||||||
loop_control:
|
loop_control:
|
||||||
loop_var: vm_name
|
loop_var: vm_name
|
||||||
|
|
||||||
|
- name: Recharger l'inventaire avec les IP DHCP
|
||||||
|
ansible.builtin.meta: refresh_inventory
|
||||||
@@ -4,53 +4,86 @@
|
|||||||
vm: "{{ hostvars[vm_name] }}"
|
vm: "{{ hostvars[vm_name] }}"
|
||||||
|
|
||||||
- name: "{{ vm_name }} : la VM existe-t-elle ?"
|
- name: "{{ vm_name }} : la VM existe-t-elle ?"
|
||||||
ansible.builtin.command: qm status {{ vm.vm_id }}
|
community.proxmox.proxmox_vm_info:
|
||||||
register: vm_status
|
vmid: "{{ vm.vm_id }}"
|
||||||
changed_when: false
|
node: "{{ pve_node }}"
|
||||||
failed_when: false
|
config: none
|
||||||
|
register: vm_info
|
||||||
|
|
||||||
- name: "{{ vm_name }} : création"
|
- name: "{{ vm_name }} : création"
|
||||||
when: vm_status.rc != 0
|
when: vm_info.proxmox_vms | length == 0
|
||||||
block:
|
block:
|
||||||
- name: "{{ vm_name }} : l'IP {{ vm.ansible_host }} doit être libre"
|
- name: "{{ vm_name }} : clonage du modèle {{ pve_template_vmid }}"
|
||||||
ansible.builtin.command: ping -c 2 -W 1 {{ vm.ansible_host }}
|
community.proxmox.proxmox_kvm:
|
||||||
register: ip_ping
|
node: "{{ pve_node }}"
|
||||||
changed_when: false
|
vmid: "{{ pve_template_vmid }}"
|
||||||
failed_when: ip_ping.rc == 0
|
clone: "{{ pve_template_name }}"
|
||||||
|
newid: "{{ vm.vm_id }}"
|
||||||
- name: "{{ vm_name }} : clonage du modèle"
|
name: "{{ vm_name }}"
|
||||||
ansible.builtin.command: >-
|
full: true
|
||||||
qm clone {{ pve_template_vmid }} {{ vm.vm_id }}
|
storage: "{{ pve_storage }}"
|
||||||
--name {{ vm_name }} --full 1 --storage {{ pve_storage }}
|
pool: "{{ pve_pool }}"
|
||||||
changed_when: true
|
timeout: 600
|
||||||
|
|
||||||
- name: "{{ vm_name }} : ressources et cloud-init"
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
qm set {{ vm.vm_id }}
|
|
||||||
--cores {{ vm.vm_cores }} --memory {{ vm.vm_memory }}
|
|
||||||
--onboot 1 --tags bwpoc
|
|
||||||
--ciuser {{ poc_vm_user }}
|
|
||||||
--sshkeys /root/bwpoc_authorized_keys
|
|
||||||
--ipconfig0 ip={{ vm.ansible_host }}/{{ poc_net_prefix }},gw={{ poc_net_gateway }}
|
|
||||||
--nameserver "{{ poc_net_dns | join(' ') }}"
|
|
||||||
--searchdomain {{ poc_net_searchdomain }}
|
|
||||||
--ciupgrade 0
|
|
||||||
changed_when: true
|
|
||||||
|
|
||||||
- name: "{{ vm_name }} : agrandissement du disque à {{ vm.vm_disk_gb }} Go"
|
- name: "{{ vm_name }} : agrandissement du disque à {{ vm.vm_disk_gb }} Go"
|
||||||
ansible.builtin.command: qm resize {{ vm.vm_id }} scsi0 {{ vm.vm_disk_gb }}G
|
community.proxmox.proxmox_disk:
|
||||||
changed_when: true
|
vmid: "{{ vm.vm_id }}"
|
||||||
|
disk: scsi0
|
||||||
|
state: resized
|
||||||
|
size: "{{ vm.vm_disk_gb }}G"
|
||||||
|
|
||||||
|
- name: "{{ vm_name }} : ressources et cloud-init"
|
||||||
|
community.proxmox.proxmox_kvm:
|
||||||
|
node: "{{ pve_node }}"
|
||||||
|
vmid: "{{ vm.vm_id }}"
|
||||||
|
update: true
|
||||||
|
cores: "{{ vm.vm_cores }}"
|
||||||
|
memory: "{{ vm.vm_memory }}"
|
||||||
|
onboot: true
|
||||||
|
tags: [bwpoc]
|
||||||
|
ciuser: "{{ poc_vm_user }}"
|
||||||
|
sshkeys: "{{ lookup('ansible.builtin.file', poc_ssh_pubkey_file) }}"
|
||||||
|
ipconfig:
|
||||||
|
ipconfig0: "ip=dhcp" # passerelle et DNS fournis par le DHCP du VLAN LAB
|
||||||
|
ciupgrade: false
|
||||||
|
|
||||||
- name: "{{ vm_name }} : démarrage"
|
- name: "{{ vm_name }} : démarrage"
|
||||||
ansible.builtin.command: qm start {{ vm.vm_id }}
|
community.proxmox.proxmox_kvm:
|
||||||
register: vm_start
|
node: "{{ pve_node }}"
|
||||||
changed_when: vm_start.rc == 0
|
vmid: "{{ vm.vm_id }}"
|
||||||
failed_when: vm_start.rc != 0 and 'already running' not in vm_start.stderr
|
state: started
|
||||||
|
timeout: 120
|
||||||
|
|
||||||
- name: "{{ vm_name }} : attente du SSH"
|
- name: "{{ vm_name }} : adresse DHCP remontée par l'agent QEMU"
|
||||||
|
community.proxmox.proxmox_vm_info:
|
||||||
|
node: "{{ pve_node }}"
|
||||||
|
vmid: "{{ vm.vm_id }}"
|
||||||
|
config: current
|
||||||
|
network: true
|
||||||
|
register: vm_net
|
||||||
|
until: >-
|
||||||
|
(vm_net.proxmox_vms[0].network | default([]) | rejectattr('name', 'equalto', 'lo')
|
||||||
|
| map(attribute='ip-addresses') | flatten | selectattr('ip-address-type', 'equalto', 'ipv4') | list | length) > 0
|
||||||
|
retries: 40
|
||||||
|
delay: 10
|
||||||
|
|
||||||
|
- name: "{{ vm_name }} : mémoriser l'IP"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
vm_ip: >-
|
||||||
|
{{ vm_net.proxmox_vms[0].network | rejectattr('name', 'equalto', 'lo')
|
||||||
|
| map(attribute='ip-addresses') | flatten | selectattr('ip-address-type', 'equalto', 'ipv4')
|
||||||
|
| map(attribute='ip-address') | first }}
|
||||||
|
|
||||||
|
- name: "{{ vm_name }} : écrire l'IP dans inventory/host_vars (relu par les playbooks suivants)"
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: "{{ inventory_dir }}/host_vars/{{ vm_name }}/dhcp.yml"
|
||||||
|
mode: "0644"
|
||||||
|
content: |
|
||||||
|
# Généré par 00-proxmox.yml : IP obtenue par DHCP (VMID {{ vm.vm_id }})
|
||||||
|
ansible_host: {{ vm_ip }}
|
||||||
|
|
||||||
|
- name: "{{ vm_name }} : attente du SSH sur {{ vm_ip }}"
|
||||||
ansible.builtin.wait_for:
|
ansible.builtin.wait_for:
|
||||||
host: "{{ vm.ansible_host }}"
|
host: "{{ vm_ip }}"
|
||||||
port: 22
|
port: 22
|
||||||
timeout: 300
|
timeout: 300
|
||||||
delegate_to: localhost
|
|
||||||
become: false
|
|
||||||
@@ -21,9 +21,9 @@ SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)"
|
|||||||
# shellcheck source=/dev/null
|
# shellcheck source=/dev/null
|
||||||
[ -f "$SCRIPT_DIR/bwpoc-tests.env" ] && . "$SCRIPT_DIR/bwpoc-tests.env"
|
[ -f "$SCRIPT_DIR/bwpoc-tests.env" ] && . "$SCRIPT_DIR/bwpoc-tests.env"
|
||||||
|
|
||||||
WAF_IP="${WAF_IP:-192.168.200.61}"
|
WAF_IP="${WAF_IP:?WAF_IP non défini (bwpoc-tests.env ou environnement)}"
|
||||||
DOMAIN="${DOMAIN:-bwpoc.internal}"
|
DOMAIN="${DOMAIN:-bwpoc.internal}"
|
||||||
APPS_IP="${APPS_IP:-192.168.200.62}"
|
APPS_IP="${APPS_IP:-}"
|
||||||
PORT_SHOP="${PORT_SHOP:-8082}"
|
PORT_SHOP="${PORT_SHOP:-8082}"
|
||||||
PORT_API="${PORT_API:-8083}"
|
PORT_API="${PORT_API:-8083}"
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user