NSX

Rôle

Micro-segmentation réseau de toute l'infrastructure Indio via VMware NSX-T 4.2 (NSX Manager 10.15.100.15). Le dépôt /root/nsx pilote, au travers du provider Terraform vmware/nsxt, l'ensemble des objets créés par l'administrateur de ce Manager : topologie de routage (T0/T1), segments réseau, groupes, services personnalisés et règles de pare-feu distribué (DFW).

Dépôt issu d'une rétro-ingénierie

Le code a été généré par export de la configuration existante (API Policy) puis reconstruit à l'identique — voir _export_raw/generate_tf.py et _export_raw/*.json. Au moment de sa génération, un terraform plan avec les blocs d'import donnait 43 to import, 0 to add, 0 to change, 0 to destroy : aucune dérive entre le code et le NSX Manager réel. Le fichier imports.tf qui portait ces 43 blocs a depuis été supprimé du dépôt (comportement attendu, documenté dans le README : il ne sert qu'au premier apply).

Seuls les objets _create_user = admin sont gérés : les ~415 services et 64 profils système livrés par défaut avec NSX sont volontairement exclus du code et seulement référencés par leur path quand une règle les utilise.

Architecture

Routage : un T0 en BGP, trois T1 par zone

Gateway Rôle Détail
T0-Edge Tier-0, ACTIVE_ACTIVE AS 65001, ECMP, 3 uplinks (10.15.91.1/28, .2/28, .3/28), voisin BGP FortiGate AS 65010 (10.15.91.14)
T1-Admin Zone admin Supervision, identité, backup, stockage — DHCP intégré
T1-DMZ Zone DMZ Proxys frontaux (Squid + DNS)
T1-SOC Zone SOC Outils de sécurité (SIEM) — DHCP intégré

Chaque T1 est raccordé au T0 (route_advertisement_types = ["TIER1_CONNECTED"], failover_mode = NON_PREEMPTIVE).

Segments (constat direct du code, 11 overlay + 2 VLAN)

Segment Zone CIDR DHCP Description
seg-sauvegarde Admin 10.100.3.30/28 non Agents de backup (Veeam, NetBackup…)
seg-supervision Admin 10.100.2.238/28 non Monitoring, syslog, SNMP, Zabbix/Prometheus
seg-identite Admin 10.100.2.254/28 non Active Directory, LDAP, Kerberos, PKI
seg-admin Admin 10.100.2.126/25 oui (.2-.121) Postes administrateurs, jump hosts
seg-data Admin 10.100.2.222/27 non Données et bases d'administration
seg-service Admin 10.100.2.190/26 non Services partagés d'administration
seg-storage Admin 10.100.3.14/28 oui (.2-.13) Interfaces mgmt NAS/SAN/vSAN — profil MAC learning dédié
seg-soc-admin SOC 10.100.20.1/26 oui (.10-.60) SOC — administration des outils
seg-soc SOC 10.100.20.129/25 non SOC — sous-réseau général
seg-proxy DMZ 10.100.10.1/29 non Proxy applicatif Squid (voir Proxy Squid & DNS)
seg-proxy-dns DMZ 10.100.10.9/29 non Proxy DNS unbound (voir Proxy Squid & DNS)
seg-t0-uplink (VLAN) VLAN 4091 Uplink T0 côté FortiGate
seg-edge-uplinks (VLAN) VLAN 0-4094 Trunk uplinks Edge

seg-storage porte un nsxt_policy_mac_discovery_profile dédié (mac_learning_enabled = true, mac_change_enabled = true, unknown_unicast_flooding_enabled = true) : les nœuds du vSAN File Service émettent avec des MAC non enregistrées auprès du segment, et le profil par défaut les rend injoignables (« no route to host ») malgré une gateway qui répond.

Vue d'ensemble de la topologie (constat direct de tier0.tf/tier1.tf/segments.tf) :

flowchart TD
    FORTI["FortiGate AS 65010<br/>10.15.91.14 (SNAT overlay)"]
    T0["T0-Edge — ACTIVE_ACTIVE<br/>AS 65001 · BGP · ECMP<br/>3 uplinks 10.15.91.1-3/28"]

    FORTI <-->|BGP| T0

    T0 --> T1A["T1-Admin<br/>DHCP intégré"]
    T0 --> T1D["T1-DMZ"]
    T0 --> T1S["T1-SOC<br/>DHCP intégré"]

    subgraph ZA[" Zone admin — DFW default ALLOW "]
        T1A --> SAUV["seg-sauvegarde<br/>10.100.3.30/28"]
        T1A --> SUPV["seg-supervision<br/>10.100.2.238/28"]
        T1A --> IDEN["seg-identite<br/>10.100.2.254/28"]
        T1A --> ADMN["seg-admin<br/>10.100.2.126/25 (DHCP)"]
        T1A --> DATA["seg-data<br/>10.100.2.222/27"]
        T1A --> SERV["seg-service<br/>10.100.2.190/26"]
        T1A --> STOR["seg-storage<br/>10.100.3.14/28 (DHCP, MAC learning)"]
    end

    subgraph ZD[" Zone DMZ — deny-all scopé "]
        T1D --> PRXY["seg-proxy<br/>10.100.10.1/29"]
        T1D --> PRXD["seg-proxy-dns<br/>10.100.10.9/29"]
    end

    subgraph ZS[" Zone SOC — deny-all scopé "]
        T1S --> SOCA["seg-soc-admin<br/>10.100.20.1/26 (DHCP)"]
        T1S --> SOCG["seg-soc<br/>10.100.20.129/25"]
    end

Groupes, services et objets de référence

  • Groupes (nsxt_policy_group) : trois familles de critères cohabitent — IP fixe/CIDR (ipaddress_expression, ex. grp-vcenter, grp-nsx-manager), appartenance à un segment (path_expression, ex. grp-seg-storage), et tag VM (condition, ex. grp-zone-admin). Ils sont répartis entre groups.tf (groupes génériques) et les fichiers dfw_*.tf (groupes posés à côté de la policy qui les consomme : dfw_dmz.tf, dfw_soc.tf, dfw_service.tf, dfw_extra.tf).
  • Services personnalisés (nsxt_policy_service) : 8 dans services.tf (svc-FleetServer, svc-Backup, svc-NFS-Full, svc-DNS-SSTP-ICMP, svc-LDAP, svc-Monitoring, svc-NSX-Intelligence, svc-StorageMgmt) + 2 posés directement dans dfw_dmz.tf (svc-Squid, svc-VRRP). svc-NFS-Full existe car les services NFS prédéfinis de NSX ne couvrent que 2049 (TCP/UDP) et 111/TCP, pas 111/UDP ni les ports mountd/lock/status utilisés par le vSAN File Service.
  • ip_pools.tf : TEP-Pool-Overlay (10.10.30.0/24, plage .20-.120) pour l'adressage TEP overlay.
  • dhcp.tf : deux serveurs DHCP intégrés aux gateways (dhcp-T1-Admin, dhcp-T1-SOC), adresses link-local 169.254.x.1/24.
  • data.tf : objets préexistants non gérés par ce module — TZ-Overlay, TZ-VLAN, EC-Edge-01.

Distributed IDS/IPS (Threat Prevention)

Module de détection/prévention d'intrusion distribué (licence NSX Data Center Distributed Threat Prevention, active). Le host prep du cluster (activation d'IDS/IPS sur les hyperviseurs) avait été fait manuellement en amont (_create_user = admin) — absorbé dans idps.tf par import Terraform, même logique que le reste du dépôt.

  • nsxt_policy_idps_cluster_config : active IDS/IPS sur le compute cluster vSphere (data.nsxt_compute_collection, nom paramétrable via var.vsphere_cluster_name, défaut "Cluster").
  • nsxt_policy_idps_settings (singleton global) : pas de mise à jour automatique des signatures (auto_update_signatures = false — ce Manager n'a pas d'accès confirmé vers le service de signatures VMware, la version DEFAULT embarquée reste utilisée telle quelle) ; export des événements IDS/IPS vers syslog activé (enable_syslog = true).
  • Profils (nsxt_policy_intrusion_service_profile) : IDS-Critical-High (sévérités CRITICAL/HIGH, zones DMZ et Admin) et IDS-Full-Coverage (toutes sévérités, zone SOC — visibilité maximale sur les outils de sécurité eux-mêmes).
  • Policy (nsxt_policy_intrusion_service_policy.idps_global, 3 rule) : une règle par zone (DMZ, SOC, Admin), scope par les groupes de segment déjà existants (grp-seg-*), action = "DETECT" partout — mode détection pure pour ce premier déploiement, pas de blocage actif (IPS) tant qu'une période d'observation n'a pas écarté les faux positifs sur le trafic réel.
  • Export syslog réel : le NSX Manager (hors overlay, 10.15.100.15) n'est pas un nœud de la flotte /root/baseline — l'exportateur syslog (exporter_name=syslog-ng-soc, UDP/514 vers 10.100.20.130) a été configuré à part via l'API node (/api/v1/node/services/syslog/exporters), hors Terraform (pas de ressource dédiée dans le provider vmware/nsxt). Nécessite la règle DFW mgmt-to-soc-syslog (dfw_soc.tf, source grp-mgmt-nat = IP SNAT FortiGate) — vérifié bout en bout (logs NSX visibles dans /var/log/syslogng/NSX/ côté syslog-ng).
