Le runbook promettait cette rotation ; le code ne savait pas la faire. Les trois roles ne posaient le mot de passe qu'a la CREATION — regenerer la voute aurait produit le mensonge silencieux corrige toute la journee. Chaque role sait desormais CHANGER un mot de passe existant, idempotent par empreinte du secret applique. Pour Nextcloud le secret passe par l'environnement, pas par la ligne de commande ou il serait visible dans la table des processus. `grafana-cli` ecrivait dans une base FANTOME en annoncant « changed successfully » a chaque fois : il prend `paths.data` a `<homepath>/data`, le paquet Debian range la base dans /var/lib/grafana. Demasque par le champ `updated` du compte, reste a l'heure du deploiement initial malgre quatre reinitialisations « reussies ». Un message de succes n'est pas une preuve ; l'etat l'est. Verifie par authentification reelle : Grafana 200, Nextcloud 200. Forgejo ferme l'API par doctrine (D-41) — seule preuve disponible : le retour de la commande. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
119 lines
4.8 KiB
YAML
119 lines
4.8 KiB
YAML
---
|
|
- name: Configuration Nextcloud (occ, en www-data avec TLS PG)
|
|
become: true
|
|
become_user: "{{ serveur_nextcloud_utilisateur }}"
|
|
environment:
|
|
PGSSLMODE: "{{ serveur_nextcloud_pg_sslmode }}"
|
|
PGSSLROOTCERT: "{{ serveur_nextcloud_ca_racine }}"
|
|
when: not ansible_check_mode
|
|
block:
|
|
- name: Reglages systeme (overwrite, redis, anti-SSRF)
|
|
ansible.builtin.command:
|
|
argv:
|
|
- php
|
|
- "{{ serveur_nextcloud_racine }}/occ"
|
|
- config:system:set
|
|
- "{{ item.k }}"
|
|
- "--value={{ item.v }}"
|
|
- "{{ item.t | default('--type=string') }}"
|
|
changed_when: false
|
|
loop:
|
|
- { k: "overwrite.cli.url", v: "https://{{ serveur_nextcloud_hostname }}" }
|
|
- { k: "overwritehost", v: "{{ serveur_nextcloud_hostname }}" }
|
|
- { k: "overwriteprotocol", v: "https" }
|
|
# Gotcha : Nextcloud REFUSE les IP privees (SSRF) => OIDC injoignable sans ceci.
|
|
- { k: "allow_local_remote_servers", v: "true", t: "--type=boolean" }
|
|
- { k: "memcache.local", v: "\\OC\\Memcache\\Redis" }
|
|
- { k: "memcache.locking", v: "\\OC\\Memcache\\Redis" }
|
|
|
|
- name: Redis (hote + port)
|
|
ansible.builtin.command:
|
|
argv:
|
|
- php
|
|
- "{{ serveur_nextcloud_racine }}/occ"
|
|
- config:system:set
|
|
- redis
|
|
- "{{ item.k }}"
|
|
- "--value={{ item.v }}"
|
|
- "{{ item.t | default('--type=string') }}"
|
|
changed_when: false
|
|
loop:
|
|
- { k: "host", v: "{{ serveur_nextcloud_redis_host }}" }
|
|
- { k: "port", v: "{{ serveur_nextcloud_redis_port }}", t: "--type=integer" }
|
|
|
|
- name: Domaines de confiance (trusted_domains)
|
|
ansible.builtin.command:
|
|
argv: ["php", "{{ serveur_nextcloud_racine }}/occ", "config:system:set", "trusted_domains", "{{ idx }}", "--value={{ item }}"]
|
|
changed_when: false
|
|
loop: "{{ serveur_nextcloud_trusted_domains }}"
|
|
loop_control:
|
|
index_var: idx
|
|
|
|
- name: Proxy de confiance (edge) — voir la vraie IP client, ne pas throttler l'edge
|
|
ansible.builtin.command:
|
|
argv: ["php", "{{ serveur_nextcloud_racine }}/occ", "config:system:set", "trusted_proxies", "0", "--value={{ serveur_nextcloud_edge_ip }}"]
|
|
changed_when: false
|
|
when: serveur_nextcloud_edge_ip | length > 0
|
|
|
|
# Gotcha : Nextcloud a son PROPRE bundle CA (pas le systeme) => OIDC/HTTPS interne echoue sans import.
|
|
- name: Importer le certificat racine step-ca dans le bundle CA de Nextcloud
|
|
ansible.builtin.command:
|
|
argv: ["php", "{{ serveur_nextcloud_racine }}/occ", "security:certificates:import", "{{ serveur_nextcloud_ca_racine }}"]
|
|
changed_when: false
|
|
|
|
- name: Whitelister le sous-reseau d'admin (anti-force-brute, plutot que desactiver)
|
|
ansible.builtin.command:
|
|
argv:
|
|
- php
|
|
- "{{ serveur_nextcloud_racine }}/occ"
|
|
- config:system:set
|
|
- "{{ item.k }}"
|
|
- "--value={{ item.v }}"
|
|
- "{{ item.t | default('--type=string') }}"
|
|
changed_when: false
|
|
loop:
|
|
- { k: "bruteForceProtection.whitelist.0.ip", v: "{{ serveur_nextcloud_bruteforce_whitelist.split('/')[0] }}" }
|
|
- { k: "bruteForceProtection.whitelist.0.mask", v: "{{ serveur_nextcloud_bruteforce_whitelist.split('/')[1] }}", t: "--type=integer" }
|
|
when:
|
|
- serveur_nextcloud_bruteforce_whitelist | length > 0
|
|
- "'/' in serveur_nextcloud_bruteforce_whitelist"
|
|
|
|
# --- ROTATION DU COMPTE DE SECOURS -------------------------------------------
|
|
# `maintenance:install --admin-pass` ne tourne qu'a l'INSTALLATION. Changer la
|
|
# voute ne changeait rien. `user:resetpassword` lit le secret dans l'environnement
|
|
# (`--password-from-env`) : il ne passe donc pas par la ligne de commande, ou il
|
|
# serait visible dans la table des processus.
|
|
- name: Empreinte du mot de passe admin en place
|
|
ansible.builtin.slurp:
|
|
src: "{{ serveur_nextcloud_marqueur_admin }}"
|
|
register: serveur_nextcloud_marque
|
|
failed_when: false
|
|
changed_when: false
|
|
no_log: true
|
|
|
|
- name: Appliquer le mot de passe du compte de secours (rotation)
|
|
ansible.builtin.command:
|
|
argv:
|
|
- php
|
|
- "{{ serveur_nextcloud_racine }}/occ"
|
|
- "user:resetpassword"
|
|
- "--password-from-env"
|
|
- "{{ serveur_nextcloud_admin_user }}"
|
|
environment:
|
|
OC_PASS: "{{ serveur_nextcloud_admin_password }}"
|
|
become: true
|
|
become_user: "{{ serveur_nextcloud_utilisateur | default('www-data') }}"
|
|
changed_when: true
|
|
no_log: true
|
|
when: >-
|
|
(serveur_nextcloud_marque.content | default('') | b64decode | trim)
|
|
!= (serveur_nextcloud_admin_password | hash('sha256'))
|
|
|
|
- name: Retenir l'empreinte appliquee
|
|
ansible.builtin.copy:
|
|
content: "{{ serveur_nextcloud_admin_password | hash('sha256') }}\n"
|
|
dest: "{{ serveur_nextcloud_marqueur_admin }}"
|
|
owner: root
|
|
group: root
|
|
mode: "0600"
|
|
no_log: true
|