Skip to content

Sécurité & Configuration (Agent)

Par défaut, un heartbeat est envoyé toutes les 30 secondes. Son payload contient : agent_id, version, hostname (selon politique de confidentialité), timestamp UTC, durée depuis démarrage, état Docker, nombre de containers connus, dernière collecte métrique, version du protocole et capacité (logs, pull, exec). L’API répond par un ACK et peut transmettre une configuration révisée non sensible.

  • Health Docker : timeout court sur Ping ; l’Agent reste vivant mais signale docker_unhealthy.
  • Health API : un heartbeat manqué n’entraîne pas l’arrêt du processus ; l’état passe degraded après quelques échéances.
  • Reconnexion : nouveaux appels gRPC après fermeture du canal, avec backoff exponentiel borné (1 s, 2 s, 4 s… maximum 60 s) et jitter.
  • Shutdown : arrêt des workers, annulation des streams, flush du spool dans une deadline courte, puis fermeture Docker et gRPC.

Le heartbeat ne remplace pas les probes systemd. Un watchdog local peut vérifier que la boucle principale progresse ; il ne doit pas redémarrer l’Agent sur une simple coupure réseau.

  1. L’API possède une CA de confiance (ou une PKI d’entreprise) et un certificat serveur dont le SAN correspond au nom gRPC.
  2. L’Agent possède un certificat client signé par cette CA et sa clé privée lisible uniquement par le compte de service.
  3. Chaque partie vérifie le certificat de l’autre pendant le handshake TLS ; l’API mappe le sujet/SAN du certificat à un agent_id et au client mono-tenant.
  4. Le token d’enrôlement n’est pas utilisé comme secret permanent : après émission du certificat, l’Agent utilise mTLS et, si souhaité, un token opaque court dans les métadonnées gRPC pour une défense en profondeur.
func ClientTLS(certFile, keyFile, caFile string) (*tls.Config, error) {
cert, err := tls.LoadX509KeyPair(certFile, keyFile); if err != nil { return nil, err }
caPEM, err := os.ReadFile(caFile); if err != nil { return nil, err }
roots := x509.NewCertPool(); if !roots.AppendCertsFromPEM(caPEM) { return nil, errors.New("invalid CA") }
return &tls.Config{MinVersion: tls.VersionTLS13, Certificates: []tls.Certificate{cert}, RootCAs: roots, ServerName: "berth-api"}, nil
}
  • L’administrateur crée dans l’API un Agent et un token unique, aléatoire, à usage unique, avec expiration courte et empreinte stockée côté API (jamais le token en clair).
  • L’Agent est installé avec ce token hors du dépôt Git et contacte l’API en bootstrap TLS contrôlé ; il envoie son token, une clé publique/CSR et sa version.
  • L’API valide le token, associe la demande au client et signe le certificat client. L’Agent écrit certificat et clé avec permissions 0600, efface le token et redémarre sa connexion mTLS.
  • La rotation est proactive avant expiration : nouvelle clé/CSR, validation par l’ancien certificat, installation atomique, puis révocation de l’ancien certificat après chevauchement.
  • Une révocation (compromission, désinstallation) bloque l’agent côté API via serial/SAN et invalide les secrets associés.

Ne pas placer une clé privée dans une image de container, un fichier de configuration versionné ou les logs. En déploiement containerisé, monter les certificats en lecture seule et protéger le volume hôte.

configs/config.example.yaml
agent_id: "" # fourni après enrollment
api:
address: "api.berth.example.com:443"
server_name: "berth-api"
enrollment_token_file: "/etc/berth-agent/enrollment-token"
request_timeout: 10s
heartbeat_interval: 30s
metrics_interval: 15s
discovery_interval: 60s
docker:
host: "unix:///var/run/docker.sock"
request_timeout: 10s
labels_allowlist: ["com.berth.application_id", "com.berth.managed"]
tls:
ca_file: "/etc/berth-agent/tls/ca.pem"
cert_file: "/etc/berth-agent/tls/agent.crt"
key_file: "/etc/berth-agent/tls/agent.key"
renew_before: 168h
resilience:
spool_dir: "/var/lib/berth-agent/spool"
max_spool_bytes: 104857600
max_retries: 5
backoff_max: 60s
logs:
level: "info"
format: "json"

Les secrets et chemins peuvent être surchargés par variables d’environnement (BERTH_API_ADDRESS, BERTH_TLS_KEY_FILE, etc.). La configuration est validée au démarrage : URL gRPC, intervalle positif, certificats lisibles, spool sous une limite et socket Docker accessible.