NetBox (IPAM / DCIM)

Rôle

NetBox est la source de vérité IPAM/DCIM de l'infrastructure Indio : plans d'adressage, réservations IP, inventaire des VM (rôle, plateforme, cluster), et pilotage de l'allocation IP de la quasi-totalité des autres projets Terraform via son provider dédié (e-breuninger/netbox).

Architecture

  • VM unique INDSERV011, zone admin (seg-service), 10.100.2.140/26, 2 vCPU / 4 Go, clone du template Rocky 9.6 durci.
  • Pile applicative en conteneurs Podman (déployée par Ansible, pas de compose/systemd-quadlet) : PostgreSQL 16, Redis 7 (cache + file de tâches asynchrones), NetBox (gunicorn) exposé directement en HTTP sur le port 8080, et un worker RQ dédié aux tâches asynchrones. Tous les conteneurs communiquent sur un réseau Podman dédié (netbox-net).
  • 18 des 19 projets IaC de la flotte lisent leur IP via le provider Terraform e-breuninger/netbox (data netbox_ip_addresses filtrée sur dns_name) au lieu d'une IP en dur. netbox lui-même reste en IP fixe (dépendance circulaire : il ne peut pas dépendre de l'API qu'il héberge). veeam est le seul projet applicatif qui n'a pas basculé — voir Veeam.
  • Jeton API Terraform dédié, lecture seule (write_enabled=false), stocké dans le terraform.tfvars (gitignored) de chaque projet consommateur.

Structure du dépôt

Fichier / dossier Contenu
main.tf VM unique vsphere_virtual_machine.netbox, IP fixe
variables.tf vm_name=INDSERV011, vsphere_network=seg-service, cpu=2, ram=4096
versions.tf Provider hashicorp/vsphere uniquement (pas de provider netbox ici)
ansible/deploy.yml Playbook, rôle unique netbox
ansible/roles/netbox/tasks/main.yml Podman : réseau, PostgreSQL, Redis, NetBox, worker
ansible/roles/netbox/templates/netbox.env.j2 Fichier d'environnement (secrets ansible-vault)
seed/populate.py Seed initial complet — piège noms obsolètes, voir Points d'attention
seed/add_k8s_vms.py Ajout ciblé : 6 VM cluster Kubernetes
seed/add_postfix_vms.py Ajout ciblé : 2 VM Postfix
seed/add_identity_bare_vms.py Ajout ciblé : 4 VM segment identité

README du dépôt partiellement daté

Le README.md du dépôt décrit une architecture avec un frontal Caddy/nginx devant NetBox — inexistant dans le code réel : le rôle Ansible expose gunicorn directement sur le port 8080 (podman run ... -p {{ netbox_http_port }}:8080), sans aucun reverse proxy local. Cette page reflète le contenu réel de roles/netbox/tasks/main.yml au 2026-07-19, pas le README.

Vue d'ensemble

flowchart LR
    subgraph NB["VM netbox — INDSERV011 (10.100.2.140:8080)"]
        direction TB
        APP["NetBox (gunicorn)"]
        WRK["worker RQ"]
        PG[("PostgreSQL 16")]
        RD[("Redis 7")]
        APP --> PG
        APP --> RD
        WRK --> PG
        WRK --> RD
    end

    subgraph Consumers["Projets Terraform consommateurs (data netbox_ip_addresses)"]
        K8S["k8s"]
        GLPI["glpi"]
        OCS["ocs"]
        PFX["postfix"]
        AUTRES["... 14 autres projets"]
    end

    Consumers -->|"GET /api/ipam/ip-addresses/?dns_name=...<br/>jeton lecture seule"| APP

    subgraph Seed["Scripts seed/*.py (jeton temporaire provisionné puis révoqué)"]
        POP["populate.py<br/>seed initial"]
        ADDK8S["add_k8s_vms.py"]
        ADDPFX["add_postfix_vms.py"]
        ADDID["add_identity_bare_vms.py"]
    end

    Seed -->|"POST/PATCH ponctuel"| APP

Provisioning Terraform

  • main.tf : VM unique vsphere_virtual_machine.netbox, IP fixe (var.vm_ip, défaut 10.100.2.140) — pas de lookup NetBox pour sa propre IP (bootstrap circulaire).
  • Variables clés (variables.tf) : vm_name = INDSERV011, vsphere_network = seg-service, vm_cpu = 2, vm_ram = 4096.
  • Providers (versions.tf) : hashicorp/vsphere uniquement — contrairement aux autres projets, pas de provider netbox ici.

