Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BAUeCFpDkRD6tU5wGsc1C
6.0 KiB
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
SecretKubernetes.
3. Configuration applicative
Déjà en place dans infra/prod/k8s/base/02-configmap-backend.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) :
AWS_ACCESS_KEY_ID: "<clé xpeditis-app>"
AWS_SECRET_ACCESS_KEY: "<secret xpeditis-app>"
Et WAL-G dans .env.data sur db-01 (04) :
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 :
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é ?
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 ?
# 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).
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