diff --git a/docker-compose.yml b/docker-compose.yml index d273c17..eedd1cb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -263,14 +263,18 @@ services: init: true environment: - # Credentials BDD pour mysqldump (lecture seule via USER applicatif, - # PAS le root password). Le user applicatif doit avoir SELECT + - # LOCK TABLES + SHOW VIEW sur la BDD (migrations P2). + # Credentials BDD pour mysqldump et les purges. Le user applicatif est en + # moindre privilege (DML + SELECT/SHOW VIEW/TRIGGER/LOCK TABLES, jamais le + # root password ; cf. db/init/10-scope-app-user.sh). DB_HOST: ${DB_HOST} DB_PORT: ${DB_PORT} DB_NAME: ${DB_NAME} DB_USER: ${DB_USER} DB_PASSWORD: ${DB_PASSWORD} + # Retention des donnees (mlt.md 13.4/13.5). Defaut applique par les scripts + # ET ici, pour rester coherent si la var manque du .env. + AUDIT_LOG_RETENTION_DAYS: ${AUDIT_LOG_RETENTION_DAYS:-365} + THROTTLE_PURGE_AFTER_HOURS: ${THROTTLE_PURGE_AFTER_HOURS:-24} TZ: ${CRON_TIMEZONE:-Europe/Paris} volumes: diff --git a/docker/cron/crontab b/docker/cron/crontab index 1a20d5e..afd27c8 100644 --- a/docker/cron/crontab +++ b/docker/cron/crontab @@ -16,6 +16,12 @@ # 03h00 : dump BDD complet, compresse et rotate (garde 14 derniers). 0 3 * * * /scripts/backup-db.sh 2>&1 +# 04h15 : purge de retention du journal d'audit (mlt.md 13.4, AUDIT_LOG_RETENTION_DAYS). +15 4 * * * /scripts/purge-audit-log.sh 2>&1 + +# 04h45 : purge des compteurs de throttle sans verrou actif (mlt.md 13.5, THROTTLE_PURGE_AFTER_HOURS). +45 4 * * * /scripts/purge-throttle.sh 2>&1 + # Toutes les 15 min pendant la fenetre de maintenance : purge des sessions # PHP expirees cote BDD (pas les sessions systeme qui sont en /tmp du conteneur # wakdo-app, donc ephemeres par nature). A activer quand la table sessions diff --git a/docker/cron/scripts/purge-audit-log.sh b/docker/cron/scripts/purge-audit-log.sh new file mode 100755 index 0000000..fc475cc --- /dev/null +++ b/docker/cron/scripts/purge-audit-log.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# +# Wakdo - purge de retention du journal d'audit (mlt.md 13.4). +# +# Supprime les lignes audit_log plus anciennes que AUDIT_LOG_RETENTION_DAYS +# (interet legitime / tracabilite fiscale, configurable). L'imputabilite recente +# est preservee. C'est l'unique exception documentee a l'append-only de audit_log +# (RG-T14) : une purge de retention planifiee, jamais une mutation applicative. +# +# Variables d'env (injectees par docker-compose depuis .env) : +# DB_HOST DB_PORT DB_NAME DB_USER DB_PASSWORD +# AUDIT_LOG_RETENTION_DAYS (defaut 365) +# +# Exit codes : 0 OK | 1 env manquant/invalide | 2 requete SQL echouee +set -euo pipefail + +log() { echo "[purge-audit-log $(date -Iseconds)] $*" >&2; } + +for var in DB_HOST DB_PORT DB_NAME DB_USER DB_PASSWORD; do + if [ -z "${!var:-}" ]; then log "ERROR: variable $var vide ou non definie"; exit 1; fi +done + +DAYS="${AUDIT_LOG_RETENTION_DAYS:-365}" +case "$DAYS" in + ''|*[!0-9]*) log "ERROR: AUDIT_LOG_RETENTION_DAYS non entier ('$DAYS')"; exit 1 ;; +esac + +if ! n="$(mariadb --host="$DB_HOST" --port="$DB_PORT" --user="$DB_USER" --password="$DB_PASSWORD" \ + --default-character-set=utf8mb4 -N -B "$DB_NAME" \ + -e "DELETE FROM audit_log WHERE created_at < NOW() - INTERVAL ${DAYS} DAY; SELECT ROW_COUNT();")"; then + log "ERROR: purge audit_log a echoue" + exit 2 +fi +log "audit_log: ${n} ligne(s) purgee(s) (> ${DAYS} jours)" diff --git a/docker/cron/scripts/purge-throttle.sh b/docker/cron/scripts/purge-throttle.sh new file mode 100755 index 0000000..a94f567 --- /dev/null +++ b/docker/cron/scripts/purge-throttle.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# +# Wakdo - purge des compteurs de throttle sans verrou actif (mlt.md 13.5). +# +# Borne la croissance de login_throttle (per-IP, RG-8) et pin_throttle +# (per-acteur, RG-T22) : supprime les lignes dont le verrou n'est plus actif +# ET dont la derniere tentative est plus ancienne que THROTTLE_PURGE_AFTER_HOURS. +# Les lignes servant encore un verrou actif sont conservees. +# +# Variables d'env (injectees par docker-compose depuis .env) : +# DB_HOST DB_PORT DB_NAME DB_USER DB_PASSWORD +# THROTTLE_PURGE_AFTER_HOURS (defaut 24) +# +# Exit codes : 0 OK | 1 env manquant/invalide | 2 requete SQL echouee +set -euo pipefail + +log() { echo "[purge-throttle $(date -Iseconds)] $*" >&2; } + +for var in DB_HOST DB_PORT DB_NAME DB_USER DB_PASSWORD; do + if [ -z "${!var:-}" ]; then log "ERROR: variable $var vide ou non definie"; exit 1; fi +done + +HOURS="${THROTTLE_PURGE_AFTER_HOURS:-24}" +case "$HOURS" in + ''|*[!0-9]*) log "ERROR: THROTTLE_PURGE_AFTER_HOURS non entier ('$HOURS')"; exit 1 ;; +esac + +db() { + mariadb --host="$DB_HOST" --port="$DB_PORT" --user="$DB_USER" --password="$DB_PASSWORD" \ + --default-character-set=utf8mb4 -N -B "$DB_NAME" -e "$1" +} + +# login_throttle et pin_throttle partagent le meme predicat (mlt.md 13.5). +for table in login_throttle pin_throttle; do + if ! n="$(db "DELETE FROM ${table} WHERE (lockout_until IS NULL OR lockout_until < NOW()) AND last_attempt_at < NOW() - INTERVAL ${HOURS} HOUR; SELECT ROW_COUNT();")"; then + log "ERROR: purge ${table} a echoue" + exit 2 + fi + log "${table}: ${n} ligne(s) purgee(s) (sans verrou actif, > ${HOURS}h)" +done