Configuration Ansible

  • ansible/deploy.yml applique le rôle unique netbox sur l'hôte netbox (inventory.ini).
  • Rôle netbox (roles/netbox/tasks/main.yml) : installe Podman, crée le réseau netbox-net, lance dans l'ordre les conteneurs PostgreSQL (postgres:16-alpine) puis Redis (redis:7-alpine, --requirepass), attend pg_isready (jusqu'à 20 tentatives), ouvre le port HTTP en firewalld (zone drop), lance le conteneur NetBox (netboxcommunity/netbox:{{ netbox_version }}, --env-file netbox.env, port 8080), attend que /login/ réponde 200 (jusqu'à 40 tentatives × 15 s — migrations de schéma au premier démarrage), puis lance le worker RQ (manage.py rqworker). Toutes les commandes podman run sont gardées par podman container exists <nom> || pour l'idempotence. podman-restart.service activé pour la persistance des conteneurs au reboot.
  • Fichier d'environnement /opt/netbox/netbox.env (template netbox.env.j2, mode 0600) : toutes les valeurs sensibles (mots de passe DB/Redis, clé secrète Django, mot de passe superutilisateur) proviennent de group_vars/all.yml, chiffrées en ansible-vault (netbox_db_password, netbox_redis_password, netbox_superuser_password, netbox_secret_key).

Procédure manuelle

  1. Jeton API Terraform (lecture seule) — créé une fois, manuellement, via l'API/UI NetBox (write_enabled=false), puis copié dans le terraform.tfvars (gitignored) de chaque projet consommateur. Aucune ressource Terraform/Ansible ne génère ce jeton — mécanisme de rotation à gérer hors IaC (coffre-fort de secrets Indio).
  2. Exécution ponctuelle de seed/populate.py — jouée une seule fois à la mise en place initiale de l'IPAM (RIR, site, cluster, rôles, prefixes, VM/IP de la flotte de l'époque). Toute VM ajoutée depuis passe par un script ciblé dédié (add_k8s_vms.py, add_postfix_vms.py, add_identity_bare_vms.py), jamais par un rejeu du seed complet — voir Points d'attention.

Procédure de déploiement

  1. terraform apply → provisionne la VM netbox.
  2. ansible-playbook deploy.yml → déploie la pile Podman complète.
  3. python3 seed/populate.py (mise en place initiale uniquement) → peuple le modèle IPAM/DCIM complet : RIR, site, cluster, rôles de zone et rôles fonctionnels, prefixes, IP flottantes/VIP, VM avec interface et IP primaire. Idempotent (get-or-create), authentification via un jeton API provisionné puis révoqué à la fin du script.
  4. Scripts d'ajout ciblés (seed/add_k8s_vms.py, seed/add_postfix_vms.py, seed/add_identity_bare_vms.py) → ajoutent uniquement les VM d'un projet précis après coup, sans toucher au reste du modèle.
  5. Bascule progressive des projets Terraform de la flotte vers une lecture d'IP via le provider NetBox (déjà effective pour 18 des 19 projets applicatifs).

Contrôle de santé / Vérification

curl -sk -o /dev/null -w '%{http_code}\n' http://10.100.2.140:8080/login/   # 200 attendu
podman ps --filter name=netbox                                              # 4 conteneurs Up
podman exec netbox-postgres pg_isready -U <netbox_db_user>

Points d'attention

Piège majeur (seed) — déjà rencontré, a créé 31 VM fantômes

seed/populate.py est idempotent (get-or-create/patch) mais sa liste VMS contient encore des noms antérieurs à la convention IND<SEGMENT><NNN> du 2026-07-04. Vérifié dans le code au 2026-07-19, 7 entrées sont toujours dans l'ancien format : haproxy-01/02/03, proxy-01/02, proxy-dns-01/02, ntp-01/02/03 (netbox et nexus sont des exceptions volontaires, jamais renommées). Comme get_or_create filtre par name, rejouer le script entier ne retrouve aucune de ces VM (elles s'appellent désormais INDSERV002/003/004 etc.) et en recrée une fantôme par ancienne entrée. Déjà arrivé une fois (31 VM fantômes créées en ajoutant 10 lignes puis en relançant tout le script), nettoyé via un script ad hoc. Pour ajouter une VM, toujours écrire ou réutiliser un script ciblé (voir add_k8s_vms.py/add_postfix_vms.py/add_identity_bare_vms.py comme modèles) — ne jamais relancer populate.py en entier tant que ces 7 entrées n'ont pas été corrigées ou retirées.

  • netbox est volontairement exclu de l'allocation IP via son propre provider Terraform (dépendance circulaire) et reste en IP fixe dans ses variables.tf.
  • Les scripts seed/*.py provisionnent un jeton API dédié (/users/tokens/provision/) et le révoquent systématiquement en fin d'exécution (bloc finally) — distinct du jeton Terraform persistant (lecture seule), qui lui n'est jamais révoqué automatiquement.
  • Les identifiants par défaut utilisés par les scripts de seed (variables d'environnement NB_USER/NB_PASS) ont une valeur de repli codée en dur dans le code source si ces variables ne sont pas positionnées — à surcharger systématiquement via l'environnement plutôt que de s'appuyer sur ce comportement par défaut.
  • Intégration DNS FreeIPA/AD (NetBox source de vérité → enregistrements A/PTR poussés automatiquement) encore marquée comme en attente dans le README du dépôt.
  • Accès direct au port 8080 non joignable depuis tous les segments (le nœud de contrôle management ne route pas vers seg-service:8080 — testé le 2026-07-19) : passer par un hôte déjà routé (proxy DMZ, bastion) pour toute vérification API depuis l'extérieur de la zone admin.