Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BAUeCFpDkRD6tU5wGsc1C
174 lines
6.0 KiB
Markdown
174 lines
6.0 KiB
Markdown
# 07 — Stockage objet (Hetzner Object Storage)
|
|
|
|
**Durée : environ 45 min.**
|
|
|
|
Hetzner Object Storage remplace MinIO en production. C'est un service
|
|
compatible S3 : **aucune ligne de code applicatif ne change**, seules les
|
|
variables `AWS_S3_ENDPOINT`, `AWS_REGION` et les clés diffèrent.
|
|
|
|
| | MinIO auto-hébergé | Hetzner Object Storage |
|
|
|---|---|---|
|
|
| Coût | « gratuit » + le disque + votre temps | ~6 €/To/mois |
|
|
| À sauvegarder | oui, un volume de plus | non, réplication incluse |
|
|
| CVE à suivre | oui | non |
|
|
| Console à protéger | oui | non |
|
|
| Perte si app-01 brûle | **totale** | aucune |
|
|
|
|
Le dernier point suffit à trancher : les connaissements et confirmations de
|
|
réservation sont des documents contractuels.
|
|
|
|
---
|
|
|
|
## 1. Créer les buckets
|
|
|
|
Console Hetzner → projet `xpeditis-prod` → **Object Storage** → région `fsn1`.
|
|
|
|
Créez **deux** buckets :
|
|
|
|
| Bucket | Contenu | Visibilité |
|
|
|---|---|---|
|
|
| `xpeditis-prod-documents` | PDF, connaissements, imports CSV | **Privé** |
|
|
| `xpeditis-prod-pgbackup` | Sauvegardes WAL-G | **Privé** |
|
|
|
|
> **Deux buckets, deux jeux de clés.** Si les clés applicatives fuitent (fuite
|
|
> de secret, faille d'injection), l'attaquant atteint les documents mais **pas
|
|
> les sauvegardes**. C'est précisément ce qui permet de se relever d'un
|
|
> rançongiciel. Ne mutualisez pas.
|
|
|
|
Vérifiez que les deux sont bien en **Private**. Un bucket public exposerait des
|
|
documents commerciaux nominatifs à toute personne devinant une URL.
|
|
|
|
---
|
|
|
|
## 2. Créer les clés d'accès
|
|
|
|
**Object Storage → Credentials → Generate credentials**, deux fois :
|
|
|
|
| Nom | Bucket | Destination |
|
|
|---|---|---|
|
|
| `xpeditis-app` | `xpeditis-prod-documents` | Secret Kubernetes `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` |
|
|
| `xpeditis-walg` | `xpeditis-prod-pgbackup` | `.env.data` de db-01, `WALG_ACCESS_KEY_ID` / `WALG_SECRET_ACCESS_KEY` |
|
|
|
|
Le secret n'est affiché **qu'une fois**. Copiez-le immédiatement.
|
|
|
|
> Hetzner Object Storage ne propose pas encore de politique par bucket aussi
|
|
> fine qu'AWS IAM. La séparation repose donc sur l'usage de deux jeux de clés
|
|
> distincts, chacun n'étant présent qu'à un seul endroit. Ne copiez jamais les
|
|
> clés WAL-G dans un `Secret` Kubernetes.
|
|
|
|
---
|
|
|
|
## 3. Configuration applicative
|
|
|
|
Déjà en place dans `infra/prod/k8s/base/02-configmap-backend.yaml` :
|
|
|
|
```yaml
|
|
AWS_REGION: "fsn1"
|
|
AWS_S3_ENDPOINT: "https://fsn1.your-objectstorage.com"
|
|
AWS_S3_BUCKET: "xpeditis-prod-documents"
|
|
```
|
|
|
|
Les clés vont dans le `Secret` ([06](./06-secrets-sops.md)) :
|
|
```yaml
|
|
AWS_ACCESS_KEY_ID: "<clé xpeditis-app>"
|
|
AWS_SECRET_ACCESS_KEY: "<secret xpeditis-app>"
|
|
```
|
|
|
|
Et WAL-G dans `.env.data` sur db-01 ([04](./04-noeud-donnees.md)) :
|
|
```
|
|
WALG_S3_PREFIX=s3://xpeditis-prod-pgbackup
|
|
WALG_S3_ENDPOINT=https://fsn1.your-objectstorage.com
|
|
WALG_S3_REGION=fsn1
|
|
WALG_ACCESS_KEY_ID=<clé xpeditis-walg>
|
|
WALG_SECRET_ACCESS_KEY=<secret xpeditis-walg>
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Vérifier
|
|
|
|
Avec le client AWS ou `s3cmd` :
|
|
|
|
```bash
|
|
export AWS_ACCESS_KEY_ID='<clé xpeditis-app>'
|
|
export AWS_SECRET_ACCESS_KEY='<secret xpeditis-app>'
|
|
|
|
# Lister
|
|
aws --endpoint-url https://fsn1.your-objectstorage.com s3 ls s3://xpeditis-prod-documents/
|
|
|
|
# Écrire puis relire
|
|
echo "test $(date -Is)" > /tmp/test.txt
|
|
aws --endpoint-url https://fsn1.your-objectstorage.com s3 cp /tmp/test.txt s3://xpeditis-prod-documents/
|
|
aws --endpoint-url https://fsn1.your-objectstorage.com s3 cp s3://xpeditis-prod-documents/test.txt -
|
|
aws --endpoint-url https://fsn1.your-objectstorage.com s3 rm s3://xpeditis-prod-documents/test.txt
|
|
```
|
|
|
|
### Le bucket est-il vraiment privé ?
|
|
|
|
```bash
|
|
curl -sI https://fsn1.your-objectstorage.com/xpeditis-prod-documents/test.txt | head -1
|
|
```
|
|
Attendu : `HTTP/1.1 403 Forbidden`. Si vous obtenez `200`, le bucket est
|
|
public : corrigez immédiatement.
|
|
|
|
### Les clés sont-elles bien cloisonnées ?
|
|
|
|
```bash
|
|
# Les clés applicatives ne doivent PAS voir le bucket de sauvegarde
|
|
aws --endpoint-url https://fsn1.your-objectstorage.com s3 ls s3://xpeditis-prod-pgbackup/
|
|
```
|
|
Un refus est le résultat attendu. S'il réussit, vous avez utilisé les mêmes
|
|
clés pour les deux : régénérez-en un jeu séparé.
|
|
|
|
---
|
|
|
|
## 5. Durée de conservation
|
|
|
|
Une règle de cycle de vie évite que les documents s'accumulent indéfiniment —
|
|
utile pour la facture comme pour le RGPD ([16](./16-rgpd-conformite.md)).
|
|
|
|
Attention : les documents contractuels maritimes ont des obligations de
|
|
conservation longues (généralement 10 ans en France pour les pièces
|
|
comptables). **Ne posez pas de règle de suppression automatique sur
|
|
`xpeditis-prod-documents`** sans validation juridique.
|
|
|
|
Sur `xpeditis-prod-pgbackup`, en revanche, la purge est gérée par WAL-G
|
|
(`wal-g delete retain FULL 7`, dans `pg-backup.sh`). N'ajoutez pas de règle de
|
|
cycle de vie côté bucket : les deux mécanismes se contrediraient et vous
|
|
risqueriez de supprimer des WAL encore nécessaires à une restauration.
|
|
|
|
---
|
|
|
|
## 6. Et le CDN ?
|
|
|
|
Le fichier de prévisions retient Bunny.net (0,005 €/Go, ~1 €/mois en phase 1)
|
|
comme CDN si Hetzner est choisi.
|
|
|
|
**Ce n'est pas nécessaire au lancement.** Cloudflare met déjà en cache les
|
|
ressources statiques de Next.js (`/_next/static/*`) gratuitement, et le trafic
|
|
de la phase 1 (10 Go/mois selon les hypothèses) ne justifie pas un service
|
|
supplémentaire.
|
|
|
|
Reconsidérez la question quand :
|
|
- le trafic sortant dépasse 500 Go/mois ; ou
|
|
- des utilisateurs hors d'Europe se plaignent de la latence ; ou
|
|
- vous servez des documents volumineux directement depuis Object Storage.
|
|
|
|
---
|
|
|
|
## 7. Contrôle
|
|
|
|
```
|
|
[ ] Bucket xpeditis-prod-documents créé, PRIVÉ
|
|
[ ] Bucket xpeditis-prod-pgbackup créé, PRIVÉ
|
|
[ ] Deux jeux de clés DISTINCTS générés
|
|
[ ] Écriture / lecture / suppression testées sur le bucket documents
|
|
[ ] Accès HTTP anonyme : 403
|
|
[ ] Les clés applicatives ne peuvent PAS lire le bucket de sauvegarde
|
|
[ ] Clés applicatives dans le Secret Kubernetes
|
|
[ ] Clés WAL-G uniquement dans .env.data de db-01
|
|
[ ] Aucune règle de cycle de vie sur le bucket de sauvegarde
|
|
```
|
|
|
|
→ **Suite : [08 — DNS, TLS, Cloudflare](./08-dns-tls-cloudflare.md)**
|