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
fleetNameest informatif.- Les
patchesglobaux s’appliquent à chaque appliance. groupssont des ensembles nommés avec leurs propres patches. Une appliance y adhère en listant les noms de groupes.fw-1etfw-2reçoivent toutes deux le patchedge; une troisième appliance qui ometgroups: [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
patchespar 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 :
| Section | Ce qu’elle configure |
|---|---|
machine.dataplane | Niveau de performance VPP, hugepages, plugins |
machine.management | Scoping de la surface de management day-2 (quelles NICs physiques restent sur le plan de management) |
network.interfaces | Toutes les interfaces VPP (PMD, bond, bridge, tap, loopback, VXLAN) + sous-interfaces (vlans, qinq) |
routing.vrfs | Tables de routage IP |
routing.static | Routes statiques nommées |
routing.bgp | Configuration BGP par VRF + pairs |
firewall.acls | ACLs avec règles ordonnées (par seq:) et bindings (bindTo:) intégrés |
nat | Rôles des interfaces, pools d’adresses, mappings statiques + identity |
loadBalancer | Plugin lb (VIPs, services NAT L4, modes d’interface) |
vrrp.virtualRouters | Routeurs virtuels VRRP |
dns.forwarders | Transfert / mise en cache DNS |
dhcp.servers / .relays | Serveur 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" | pmd | NIC DPDK PMD lié par adresse PCI |
hostDevice: enp5s0 | host-interface | Attache de débogage AF_PACKET |
bond: {members, mode} | bond | Agrégation de liens |
bridge: {members, bvi} | bridge | Domaine bridge L2 |
tap: {hostDevice, ...} | tap | Tap VPP vers l’hôte |
loopback: {} | loopback | Loopback logiciel |
vxlan: {vni, local, remote, l3Peer} | vxlan | Tunnel 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 configne touche jamais un nœud — lecture et génération uniquement ; sûr à relancer (idempotent).config applyest 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-wipeest requis si le document ne dérive aucune ressource (document vide contre un nœud configuré).--dry-runprévisualise les créations/mises à jour/suppressions sans toucher le nœud.
Voir CLI · sihactl pour la surface complète des commandes.