> ## Documentation Index
> Fetch the complete documentation index at: https://docs.snakysec.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 04 object lock procedures

# Object Lock — runbook anti-ransomware sur les buckets DR

## Quoi et pourquoi

Les buckets `mssp-backup-ovh` (OVH Gravelines) et `mssp-backup-scw` (Scaleway Paris) qui hébergent les backups Postgres + Vault + artifacts sont configurés en **Object Lock COMPLIANCE mode, rétention 90 jours** (cf. `scripts/dr/setup/bootstrap-buckets.sh`).

Conséquence concrète :

> Un attaquant qui obtient les credentials DR Vault ou les access keys S3 OVH/Scaleway **ne peut pas** supprimer ou overwriter les backups écrits dans les 90 derniers jours. Pas même le détenteur du compte root du provider. Pas de bypass.

C'est la dernière ligne de défense contre un ransomware qui aurait passé tous les autres remparts (compromise de Vault, exfiltration des creds, accès SSH).

## Modes Object Lock

| Mode           | Quoi                                                                                                              | Choix SnakySec                             |
| -------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------ |
| **COMPLIANCE** | Personne ne peut désactiver/raccourcir le lock pendant la rétention. Attendre l'expiration ou détruire le bucket. | ✅ **Activé**                               |
| GOVERNANCE     | Un user avec le permission spécifique `s3:BypassGovernanceRetention` peut overrider.                              | ❌ Trop laxiste pour un cas anti-ransomware |
| OFF            | Pas de lock.                                                                                                      | ❌ Risque ransomware non couvert            |

## Implications opérationnelles

### Le storage va grossir au-delà des cibles théoriques