flowchart LR
    ESXI["Hyperviseurs ESXi du cluster<br/>host prep IDS/IPS actif<br/>(nsxt_policy_idps_cluster_config)"]

    ESXI --> DMZT["Trafic zone DMZ<br/>seg-proxy / seg-proxy-dns"]
    ESXI --> ADMT["Trafic zone Admin<br/>7 segments seg-*"]
    ESXI --> SOCT["Trafic zone SOC<br/>seg-soc / seg-soc-admin"]

    DMZT --> RDMZ["rule idps-dmz<br/>profil IDS-Critical-High"]
    ADMT --> RADM["rule idps-admin<br/>profil IDS-Critical-High"]
    SOCT --> RSOC["rule idps-soc<br/>profil IDS-Full-Coverage"]

    RDMZ --> DETECT["action = DETECT (pas de blocage)<br/>logged = true"]
    RADM --> DETECT
    RSOC --> DETECT

    DETECT --> NSXMGR["NSX Manager 10.15.100.15<br/>enable_syslog = true"]
    NSXMGR -->|"exportateur API node<br/>syslog-ng-soc, UDP/514<br/>(hors Terraform)"| GATE{"DFW SOC deny-all scopé<br/>rule mgmt-to-soc-syslog<br/>source grp-mgmt-nat"}
    GATE -->|autorisé| SYSLOG["syslog-ng<br/>INDSOC001 · 10.100.20.130"]
    SYSLOG --> LOGS["/var/log/syslogng/NSX/"]
  • Non fait, évalué : Identity Firewall (règles DFW basées sur l'identité AD, ressource nsxt_policy_ldap_identity_source disponible dans le provider, prérequis déjà en place — compte svc-nsx-bind, LDAPS déjà actif sur les 2 DC) — réalisable mais pas implémenté, à faire sur demande. Malware Prevention et NSX Intelligence/NDR nécessitent le déploiement de NSX Application Platform (cluster Kubernetes dédié, composant séparé non provisionné) — hors de portée sans un provisioning massif supplémentaire ; la licence correspondante (NSX Firewall with Advanced Threat Prevention) est présente mais à quantité 0, donc non réellement exploitable en l'état.

Provisioning Terraform

  • Provider vmware/nsxt >= 3.7.0, Terraform >= 1.5.0.
  • Variables (variables.tf) : nsx_manager, nsx_username (défaut admin), nsx_password (sensitive), nsx_insecure (défaut true — certificat auto-signé).
  • Fichiers de ressources et contenu réel constaté (le README du dépôt liste un découpage légèrement plus ancien — voir encadré ci-dessous) :
Fichier Contenu
tier0.tf T0-Edge + 3 interfaces uplink + voisin BGP FortiGate
tier1.tf T1-Admin, T1-DMZ, T1-SOC
segments.tf 11 segments overlay + 2 VLAN + profil MAC learning vSAN
services.tf 8 services personnalisés
groups.tf Groupes génériques (IP, segment, tag)
security_policies.tf DFW-InterZone-Admin, DFW-Zone-Admin, DFW-Complements
dfw_dmz.tf Groupes + services + policy DFW-Zone-DMZ
dfw_service.tf Policy DFW-Service-Apps + groupe GLPI
dfw_soc.tf Policy DFW-Zone-SOC + groupes SOC
dfw_extra.tf Groupe grp-ad-dns
dhcp.tf Serveurs DHCP T1-Admin / T1-SOC
ip_pools.tf Pool TEP overlay
data.tf Data sources infra préexistante
idps.tf Distributed IDS/IPS : host prep cluster, settings globaux, profils et policy

README partiellement daté

Le README du dépôt décrit un état antérieur (2 security policies, 17 groupes, 7 segments overlay, 6 services). Le code a évolué depuis (ajout des zones DMZ/SOC détaillées, GLPI, Aria Operations for Logs, Zabbix, OCS…) : cette page reflète le contenu réel des fichiers .tf au 2026-07-17, pas le README.

Configuration Ansible

Aucune — ce dépôt ne contient pas de dossier ansible/. NSX est piloté uniquement via l'API Policy par Terraform ; il n'y a pas de configuration au niveau OS/applicatif à gérer ici.

Procédure manuelle

Point central de ce dépôt, déjà signalé dans l'encadré en tête de page mais jamais détaillé jusqu'ici : la quasi-totalité de la configuration NSX décrite par ce Terraform (T0/T1, segments, groupes, services, règles DFW) a été construite à la main dans le NSX Manager — UI et/ou appels API directs en tant qu'admin — avant même que ce dépôt existe. Ce n'est pas un provisioning ab initio : c'est de la rétro-ingénierie d'un existant déjà en place. Deux choses distinctes sont documentées ci-dessous : (1) la méthode de rétro-ingénierie elle-même — réutilisable si un futur objet est un jour créé à la main dans NSX Manager et qu'on veut le faire entrer sous IaC — et (2) un petit nombre de réglages qui restent, aujourd'hui encore, gérés en dehors de tout Terraform.

1. Comment ce dépôt a été construit à partir de l'existant (rétro-ingénierie)

Outillage conservé dans _export_raw/ (ignoré par git, présent uniquement en local) :

  1. Export brut : un fichier JSON par type d'objet, obtenu par des GET en lecture seule contre l'API Policy du NSX Manager (10.15.100.15) — les noms de fichiers correspondent directement aux endpoints standards de cette API (tier0s.jsonGET /policy/api/v1/infra/tier-0s, tier1s.json.../tier-1s, segments.json.../segments, groups.json.../domains/default/groups, services.json.../services, security_pol.json.../domains/default/security-policies, et de même pour t0_interfaces, t0_bgp/t0_bgp_neighbors, dhcp_server_cfg, ip_pools/ip_pool_subnets, context_profiles, edge_clusters, gateway_pol). La séquence exacte des appels (script ad hoc ou requêtes manuelles au fil de l'eau) n'est pas tracée dans l'historique du projet — seul le résultat (les JSON) a été conservé.
  2. Génération du Terraform : _export_raw/generate_tf.py (404 lignes, toujours présent sur disque) lit ces JSON, filtre aux seuls objets créés par un utilisateur (not _system_owned and _create_user != "system" — exclut les ~415 services et 64 profils système livrés par défaut avec NSX), puis émet les fichiers .tf un par type d'objet. Point technique clé : le script maintient une table de résolution path Policy → référence HCL (dict REF) au fur et à mesure qu'il traite les objets dans un ordre qui respecte les dépendances (T0 avant T1, T1 avant segments, objets avant groupes qui les référencent, etc.) — une référence croisée entre deux objets user devient une expression Terraform (nsxt_policy_xxx.yyy.path) plutôt qu'une chaîne de caractères, ce qui rend le code résultant lisible et refactorable au lieu d'un simple recopiage de paths bruts.
  3. Réconciliation du state : les objets déjà réels côté NSX ne peuvent pas être « créés » par un premier terraform apply sans provoquer un conflit — un fichier imports.tf temporaire (43 blocs import {}, un par ressource) a été généré en parallèle du reste, pointant chaque ressource Terraform vers l'ID réel de l'objet NSX correspondant (format d'ID spécifique par type, ex. <gateway>/<locale-service>/<id> pour une interface T0 — détail dans le README du dépôt). terraform plan avec ces blocs a montré 43 to import, 0 to add, 0 to change, 0 to destroy : preuve que le code généré correspondait exactement à la config réelle, aucune divergence introduite par la traduction JSON→HCL.
  4. terraform apply a alors peuplé le state à partir de ces imports (aucune écriture réelle côté NSX, diff nul). imports.tf a ensuite été supprimé — il ne sert qu'à ce premier rattachement, le garder provoque sinon des tentatives d'import répétées à chaque plan suivant.

Cette méthode (export ciblé par type d'objet → script de génération avec table de résolution de références → réconciliation par blocs import → suppression des imports) est directement réutilisable pour tout futur objet NSX créé hors Terraform (à la main ou par un autre outil) qu'on voudrait faire entrer sous IaC sans le recréer.

2. Ce qui reste configuré en dehors de Terraform aujourd'hui

  • Export syslog du NSX Manager : le NSX Manager lui-même (nœud d'administration, pas un objet du modèle Policy) exporte ses logs vers syslog-ng (exporter_name=syslog-ng-soc, protocole UDP, port 514, cible 10.100.20.130). Configuré via l'API node, pas l'API Policy — aucune ressource du provider vmware/nsxt ne couvre cet objet :
curl -sk -u admin:'<mot de passe NSX>' -X POST \
  https://10.15.100.15/api/v1/node/services/syslog/exporters \
  -H 'Content-Type: application/json' \
  -d '{
    "exporter_name": "syslog-ng-soc",
    "protocol": "UDP",
    "server": "10.100.20.130",
    "port": "514",
    "level": "INFO"
  }'

Nécessite la règle DFW mgmt-to-soc-syslog (dfw_soc.tf, source grp-mgmt-nat = IP SNAT FortiGate) déjà en place côté Terraform — seul l'export lui-même échappe à l'IaC. - Host prep Distributed IDS/IPS : l'activation d'IDS/IPS sur le compute cluster (licence Distributed Threat Prevention) a été faite avant l'écriture d'idps.tf, en tant qu'admin (_create_user = admin, même logique que le reste du dépôt), puis absorbée par le même mécanisme d'import que les autres objets. La méthode standard NSX pour ce type d'activation est NSX Manager → Security → Distributed IDS/IPS → Cluster Settings → Enable sur le cluster cible (ou l'appel API Policy équivalent) — mais la séquence exacte réellement suivie sur ce Manager n'est pas tracée dans l'historique du projet, seul le résultat final (nsxt_policy_idps_cluster_config importé, cf. Architecture ci-dessus) est connu avec certitude. - Plus généralement : toute la configuration T0/T1/segments/groupes/ services/DFW décrite dans ce dépôt a, par construction, été créée à la main avant d'être rétro-documentée (voir section 1) — il n'existe pas de procédure « from scratch » validée pour reconstruire ce NSX Manager entièrement par terraform apply sur un environnement vierge (aucun T0, aucune zone de transport, aucun Edge n'est créé par ce code, voir data.tf — ce sont des prérequis d'infrastructure NSX considérés comme déjà en place).

Procédure de déploiement

cp terraform.tfvars.example terraform.tfvars   # renseigner le mot de passe NSX
terraform init
terraform plan     # doit montrer 0 changement si rien n'a dérivé côté NSX Manager
terraform apply

Le terraform.tfstate est local (ignoré par git avec terraform.tfvars) ; pour un usage en équipe, le README suggère un backend distant (voir le bucket S3 disponible côté AWS, bien que ce dépôt ne l'utilise pas actuellement).

Points d'attention

  • Une règle direction = OUT (ou IN) seule ne couvre que les connexions initiées dans ce sens. Le caractère stateful = true des security policies fait que les paquets retour d'une connexion déjà établie sont automatiquement acceptés — mais dès qu'un flux logiquement distinct doit être initié dans le sens opposé (ex. un serveur qui vient interroger activement un agent, en plus de l'agent qui pousse vers le serveur), c'est une connexion différente qui exige sa propre règle. Dans une zone à deny-all scopé (DMZ, SOC), l'absence de cette règle complémentaire fait tomber ce second flux dans le deny-all — silencieusement. Exemple réel et documenté dans le code (dfw_dmz.tf, dfw_soc.tf) : la supervision Zabbix a nécessité deux règles séparées de part et d'autre — dmz-to-zabbix/zabbix-to-dmz et soc-to-zabbix/zabbix-to-soc — le polling passif (10050, le serveur interroge l'agent) restait bloqué tant que le pendant symétrique de la règle active (10051) n'existait pas.
  • Ordre d'évaluation (« cascade ») : NSX évalue d'abord par catégorie fixe (Ethernet > Emergency > Infrastructure > Environment > Application), puis par sequence_number croissant au sein d'une même catégorie, puis les rule {} d'une policy dans leur ordre d'écriture (premier match = décision, comme un ACL classique). Ordre réel des 6 policies de ce dépôt :

    Ordre Policy Catégorie sequence_number
    1 DFW-InterZone-Admin Infrastructure 0
    2 DFW-Zone-Admin Application 20
    3 DFW-Service-Apps Application 25
    4 DFW-Complements Application 30
    5 DFW-Zone-DMZ Application 40
    6 DFW-Zone-SOC Application 50

    Visualisé comme une cascade de décision (premier match = ALLOW/DROP immédiat, sinon passage à la policy suivante) :

    flowchart TD
        A(["Paquet évalué par le DFW distribué"]) --> B{"1. DFW-InterZone-Admin<br/>catégorie Infrastructure · seq 0"}
        B -->|match| Z(["Décision : ALLOW / DROP"])
        B -->|pas de match| C{"2. DFW-Zone-Admin<br/>catégorie Application · seq 20"}
        C -->|match| Z
        C -->|pas de match| D{"3. DFW-Service-Apps<br/>catégorie Application · seq 25"}
        D -->|match| Z
        D -->|pas de match| E{"4. DFW-Complements<br/>catégorie Application · seq 30"}
        E -->|match| Z
        E -->|pas de match| F{"5. DFW-Zone-DMZ<br/>catégorie Application · seq 40<br/>deny-all scopé en fin de policy"}
        F -->|match| Z
        F -->|pas de match| G{"6. DFW-Zone-SOC<br/>catégorie Application · seq 50<br/>deny-all scopé en fin de policy"}
        G -->|match| Z
        G -->|pas de match| H(["Default Layer3 Rule : ALLOW<br/>(catch-all NSX, hors code)"])
  • La zone admin n'a pas de deny-all scopé (contrairement à DMZ et SOC) : elle reste en allow par défaut. Les règles de DFW-Service-Apps (ex. accès à GLPI) sont donc posées pour la traçabilité/l'audit (logged = true), pas parce qu'elles sont strictement nécessaires au fonctionnement — un futur durcissement de la zone admin vers un modèle deny-all comme DMZ/SOC est anticipé dans les commentaires du code.

  • Piège SNAT FortiGate : tout accès management vers l'overlay est source-NATé par le FortiGate vers 10.15.91.14 avant d'entrer dans NSX — c'est cette IP que voit la DFW dans toutes les règles mgmt-to-*, pas l'IP réelle du poste administrateur.
  • grp-zone-admin (groupe par tag VM zone|admin, critère condition) a 0 membre réel en pratique (constaté le 2026-07-05) : le tag n'est jamais appliqué aux VM. Les règles qui doivent cibler « toute la zone admin » utilisent donc des groupes par appartenance de segment (path_expression), plus fiables.
  • La règle sauvegarde-to-aws-s3 (dans DFW-Zone-Admin) autorise en sortie HTTPS le segment seg-sauvegarde vers Internet — c'est le chemin réseau qu'emprunte l'archivage vers le bucket S3 dédié (voir AWS).
  • Les objets NSX prédéfinis (~415 services, 64 profils système) ne sont pas dans le code : normal, ils sont exclus par choix et référencés par path uniquement.