Prise en main

Flotte déclarative

Un unique fleet.yaml décrit chaque appliance — groupes, patches, faits par nœud — et `sihactl gen config` génère un document ApplianceConfig par appliance, prêt à appliquer avec élagage côté client.

Un seul fleet.yaml est la source de vérité pour toute une flotte. sihactl gen config le lit et écrit un document ApplianceConfig par appliance sous configs/. config apply sur un document l’interprète en ressources COSI sur le nœud, avec élagage automatique côté client — ce que vous retirez du fichier disparaît du nœud.

Source vs généré

fleet.yaml              # SOURCE — vous écrivez et committez ceci
patches/hardening.yaml  # patch réutilisable, inclus par @- là où nécessaire
patches/edge-acl.yaml   # autre patch

gen/dev/configs/        # GÉNÉRÉ par `gen config` (gitignoré, jamais committé)
  fw-1.yaml             #   document ApplianceConfig de fw-1
  fw-2.yaml             #   document ApplianceConfig de fw-2
  sihaconfig            #   votre identité client day-2

Vous éditez fleet.yaml (+ patches) ; gen config écrit tout sous configs/. Ce répertoire est gitignoré (il peut contenir des clés sensibles). Votre secret (la CA de la flotte) reste à l’extérieur, chiffré avec SOPS (voir Prise en main).

fleet.yaml

version: v1alpha1
fleetName: prod

patches:                            # 1. global — chaque appliance
  - "@./patches/hardening.yaml"

groups:
  - name: edge
    patches: ["@./patches/edge-acl.yaml"]

appliances:
  - name: fw-1
    endpoint: "192.0.2.10:6443"
    groups: [edge]
    network:
      interfaces:
        - name: wan
          pci: "0000:05:00.0"
          addresses: ["203.0.113.2/24"]
          rxMode: adaptive
        - name: lan
          pci: "0000:06:00.0"
          addresses: ["10.0.0.1/24"]
    nat:
      interfaces:
        - name: lan-inside
          interface: lan
          role: inside
        - name: wan-outside
          interface: wan
          role: outside

  - name: fw-2
    endpoint: "192.0.2.11:6443"
    groups: [edge]
    network:
      interfaces:
        - name: wan
          pci: "0000:05:00.0"
          addresses: ["203.0.113.3/24"]
        - name: lan
          pci: "0000:06:00.0"
          addresses: ["10.0.0.1/24"]
    nat:
      interfaces:
        - name: lan-inside
          interface: lan
          role: inside
        - name: wan-outside
          interface: wan
          role: outside
  • fleetName est informatif.
  • Les patches globaux s’appliquent à chaque appliance.
  • groups sont des ensembles nommés avec leurs propres patches. Une appliance y adhère en listant les noms de groupes. fw-1 et fw-2 reçoivent toutes deux le patch edge ; une troisième appliance qui omet groups: [edge] n’est pas affectée.
  • Les sections structurées (network:, nat:, …) décrivent ce que cette appliance EST — ses ports et ses adresses. Ce sont ses faits ; ils l’emportent sur les patches globaux/groupe (le plus spécifique gagne).
  • Les patches par appliance (non montrés) sont des exceptions de dernière passe qui l’emportent sur tout.

Ordre de cascade

patches globaux

patches de groupe (dans l'ordre des groupes listés sur l'appliance)

base structurée (les propres sections de l'appliance — l'emporte sur les patches)

