From e648009b8771a9196c4df3c04da4c2476b2ec6ea Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Sat, 4 Jul 2026 21:04:40 -0400 Subject: [PATCH] =?UTF-8?q?Z=C3=A9ro-confiance=20:=20flux=20M=C3=A9triques?= =?UTF-8?q?=20chiffr=C3=A9=20(node=5Fexporter=20TLS=20+=20scrape=20https)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit node_exporter sert en HTTPS via le cert step-ca (--web.config.file + cert-sync owned prometheus, motif .path). Prometheus scrape en scheme https + tls_config (ca=root_ca), vérifie contre l'IP en SAN. Vars : client_metrique_tls_actif, serveur_prometheus_metriques_tls. Prouvé : node_exporter HTTPS 200 (vérif root_ca), HTTP rejeté (400), 4 cibles Prometheus UP en https sans erreur. Co-Authored-By: Claude Opus 4.8 --- roles/client_metrique/defaults/main.yml | 8 ++ roles/client_metrique/tasks/main.yml | 83 +++++++++++++++++++ .../templates/default-node-exporter.j2 | 2 +- .../setops-node-exporter-cert-sync.path.j2 | 10 +++ .../setops-node-exporter-cert-sync.service.j2 | 7 ++ .../setops-node-exporter-cert-sync.sh.j2 | 18 ++++ .../templates/web-config.yml.j2 | 5 ++ roles/serveur_prometheus/defaults/main.yml | 4 + .../templates/prometheus.yml.j2 | 5 ++ 9 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 roles/client_metrique/templates/setops-node-exporter-cert-sync.path.j2 create mode 100644 roles/client_metrique/templates/setops-node-exporter-cert-sync.service.j2 create mode 100644 roles/client_metrique/templates/setops-node-exporter-cert-sync.sh.j2 create mode 100644 roles/client_metrique/templates/web-config.yml.j2 diff --git a/roles/client_metrique/defaults/main.yml b/roles/client_metrique/defaults/main.yml index db6a4b1..11e7b69 100644 --- a/roles/client_metrique/defaults/main.yml +++ b/roles/client_metrique/defaults/main.yml @@ -7,3 +7,11 @@ client_metrique_service: "prometheus-node-exporter" # Adresse d'ecoute de node_exporter. Par defaut toutes les interfaces ; la # segmentation reseau (VLAN / nftables) restreint l'acces au serveur Prometheus. client_metrique_adresse_ecoute: ":9100" + +# --- TLS (zero-confiance) : node_exporter sert en HTTPS avec le cert step_ca --- +# Requiert client_pki sur le noeud. Prometheus scrape alors en https + verifie. +client_metrique_tls_actif: false +client_metrique_tls_dir: "/etc/prometheus-node-exporter/tls" +client_metrique_web_config: "/etc/prometheus-node-exporter/web-config.yml" +client_metrique_tls_source_cert: "/etc/step/certs/{{ ansible_fqdn | default(ansible_hostname) }}.crt" +client_metrique_tls_source_cle: "/etc/step/certs/{{ ansible_fqdn | default(ansible_hostname) }}.key" diff --git a/roles/client_metrique/tasks/main.yml b/roles/client_metrique/tasks/main.yml index e5f4939..3a123e1 100644 --- a/roles/client_metrique/tasks/main.yml +++ b/roles/client_metrique/tasks/main.yml @@ -6,6 +6,89 @@ update_cache: true cache_valid_time: 3600 +# --- TLS : synchroniser le cert step_ca AVANT d'activer --web.config.file --- +- name: TLS — repertoire des certificats (prometheus) + ansible.builtin.file: + path: "{{ client_metrique_tls_dir }}" + state: directory + owner: prometheus + group: prometheus + mode: "0700" + when: client_metrique_tls_actif | bool + +- name: TLS — script de synchronisation du certificat + ansible.builtin.template: + src: setops-node-exporter-cert-sync.sh.j2 + dest: /usr/local/sbin/setops-node-exporter-cert-sync + owner: root + group: root + mode: "0755" + when: client_metrique_tls_actif | bool + +- name: TLS — unites de synchronisation (service + path) + ansible.builtin.template: + src: "{{ item.s }}" + dest: "{{ item.d }}" + owner: root + group: root + mode: "0644" + loop: + - { s: "setops-node-exporter-cert-sync.service.j2", d: "/etc/systemd/system/setops-node-exporter-cert-sync.service" } + - { s: "setops-node-exporter-cert-sync.path.j2", d: "/etc/systemd/system/setops-node-exporter-cert-sync.path" } + loop_control: + label: "{{ item.d | basename }}" + when: client_metrique_tls_actif | bool + +- name: TLS — activer la surveillance du certificat (path) + ansible.builtin.systemd: + name: setops-node-exporter-cert-sync.path + enabled: true + state: started + daemon_reload: true + when: + - client_metrique_tls_actif | bool + - not ansible_check_mode + +- name: TLS — synchroniser le certificat maintenant + ansible.builtin.command: /usr/local/sbin/setops-node-exporter-cert-sync + changed_when: false + when: + - client_metrique_tls_actif | bool + - not ansible_check_mode + +- name: TLS — deployer la web-config de node_exporter + ansible.builtin.template: + src: web-config.yml.j2 + dest: "{{ client_metrique_web_config }}" + owner: root + group: prometheus + mode: "0640" + when: client_metrique_tls_actif | bool + notify: Redemarrer node_exporter + +- name: TLS — verifier la presence du certificat synchronise + ansible.builtin.stat: + path: "{{ client_metrique_tls_dir }}/{{ item }}" + register: client_metrique_tls_stat + loop: + - node.crt + - node.key + when: + - client_metrique_tls_actif | bool + - not ansible_check_mode + +- name: TLS — garde-fou, ne pas activer sans certificat en place + ansible.builtin.assert: + that: + - item.stat.exists + fail_msg: "Cert node_exporter absent ({{ item.item }}) — TLS non active." + loop: "{{ client_metrique_tls_stat.results | default([]) }}" + loop_control: + label: "{{ item.item }}" + when: + - client_metrique_tls_actif | bool + - not ansible_check_mode + - name: Definir les arguments de node_exporter (adresse d'ecoute) ansible.builtin.template: src: default-node-exporter.j2 diff --git a/roles/client_metrique/templates/default-node-exporter.j2 b/roles/client_metrique/templates/default-node-exporter.j2 index ac67158..137384b 100644 --- a/roles/client_metrique/templates/default-node-exporter.j2 +++ b/roles/client_metrique/templates/default-node-exporter.j2 @@ -1,2 +1,2 @@ # Gere par Set-OPS (role client_metrique). -ARGS="--web.listen-address={{ client_metrique_adresse_ecoute }}" +ARGS="--web.listen-address={{ client_metrique_adresse_ecoute }}{% if client_metrique_tls_actif | default(false) %} --web.config.file={{ client_metrique_web_config }}{% endif %}" diff --git a/roles/client_metrique/templates/setops-node-exporter-cert-sync.path.j2 b/roles/client_metrique/templates/setops-node-exporter-cert-sync.path.j2 new file mode 100644 index 0000000..f4e1ffb --- /dev/null +++ b/roles/client_metrique/templates/setops-node-exporter-cert-sync.path.j2 @@ -0,0 +1,10 @@ +# Gere par Set-OPS (role client_metrique). Ne pas editer a la main. +[Unit] +Description=Set-OPS — surveille le certificat step_ca de node_exporter + +[Path] +PathChanged={{ client_metrique_tls_source_cert }} +Unit=setops-node-exporter-cert-sync.service + +[Install] +WantedBy=multi-user.target diff --git a/roles/client_metrique/templates/setops-node-exporter-cert-sync.service.j2 b/roles/client_metrique/templates/setops-node-exporter-cert-sync.service.j2 new file mode 100644 index 0000000..728d58d --- /dev/null +++ b/roles/client_metrique/templates/setops-node-exporter-cert-sync.service.j2 @@ -0,0 +1,7 @@ +# Gere par Set-OPS (role client_metrique). Ne pas editer a la main. +[Unit] +Description=Set-OPS — synchronise le certificat step_ca vers node_exporter + +[Service] +Type=oneshot +ExecStart=/usr/local/sbin/setops-node-exporter-cert-sync diff --git a/roles/client_metrique/templates/setops-node-exporter-cert-sync.sh.j2 b/roles/client_metrique/templates/setops-node-exporter-cert-sync.sh.j2 new file mode 100644 index 0000000..8c2cec6 --- /dev/null +++ b/roles/client_metrique/templates/setops-node-exporter-cert-sync.sh.j2 @@ -0,0 +1,18 @@ +#!/bin/sh +# Gere par Set-OPS (role client_metrique). Ne pas editer a la main. +# Synchronise le cert step_ca (client_pki, root:root) vers un emplacement lisible +# par 'prometheus' (l'user de node_exporter). Cle en 0600. Idempotent ; appele au +# deploiement ET au renouvellement (unite .path). +set -eu + +SRC_CERT="{{ client_metrique_tls_source_cert }}" +SRC_KEY="{{ client_metrique_tls_source_cle }}" +DST="{{ client_metrique_tls_dir }}" + +[ -f "$SRC_CERT" ] && [ -f "$SRC_KEY" ] || exit 0 + +install -o prometheus -g prometheus -m 0644 "$SRC_CERT" "$DST/node.crt" +install -o prometheus -g prometheus -m 0600 "$SRC_KEY" "$DST/node.key" + +# node_exporter (exporter-toolkit) relit le cert a chaud ; un reload suffit. +systemctl try-reload-or-restart {{ client_metrique_service }} 2>/dev/null || true diff --git a/roles/client_metrique/templates/web-config.yml.j2 b/roles/client_metrique/templates/web-config.yml.j2 new file mode 100644 index 0000000..f8cd0fe --- /dev/null +++ b/roles/client_metrique/templates/web-config.yml.j2 @@ -0,0 +1,5 @@ +# Gere par Set-OPS (role client_metrique). TLS pour node_exporter (exporter-toolkit). +# Le cert/cle step_ca sont synchronises par setops-node-exporter-cert-sync (owned prometheus). +tls_server_config: + cert_file: {{ client_metrique_tls_dir }}/node.crt + key_file: {{ client_metrique_tls_dir }}/node.key diff --git a/roles/serveur_prometheus/defaults/main.yml b/roles/serveur_prometheus/defaults/main.yml index eabc9fa..78a1b84 100644 --- a/roles/serveur_prometheus/defaults/main.yml +++ b/roles/serveur_prometheus/defaults/main.yml @@ -14,3 +14,7 @@ serveur_prometheus_port_node: 9100 # Jobs additionnels libres : [{ job: nom, cibles: ["hote:port", ...] }] serveur_prometheus_cibles_supplementaires: [] + +# TLS vers node_exporter (zero-confiance) : scrape en https + verif contre root_ca. +serveur_prometheus_metriques_tls: false +serveur_prometheus_metriques_ca: "/etc/step/certs/root_ca.crt" diff --git a/roles/serveur_prometheus/templates/prometheus.yml.j2 b/roles/serveur_prometheus/templates/prometheus.yml.j2 index 8b81732..264c88a 100644 --- a/roles/serveur_prometheus/templates/prometheus.yml.j2 +++ b/roles/serveur_prometheus/templates/prometheus.yml.j2 @@ -8,6 +8,11 @@ scrape_configs: - targets: ['localhost:9090'] - job_name: node +{% if serveur_prometheus_metriques_tls | default(false) %} + scheme: https + tls_config: + ca_file: {{ serveur_prometheus_metriques_ca }} +{% endif %} static_configs: - targets: {{ groups.get(serveur_prometheus_groupe_metriques, []) | map('extract', hostvars)