`pgbackrest.conf` configure `repo-retention-full=4` (= \~30 jours d'historique) ; restic `--keep-daily 14 --keep-weekly 8 --keep-monthly 3` (= \~90 jours).

Avec Object Lock 90j actif :

* pgbackrest tentera de DELETE les fulls > 4 → **DELETE bloqué**, fichiers non supprimés
* restic `forget --prune` tentera de purger les snapshots forgottens → **DELETE bloqué**

Le storage va donc **augmenter au-dessus du steady-state théorique** pendant \~90 jours, jusqu'à ce que les premières ondes de locks expirent. Après J+90 c'est en flux tendu : ce qui s'efface en J = ce qui est libéré du lock en J-90.

**Estimation steady-state** (ordre de grandeur, à ajuster sur ton volume réel) :

* Postgres : \~200 MB par full × 12 fulls (90j) + \~50 MB/jour de WAL × 90j = **6.7 GB** par bucket
* Vault : \~2 MB par snapshot × 90 = **180 MB** par bucket
* Artifacts : croît avec le nombre de clients, \~100 MB/mois × 3 mois = **300 MB** par bucket

Total \~7 GB par bucket × 2 buckets = **\~15 GB cumulés en steady-state**. Largement dans le free tier OVH/Scaleway (5 TB inclus chez OVH HSP, 75 GB free chez Scaleway Multi-AZ).

### Les warnings DELETE dans les logs sont normaux

```
WARN: [pgbackrest] expire failed for archive 16/000000010000... — Access Denied
WARN: [restic] forget could not delete pack 7a4f9d... — locked
```

→ **Ne pas remédier**. C'est le comportement voulu. Les scripts `postgres-pgbackrest.sh`, `vault-snapshot.sh`, `artifacts-restic.sh` tagguent ces warnings comme informatifs.

Tu peux confirmer la santé via la métrique storage utilisée dans la console provider — si elle plafonne \~J+90 c'est que les locks expirent comme prévu.

## Cas d'urgence : besoin réel de purger un objet

**Réponse courte** : tu ne peux pas, c'est le point.

**Si vraiment vital** (genre fuite légale RGPD d'un objet contenant des données personnelles client qui aurait dû être anonymisé) :

1. **Attendre l'expiration naturelle du lock** (max 90 jours)
2. **OU recréer le bucket** : créer un nouveau bucket sans Object Lock (ou avec rétention plus courte), drainer ce qu'on veut garder, supprimer l'ancien bucket. Côté OVH/Scaleway il faut généralement contacter le support pour forcer la suppression d'un bucket Object-Locked. Procédure semaine + paperasse.

**Pas d'autre voie**. C'est la promesse du compliance mode et c'est ce qu'on veut.

## Migration d'un bucket legacy (sans Object Lock)

Si `bootstrap-buckets.sh` détecte un bucket existant SANS Object Lock, il refuse l'opération avec un message clair. La migration manuelle :

```bash theme={null}
# 1. Créer un nouveau bucket "mssp-backup-ovh-v2" avec Object Lock
docker exec mssp-dr-runner aws --endpoint-url=https://s3.gra.io.cloud.ovh.net \
  s3api create-bucket \
  --bucket mssp-backup-ovh-v2 \
  --object-lock-enabled-for-bucket

# 2. Configurer Versioning + Object Lock retention 90j compliance
# (commandes dans bootstrap-buckets.sh)

# 3. Drainer les backups de l'ancien bucket vers le nouveau
docker exec mssp-dr-runner rclone --config=/dr-runtime/rclone-ovh.conf \
  copy ovh:mssp-backup-ovh ovh:mssp-backup-ovh-v2 \
  --progress --transfers 4

# 4. Mettre à jour les configs qui pointent sur l'ancien bucket :
#    • platform/docker/postgres/pgbackrest.conf (repo1-s3-bucket)
#    • platform/compose/dr.yml (env vars OVH_REPO_URL, OVH_ARTIFACTS_REPO, etc)
# 5. Renommer ou supprimer l'ancien bucket via le support OVH

# 6. Vérifier que les backups continuent de tourner sur le nouveau bucket
docker exec mssp-cron ofelia exec --config=/etc/ofelia/config.ini --job=vault-snapshot
docker exec mssp-dr-runner rclone --config=/dr-runtime/rclone-ovh.conf \
  ls ovh:mssp-backup-ovh-v2/vault | tail -3
```

## Vérifier que le lock est actif (sanity check post-bootstrap)

```bash theme={null}
# OVH
docker exec mssp-dr-runner aws --endpoint-url=https://s3.gra.io.cloud.ovh.net \
  s3api get-object-lock-configuration --bucket mssp-backup-ovh

# Sortie attendue :
# {
#   "ObjectLockConfiguration": {
#     "ObjectLockEnabled": "Enabled",
#     "Rule": {
#       "DefaultRetention": {
#         "Mode": "COMPLIANCE",
#         "Days": 90
#       }
#     }
#   }
# }

# Tester la non-suppression
docker exec mssp-dr-runner sh -c '
  echo "test-immutable" > /tmp/test-lock.txt
  aws --endpoint-url=https://s3.gra.io.cloud.ovh.net \
    s3 cp /tmp/test-lock.txt s3://mssp-backup-ovh/test-lock.txt
  aws --endpoint-url=https://s3.gra.io.cloud.ovh.net \
    s3 rm s3://mssp-backup-ovh/test-lock.txt
'
# La suppression doit échouer avec "AccessDenied: object is in compliance mode"
```

## Audit régulier

À ajouter au script `scripts/dr/verify/dr-verify.sh` ou comme job Ofelia distinct (cf. plan Volet 3) :

* Hebdomadaire : appeler `get-object-lock-configuration` sur les 2 buckets, vérifier que `Mode=COMPLIANCE` et `Days=90`. Si l'un des deux a divergé (downgrade vers GOVERNANCE ou OFF), Sentry alert tag `dr.object-lock.drift`.

## Décisions historiques

| Date       | Décision                                                                                        | Raison                                                       |
| ---------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| 2026-04-XX | Object Lock listé comme "V2 manual follow-up" dans `bootstrap-buckets.sh`                       | Setup initial DR, focus first on backup pipeline correctness |
| 2026-05-02 | Object Lock COMPLIANCE 90d activé via `bootstrap-buckets.sh` (Volet 1 du plan backup hardening) | Anti-ransomware, dernière ligne de défense                   |