patches par appliance (dernière passe — l'emporte sur tout)

Chaque étape est une fusion stratégique sensible au schéma : les listes fusionnent par leur clé (ex. par name pour les interfaces), les scalaires se remplacent, $patch: delete supprime une entrée par clé, @./file.yaml inline un fichier. Les champs inconnus sont des erreurs fatales.

La règle : les champs structurés décrivent ce qu’une appliance EST (ses faits : ports, adresses, pairs). Les patches expriment une politique partagée (globale/groupe) ou des exceptions de dernière passe.

ApplianceConfig — le document

Chaque exécution de gen config écrit un document ApplianceConfig par appliance. C’est un document YAML orienté opérateur — un schéma conçu avec des noms de sections concrets. Les types COSI n’y apparaissent jamais.

Sections :

SectionCe qu’elle configure
machine.dataplaneNiveau de performance VPP, hugepages, plugins
machine.managementScoping de la surface de management day-2 (quelles NICs physiques restent sur le plan de management)
network.interfacesToutes les interfaces VPP (PMD, bond, bridge, tap, loopback, VXLAN) + sous-interfaces (vlans, qinq)
routing.vrfsTables de routage IP
routing.staticRoutes statiques nommées
routing.bgpConfiguration BGP par VRF + pairs
firewall.aclsACLs avec règles ordonnées (par seq:) et bindings (bindTo:) intégrés
natRôles des interfaces, pools d’adresses, mappings statiques + identity
loadBalancerPlugin lb (VIPs, services NAT L4, modes d’interface)
vrrp.virtualRoutersRouteurs virtuels VRRP
dns.forwardersTransfert / mise en cache DNS
dhcp.servers / .relaysServeur DHCP embarqué ou relais

Chaque type COSI inscriptible par l’opérateur est accessible depuis le schéma — garanti par le test de couverture piloté par le registre.

Les kinds d’interface sont identifiés par la clé discriminante présente :

CléKind
pci: "0000:05:00.0"pmdNIC DPDK PMD lié par adresse PCI
hostDevice: enp5s0host-interfaceAttache de débogage AF_PACKET
bond: {members, mode}bondAgrégation de liens
bridge: {members, bvi}bridgeDomaine bridge L2
tap: {hostDevice, ...}tapTap VPP vers l’hôte
loopback: {}loopbackLoopback logiciel
vxlan: {vni, local, remote, l3Peer}vxlanTunnel L3VNI

Les sous-interfaces VLAN et QinQ s’imbriquent DANS leur entrée parente :

- name: uplink
  bond:
    members: [upA, upB]
    mode: xor
  addresses: ["10.99.0.2/24"]
  vlans:
    - id: 100
      addresses: ["10.100.0.2/24"]
  qinq:
    - outer: 200
      inner: 100
      addresses: ["10.200.0.2/24"]

Ceci dérive trois ressources Interfaces : uplink (bond), uplink.100 (VLAN 100), uplink.200.100 (QinQ).

Les règles ACL utilisent seq: à la fois comme clé de fusion et de tri ; bindTo: est intégré :

firewall:
  acls:
    - name: edge-in
      family: IPv4
      defaultAction: DROP
      rules:
        - seq: 10
          action: ACCEPT
          protocol: 1
          description: "ICMP diagnostics"
        - seq: 20
          action: ACCEPT_STATEFUL
          protocol: 6
          destinationPortFirst: 443
      bindTo:
        - interface: wan
          direction: INPUT

Patches — surcharges en couches

Un patch est un fragment de document ApplianceConfig (sans champ version:). Il fusionne par les mêmes clés que le document. Exemples :

Ajouter un binding à une ACL sans toucher ses règles (illustre la fusion récursive par liste de clés) :

firewall:
  acls:
    - name: edge-in
      bindTo:
        - { interface: dmz, direction: INPUT }

Ceci ajoute le binding dmz ; le binding wan et toutes les règles sont conservés.

Supprimer une règle par seq :

firewall:
  acls:
    - name: edge-in
      rules:
        - seq: 20
          $patch: delete

Retirer une ACL de groupe sur une appliance (patch par appliance) :

firewall:
  acls:
    - name: edge-in
      $patch: delete

Générer, valider, appliquer

# générer fleet.yaml → configs/*.yaml + sihaconfig
sihactl gen config -f fleet.yaml --secret siha-secret.yaml

# inspecter ce qui atterrirait sur fw-1 (types COSI dérivés+ids, sans appliquer)
sihactl gen config -f fleet.yaml --secret siha-secret.yaml --show-derived fw-1

# valider sans appliquer (interprétation + validation des adaptateurs)
sihactl config validate -f configs/fw-1.yaml

# adopter le nœud (idempotent — sans danger à relancer)
sihactl adopt --secret siha-secret.yaml --addr 192.0.2.10:6443

# puis appliquer (ou réappliquer) la config de jour 2
sihactl config apply --sihaconfig configs/sihaconfig --addr 192.0.2.10:6443 \
  -f configs/fw-1.yaml

# alternative ZTP tout-en-un : adopt + apply en une seule commande
sihactl config apply --insecure --secret siha-secret.yaml \
  --sihaconfig configs/sihaconfig --addr 192.0.2.10:6443 \
  -f configs/fw-1.yaml

Points clés :

  • gen config ne touche jamais un nœud — lecture et génération uniquement ; sûr à relancer (idempotent).
  • config apply est déclaratif : ce qui est dans le document est l’état désiré. Les ressources des types couverts qui n’apparaissent plus dans le document sont supprimées du nœud (élagage côté client).
  • Garde-fou d’effacement : --allow-wipe est requis si le document ne dérive aucune ressource (document vide contre un nœud configuré). --dry-run prévisualise les créations/mises à jour/suppressions sans toucher le nœud.

Voir CLI · sihactl pour la surface complète des commandes.