Exécutez vos playbooks sur des hôtes derrière NAT, firewall ou DMZ. Les agents initient eux-mêmes la connexion — vous n'ouvrez aucun port entrant.
Ansible est un outil remarquable. Mais son modèle SSH classique crée des angles morts sécurité et des dettes opérationnelles que la plupart des équipes découvrent trop tard.
Votre Control Node centralise les accès SSH à tous vos hôtes. Un token CI/CD leaké, une dépendance malveillante, un accès mal révoqué — et c'est l'ensemble de votre parc qui est à portée d'un attaquant. En une seule étape. Sans alarme.
La surface d'attaque croît linéairement avec la taille de votre infra.
Une clef volée. L'horloge tourne. Vous devez la supprimer sur 300 hôtes, maintenant. Avec SSH classique, c'est une opération manuelle, hôte par hôte, qui prend des heures — sans blacklist centrale, sans expiration automatique.
La rotation de clefs n'est pas un événement planifié. C'est une urgence.
Ansible n'a pas de log d'audit centralisé. Les traces restent sur le Control Node — modifiables, supprimables. En cas d'incident ou de contrôle ISO 27001, NIS2 ou SOC2, vous ne pouvez pas prouver qu'une configuration a été appliquée, ni par qui.
L'auditeur pose la question. Vous cherchez dans des logs dispersés sur plusieurs machines.
Ansible n'a pas de RBAC natif. Sur un Control Node standard, quiconque a accès peut lancer n'importe quel playbook sur n'importe quel hôte. AWX apporte du contrôle d'accès — au prix d'une stack lourde, d'une base de données et d'une interface à maintenir.
Vos droits sont gérés dans Azure AD. Votre automation n'a aucun contrôle d'accès.
Ansible pousse une configuration, puis rien. Entre deux runs, un admin fait un changement manuel, un paquet se met à jour, une règle firewall est modifiée. Votre infra dérive silencieusement de l'état décrit dans vos playbooks — et vous ne le saurez qu'au prochain incident.
Push-and-forget n'est pas de la gestion de configuration. C'est de l'espoir.
Un redémarrage inattendu, une coupure réseau — si un hôte est indisponible au moment du playbook, la tâche échoue et n'est jamais rejouée. En rolling deploy sur 50 machines, il suffit d'une qui redémarre pour avoir un parc partiellement patché, sans alerte.
Votre fenêtre de maintenance a 4 heures. 3 hôtes n'ont pas reçu le patch. Vous ne le savez pas.
C'est votre Control Node qui initie les connexions SSH vers les hôtes. Un hôte compromis connaît son adresse IP — et cette machine détient les clefs SSH de tout votre parc. Compromettre un hôte → cibler le Control Node → accéder à l'intégralité de l'infrastructure.
Vous avez segmenté vos hôtes. Mais le Control Node est le dénominateur commun de tous vos segments.
Production, DMZ, disaster recovery, cloud A, cloud B, sites edge — chaque segment doit avoir une route vers le Control Node. VPN site-à-site, peering cloud, tunnels SSH : chaque connexion est une règle firewall de plus, une surface d'attaque de plus, une configuration à maintenir.
Votre infra est multi-cloud. Votre automation ne devrait pas exiger un réseau plat entre tous vos environnements.
Ansible-SecAgent inverse le flux de contrôle. L'agent sur l'hôte cible initie une connexion WebSocket persistante vers le serveur relay. Ansible n'a besoin d'atteindre que le relay server — jamais les hôtes directement.
Un seul serveur relay pour démarrer. Idéal pour les petites infrastructures ou pour valider la solution en quelques minutes.
Plusieurs nodes relay derrière un load balancer. Les agents se connectent à n'importe quel node, le routage est transparent via cluster NATS.
Ansible-SecAgent s'intègre comme un plugin Ansible standard. Vos playbooks, rôles et collections fonctionnent sans modification. La transition est progressive : SSH reste disponible en parallèle.
Tous vos playbooks, rôles et collections Ansible Galaxy fonctionnent sans modification. Changez juste connection: relay dans ansible.cfg.
Ansible-SecAgent ne supprime pas SSH. Les deux coexistent. Idéal pour une migration progressive : passez hôte par hôte sans rupture de service.
Le plugin inventory liste automatiquement tous les agents connectés avec leurs facts. Vos inventaires statiques restent compatibles en parallèle.
become: true et become_user fonctionnent comme avec SSH. Le become_pass est transmis de façon sécurisée, masqué dans tous les logs.
Ansible-SecAgent est conçu autour d'un modèle Zero Trust. Chaque décision d'architecture a une justification sécurité explicite, documentée dans les specs techniques.
Chaque agent génère sa paire de clefs RSA-4096 au premier démarrage. La clef privée ne quitte jamais l'hôte.
La clef publique de l'agent doit être pré-autorisée en base de données avant tout enrôlement. Zéro TOFU (Trust On First Use).
Le token JWT retourné à l'agent est chiffré avec sa clef publique RSA. Illisible sans la clef privée, même en cas d'interception réseau.
Révocation immédiate par JTI (JWT ID unique). La connexion WebSocket active est fermée avec le code 4001 — l'agent ne reconnecte pas automatiquement.
Rotation des secrets JWT sans interruption de service. Grace period configurable : les anciens tokens restent valides pendant la migration.
WSS (WebSocket over TLS) et HTTPS sur toutes les connexions. Terminaison TLS via Caddy. Aucun plain HTTP accepté.
Le mot de passe sudo/su est transmis via stdin chiffré et masqué dans tous les logs — agent, serveur et plugins Ansible.
Les secrets serveur (RSA keypair, JWT secrets) sont chiffrés en base de données via AES-256-GCM dérivé de RSA_MASTER_KEY.
| Vecteur d'attaque | SSH classique | Ansible-SecAgent |
|---|---|---|
| Port entrant exposé | Port 22 ouvert | Aucun port entrant |
| Interception du token d'auth | Clef SSH en clair sur disque | JWT chiffré RSA-OAEP |
| Agent compromis → propagation | Clef SSH donne accès direct | JTI révocable immédiatement |
| Agent non autorisé | TOFU implicite possible | Pre-authorize obligatoire |
| Rotation des secrets | Manuelle, downtime | Dual-key, grace period, zero downtime |
| Fuite become_pass dans logs | Risque selon configuration | Masqué dans tous les logs |
Ansible-SecAgent est développé par phases incrémentales, chacune validée par tests automatisés et audit sécurité avant déploiement.
La CLI cobra est intégrée directement dans le binaire secagent-server.
Accessible depuis un container Docker ou un pod Kubernetes — sans binaire supplémentaire.
Listez, suspendez, révoquez et autorisez les agents. Gérez leurs variables d'inventaire.
secagent-server minions list
secagent-server minions get my-host
secagent-server minions suspend my-host
secagent-server minions resume my-host
secagent-server minions revoke my-host
secagent-server minions authorize new-host \
--pubkey "$(cat pubkey.pem)"
secagent-server minions vars set my-host \
env=prod region=eu-west
Rotation des secrets JWT zero downtime, gestion des tokens actifs et de la blacklist JTI.
# Rotation JWT (grace period configurable)
secagent-server security keys rotate --grace 24h
# État des clefs
secagent-server security keys status
# Tokens actifs et blacklist
secagent-server security tokens list
secagent-server security blacklist list
secagent-server security blacklist purge
Visualisez les agents connectés et leurs facts directement depuis la CLI.
# Agents connectés uniquement
secagent-server inventory list --only-connected
# Format JSON (compatible Ansible)
secagent-server inventory list --format json
# Format YAML
secagent-server inventory list --format yaml
Consultez l'état du serveur relay : uptime, connexions actives, tâches exécutées, mémoire.
# État général
secagent-server server status --format table
# Statistiques détaillées
secagent-server server stats --format json
# Exemple de sortie :
# UPTIME AGENTS TASKS 24H MEMORY
# 3d 14h 4 / 5 127 12 MB
ADMIN_TOKEN, JWT_SECRET_KEY). Aucune configuration supplémentaire requise.
# Docker
docker exec relay-api secagent-server <commande>
# Kubernetes
kubectl exec -n ansible-relay deploy/relay-api -- secagent-server <commande>
Les phases suivantes sont spécifiées dans ARCHITECTURE.md et prêtes à démarrer.
Helm chart complet : Deployment relay-api (replicas 3), StatefulSet NATS JetStream (cluster, PVC 20Gi fast-ssd), Ingress nginx avec TLS cert-manager, Secrets K8s pour JWT_SECRET_KEY et ADMIN_TOKEN. PostgreSQL externe (RDS/CloudSQL) en remplacement de SQLite.
Rate limiting sur les endpoints sensibles (/api/register, /api/exec), audit logs structurés (JSON, syslog), RBAC granulaire par hostgroup, métriques Prometheus / Grafana dashboard, runbook opérationnel.
Intégration enterprise : enrollment via ipa-client-install, authentification mTLS avec certificats signés par l'AC FreeIPA (Dogtag), révocation via CRL/OCSP, inventory plugin LDAP (hostgroups IPA → groupes Ansible natifs).
Choisissez votre méthode de déploiement.
/etc/secagent-minion/id_rsa (mode 0600). Il tente immédiatement de s'enroller auprès du serveur — mais le serveur refuse tant que la clef publique n'est pas pré-autorisée. L'agent retente automatiquement avec un backoff exponentiel.
git clone https://github.com/CCoupel/Ansible-SecAgent.git
cd Ansible-SecAgent/GO
export JWT_SECRET_KEY="$(openssl rand -hex 32)"
export ADMIN_TOKEN="$(openssl rand -hex 16)"
export RSA_MASTER_KEY="$(openssl rand -hex 32)"
docker compose up -d
# === Sur l'hôte cible (accès initial : SSH, USB, provisioning existant…) ===
cp secagent-minion /usr/local/bin/secagent-minion
# Variables d'environnement de l'agent
export RELAY_SERVER_URL=wss://relay.example.com
export RELAY_HOSTNAME=my-host
# Clef privée : /etc/secagent-minion/id_rsa (créée au 1er démarrage, mode 0600)
# JWT : /etc/secagent-minion/token.jwt (créé après enrollment réussi)
./secagent-minion
# → Génère RSA-4096 dans /etc/secagent-minion/id_rsa
# → POST /api/register → 403 (pas encore autorisé) → retente en backoff
# === Toujours sur l'hôte cible : extraire la clef publique ===
openssl rsa -in /etc/secagent-minion/id_rsa -pubout 2>/dev/null
# → -----BEGIN PUBLIC KEY-----
# MIICIjANBgkqhkiG9w0BAQ...
# -----END PUBLIC KEY-----
# === Sur le serveur relay : enregistrer la clef publique ===
docker exec relay-api secagent-server minions authorize my-host \
--pubkey "$(ssh user@my-host openssl rsa -in /etc/secagent-minion/id_rsa -pubout 2>/dev/null)"
# → L'agent retente automatiquement et se connecte
ansible-playbook site.yml # Aucune modification du playbook requise !
# Vérifier la connexion
docker exec relay-api secagent-server minions list --format table
/etc/secagent-minion/id_rsa (mode 0600). Il tente immédiatement de s'enroller auprès du serveur — mais le serveur refuse tant que la clef publique n'est pas pré-autorisée. L'agent retente automatiquement avec un backoff exponentiel.
git clone https://github.com/CCoupel/Ansible-SecAgent.git
cd Ansible-SecAgent/GO
go build -o secagent-server ./cmd/server
go build -o secagent-minion ./cmd/agent
go build -o secagent-inventory ./cmd/inventory
nats-server -js -p 4222 &
# ou : docker run -d --rm -p 4222:4222 nats:latest -js
export JWT_SECRET_KEY="$(openssl rand -hex 32)"
export ADMIN_TOKEN="$(openssl rand -hex 16)"
export RSA_MASTER_KEY="$(openssl rand -hex 32)"
export NATS_URL="nats://localhost:4222"
./secagent-server serve --addr :8080
# === Sur l'hôte cible ===
cp secagent-minion /usr/local/bin/
cp secagent-minion.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now secagent-minion
# → Génère /etc/secagent-minion/id_rsa (RSA-4096, mode 0600)
# → Tente l'enrollment → refusé → retente en backoff
# === Sur l'hôte cible : extraire la clef publique depuis la clef privée ===
openssl rsa -in /etc/secagent-minion/id_rsa -pubout 2>/dev/null
# === Sur le serveur relay ===
./secagent-server minions authorize my-host \
--pubkey "$(ssh user@my-host openssl rsa -in /etc/secagent-minion/id_rsa -pubout 2>/dev/null)"
# → L'agent retente et se connecte. JWT stocké dans /etc/secagent-minion/token.jwt
kubectl create namespace ansible-relay
kubectl create secret generic relay-secrets -n ansible-relay \
--from-literal=JWT_SECRET_KEY="$(openssl rand -hex 32)" \
--from-literal=ADMIN_TOKEN="$(openssl rand -hex 16)" \
--from-literal=RSA_MASTER_KEY="$(openssl rand -hex 32)"
helm upgrade --install ansible-relay ./helm/ansible-relay \
-n ansible-relay \
--set ingress.host=relay.example.com \
--set replicaCount=3
kubectl get pods -n ansible-relay
kubectl exec -n ansible-relay deploy/relay-api -- \
secagent-server server status --format table
kubectl exec -n ansible-relay deploy/relay-api -- \
secagent-server minions list --format table
# Rotation JWT zero downtime
kubectl exec -n ansible-relay deploy/relay-api -- \
secagent-server security keys rotate --grace 24h