From 5aa7fcafdbd91f7016eca1470c46a4d9701f298f Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Sat, 4 Jul 2026 18:31:30 -0400 Subject: [PATCH] =?UTF-8?q?PostgreSQL=20:=20servir=20le=20cert=20step-ca?= =?UTF-8?q?=20(TLS=20v=C3=A9rifiable),=20non-bloquant?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zéro-confiance flux PG, côté serveur : cert-sync (owned postgres, motif .path comme Dovecot/Postfix) + ssl_cert/key/ca_file pointant sur le cert step-ca au lieu du snakeoil. Garde-fou : ssl non activé si le cert n'est pas en place. Reload robuste (instance postgresql@NN-main, pas le wrapper). Prouvé sur data-sql-01 : cert servi = Set-OPS Internal CA (vérif root_ca ok), keycloak/forgejo toujours 200 (TLS reste optionnel, rien de cassé). Co-Authored-By: Claude Opus 4.8 --- roles/serveur_postgresql/defaults/main.yml | 9 +++ roles/serveur_postgresql/tasks/main.yml | 73 +++++++++++++++++++ .../templates/99-setops.conf.j2 | 7 ++ .../setops-postgresql-cert-sync.path.j2 | 12 +++ .../setops-postgresql-cert-sync.service.j2 | 7 ++ .../setops-postgresql-cert-sync.sh.j2 | 25 +++++++ 6 files changed, 133 insertions(+) create mode 100644 roles/serveur_postgresql/templates/setops-postgresql-cert-sync.path.j2 create mode 100644 roles/serveur_postgresql/templates/setops-postgresql-cert-sync.service.j2 create mode 100644 roles/serveur_postgresql/templates/setops-postgresql-cert-sync.sh.j2 diff --git a/roles/serveur_postgresql/defaults/main.yml b/roles/serveur_postgresql/defaults/main.yml index f3dfded..d9f09ca 100644 --- a/roles/serveur_postgresql/defaults/main.yml +++ b/roles/serveur_postgresql/defaults/main.yml @@ -36,3 +36,12 @@ serveur_postgresql_comptes: [] serveur_postgresql_bases: [] # - nom: keycloak # proprietaire: keycloak + +# --- TLS via le certificat d'hote step_ca (client_pki requis sur le noeud) --- +# Cote serveur : PostgreSQL sert le cert step_ca (verifiable contre root_ca), au lieu +# du snakeoil auto-signe. Non-bloquant : le TLS reste optionnel tant que pg_hba ne le force pas. +serveur_postgresql_tls_actif: false +serveur_postgresql_tls_source_cert: "/etc/step/certs/{{ ansible_fqdn | default(ansible_hostname) }}.crt" +serveur_postgresql_tls_source_cle: "/etc/step/certs/{{ ansible_fqdn | default(ansible_hostname) }}.key" +serveur_postgresql_tls_source_ca: "/etc/step/certs/root_ca.crt" +serveur_postgresql_tls_dir: "/etc/postgresql/tls" diff --git a/roles/serveur_postgresql/tasks/main.yml b/roles/serveur_postgresql/tasks/main.yml index 705a83c..55ae38a 100644 --- a/roles/serveur_postgresql/tasks/main.yml +++ b/roles/serveur_postgresql/tasks/main.yml @@ -37,6 +37,79 @@ group: postgres mode: "0755" +# --- TLS : synchroniser le cert step_ca AVANT d'activer ssl (ordre critique) --- +- name: TLS — assurer le repertoire des certificats (postgres) + ansible.builtin.file: + path: "{{ serveur_postgresql_tls_dir }}" + state: directory + owner: postgres + group: postgres + mode: "0700" + when: serveur_postgresql_tls_actif | bool + +- name: TLS — deployer le script de synchronisation du certificat + ansible.builtin.template: + src: setops-postgresql-cert-sync.sh.j2 + dest: /usr/local/sbin/setops-postgresql-cert-sync + owner: root + group: root + mode: "0755" + when: serveur_postgresql_tls_actif | bool + +- name: TLS — deployer les unites de synchronisation (service + path) + ansible.builtin.template: + src: "{{ item.s }}" + dest: "{{ item.d }}" + owner: root + group: root + mode: "0644" + loop: + - { s: "setops-postgresql-cert-sync.service.j2", d: "/etc/systemd/system/setops-postgresql-cert-sync.service" } + - { s: "setops-postgresql-cert-sync.path.j2", d: "/etc/systemd/system/setops-postgresql-cert-sync.path" } + loop_control: + label: "{{ item.d | basename }}" + when: serveur_postgresql_tls_actif | bool + +- name: TLS — activer la surveillance du certificat (path) + ansible.builtin.systemd: + name: setops-postgresql-cert-sync.path + enabled: true + state: started + daemon_reload: true + when: + - serveur_postgresql_tls_actif | bool + - not ansible_check_mode + +- name: TLS — synchroniser le certificat maintenant + ansible.builtin.command: /usr/local/sbin/setops-postgresql-cert-sync + changed_when: false + when: + - serveur_postgresql_tls_actif | bool + - not ansible_check_mode + +- name: TLS — verifier la presence du certificat synchronise + ansible.builtin.stat: + path: "{{ serveur_postgresql_tls_dir }}/{{ item }}" + register: serveur_postgresql_tls_stat + loop: + - server.crt + - server.key + when: + - serveur_postgresql_tls_actif | bool + - not ansible_check_mode + +- name: TLS — garde-fou, refuser d'activer ssl sans certificat en place + ansible.builtin.assert: + that: + - item.stat.exists + fail_msg: "Certificat PG absent ({{ item.item }}) — ssl non active pour ne rien casser." + loop: "{{ serveur_postgresql_tls_stat.results | default([]) }}" + loop_control: + label: "{{ item.item }}" + when: + - serveur_postgresql_tls_actif | bool + - not ansible_check_mode + - name: Deployer la configuration Set-OPS ansible.builtin.template: src: 99-setops.conf.j2 diff --git a/roles/serveur_postgresql/templates/99-setops.conf.j2 b/roles/serveur_postgresql/templates/99-setops.conf.j2 index a3bf21d..2f38ac0 100644 --- a/roles/serveur_postgresql/templates/99-setops.conf.j2 +++ b/roles/serveur_postgresql/templates/99-setops.conf.j2 @@ -2,6 +2,13 @@ listen_addresses = '{{ serveur_postgresql_ecoute | join(",") }}' port = {{ serveur_postgresql_port }} password_encryption = '{{ serveur_postgresql_methode_auth }}' +{% if serveur_postgresql_tls_actif | default(false) %} +# TLS via le certificat d'hote step_ca (synchronise par setops-postgresql-cert-sync). +ssl = on +ssl_cert_file = '{{ serveur_postgresql_tls_dir }}/server.crt' +ssl_key_file = '{{ serveur_postgresql_tls_dir }}/server.key' +ssl_ca_file = '{{ serveur_postgresql_tls_dir }}/root.crt' +{% endif %} {% for cle, valeur in serveur_postgresql_parametres.items() %} {{ cle }} = '{{ valeur }}' {% endfor %} diff --git a/roles/serveur_postgresql/templates/setops-postgresql-cert-sync.path.j2 b/roles/serveur_postgresql/templates/setops-postgresql-cert-sync.path.j2 new file mode 100644 index 0000000..5d19837 --- /dev/null +++ b/roles/serveur_postgresql/templates/setops-postgresql-cert-sync.path.j2 @@ -0,0 +1,12 @@ +# Géré par Set-OPS (rôle serveur_postgresql). Ne pas éditer à la main. +# Surveille le certificat step_ca : au renouvellement (client_pki), resynchronise +# vers PostgreSQL et le recharge. +[Unit] +Description=Set-OPS — surveille le certificat step_ca de PostgreSQL + +[Path] +PathChanged={{ serveur_postgresql_tls_source_cert }} +Unit=setops-postgresql-cert-sync.service + +[Install] +WantedBy=multi-user.target diff --git a/roles/serveur_postgresql/templates/setops-postgresql-cert-sync.service.j2 b/roles/serveur_postgresql/templates/setops-postgresql-cert-sync.service.j2 new file mode 100644 index 0000000..2e1ac67 --- /dev/null +++ b/roles/serveur_postgresql/templates/setops-postgresql-cert-sync.service.j2 @@ -0,0 +1,7 @@ +# Géré par Set-OPS (rôle serveur_postgresql). Ne pas éditer à la main. +[Unit] +Description=Set-OPS — synchronise le certificat step_ca vers PostgreSQL + +[Service] +Type=oneshot +ExecStart=/usr/local/sbin/setops-postgresql-cert-sync diff --git a/roles/serveur_postgresql/templates/setops-postgresql-cert-sync.sh.j2 b/roles/serveur_postgresql/templates/setops-postgresql-cert-sync.sh.j2 new file mode 100644 index 0000000..0471717 --- /dev/null +++ b/roles/serveur_postgresql/templates/setops-postgresql-cert-sync.sh.j2 @@ -0,0 +1,25 @@ +#!/bin/sh +# Géré par Set-OPS (rôle serveur_postgresql). Ne pas éditer à la main. +# Synchronise le certificat step_ca (déposé par client_pki, root:root) vers un +# emplacement lisible par postgres, puis recharge. PostgreSQL lit en tant que +# 'postgres' et REFUSE une clé accessible au groupe/monde → owner postgres, 0600. +# Idempotent ; appelé au déploiement ET au renouvellement (unité .path). +set -eu + +SRC_CERT="{{ serveur_postgresql_tls_source_cert }}" +SRC_KEY="{{ serveur_postgresql_tls_source_cle }}" +SRC_CA="{{ serveur_postgresql_tls_source_ca }}" +DST="{{ serveur_postgresql_tls_dir }}" + +[ -f "$SRC_CERT" ] && [ -f "$SRC_KEY" ] || exit 0 + +install -o postgres -g postgres -m 0644 "$SRC_CERT" "$DST/server.crt" +install -o postgres -g postgres -m 0600 "$SRC_KEY" "$DST/server.key" +[ -f "$SRC_CA" ] && install -o postgres -g postgres -m 0644 "$SRC_CA" "$DST/root.crt" + +# Reload (SIGHUP) : PostgreSQL recharge ses fichiers TLS sans coupure. +# ATTENTION : 'postgresql' est un wrapper Debian (reload = no-op) ; il faut recharger +# la ou les INSTANCES de cluster (postgresql@NN-main), sinon le nouveau cert n'est pas pris. +for u in $(systemctl list-units --plain --no-legend 'postgresql@*' 2>/dev/null | awk '{print $1}'); do + systemctl reload "$u" 2>/dev/null || true +done