Construire une passerelle S3-compatible entre deux comptes AWS réellement distincts : baleine, le compte consommateur, et gros chat, le compte propriétaire du bucket. Le compte baleine utilise un endpoint S3 custom via PrivateLink, sans droit IAM sur gros chat ; gros chat conserve le bucket réel, l'accès IAM interne et la capacité de couper l'accès rapidement.
Ce POC a été réalisé avec deux comptes AWS distincts, ce qui permet de tester un vrai scénario inter-comptes : gros chat possède le bucket S3 et expose une passerelle privée ; baleine consomme cette passerelle sans jamais recevoir de droits IAM sur le bucket.
Le vrai compromis architectural
Ce POC ne cherche pas à prouver que cette approche est supérieure au modèle natif S3/IAM dans tous les cas. Il explore un pattern précis :
exposer un bucket S3 d’un compte propriétaire (gros chat) à un compte consommateur (baleine) sans donner à ce dernier de droit IAM direct sur le bucket.
Le compromis est assumé : on déplace l’autorisation d’accès depuis le data-layer natif AWS/S3/IAM vers une
passerelle réseau et applicative exposée en privé via PrivateLink. Cela renforce le contrôle côté propriétaire, mais introduit un composant intermédiaire à opérer et une perte d’attribution CloudTrail native directe à l’identité AWS du consommateur.
Le bénéfice opérationnel recherché est double : baleine reste un client S3 standard,
agnostique au modèle d’autorisation interne de gros chat, et gros chat
évite de maintenir une bucket policy ou des grants S3/IAM spécifiques par consommateur. Le contrat
exposé devient un endpoint privé S3-compatible, tandis que l’autorisation réelle reste centralisée côté propriétaire.
🐈
Compte propriétaire
gros chat héberge le bucket réel mab-documents, ECS Fargate, le NLB interne et l'Endpoint Service PrivateLink.
🐋
Compte consommateur
baleine héberge le VPC Endpoint PrivateLink et le client S3 standard utilisé pour lire les objets.
🧪
Mode POC
La construction a été faite en CLI pour comprendre chaque brique. Quand j'aurai le temps, je referai cette version proprement en Terraform ;).
Contrainte initiale
Réponse dans le POC
baleine ne doit recevoir aucun droit IAM sur gros chat
Le vrai accès S3 est porté exclusivement par le Task Role ECS Fargate côté gros chat.
baleine ne doit pas connaître les credentials AWS de gros chat
Le client reçoit uniquement des credentials applicatifs S3-compatible propres à la passerelle.
baleine ne doit jamais toucher directement S3 dans gros chat
Le trafic passe par un endpoint custom PrivateLink, puis NLB, puis Fargate/rclone.
Le client doit rester standard
La validation utilise aws s3 ls et aws s3 cp avec --endpoint-url.
Le flux doit rester privé
Le chemin réseau utilise PrivateLink entre les deux comptes, sans exposition publique de la passerelle.
Pas de composant local permanent
La passerelle tourne directement sur ECS Fargate côté gros chat, pas sur un poste local.
Ce document conserve volontairement les noms pédagogiques baleine et gros chat, ainsi que le nom du bucket mab-documents et les trois fichiers de test. Les IDs de comptes, ARNs, endpoints réels et secrets sont anonymisés ou représentés sous forme de variables.
02 / 25
Lecture rapide : qui fait quoi ?
Pour comprendre le POC sans se perdre dans les commandes, il faut d'abord séparer les responsabilités. gros chat construit et contrôle la passerelle ; baleine ne fait que la consommer comme un endpoint S3 privé.
🐈 gros chat · propriétaire et opérateur
1Possède le bucket réel mab-documents et les fichiers fifi.txt, loulou.txt, riri.txt.
2Crée le rôle IAM de la task Fargate, seul autorisé à lire le bucket S3.
3Déploie rclone serve s3 gateway: sur ECS Fargate, derrière un NLB interne.
4Expose le NLB via un Endpoint Service PrivateLink et autorise explicitement le compte baleine.
5Fournit à baleine des credentials applicatifs, puis peut couper l'accès en arrêtant ECS, en révoquant PrivateLink ou en rotant ces credentials.
🐋 baleine · consommateur uniquement
1Crée un Interface VPC Endpoint vers le service PrivateLink publié par gros chat.
2Configure son client S3 avec un --endpoint-url privé et les credentials applicatifs fournis.
3Liste et lit le bucket logique mab-documents via aws s3 ls et aws s3 cp.
4Peut copier les fichiers sur son disque local, par exemple dans ~/mab-documents.
5Ne reçoit aucun rôle IAM dans gros chat, aucun accès direct au S3 réel, aucun secret AWS de gros chat.
Action
À faire côté gros chat
À faire côté baleine
Publier le service
Fargate + NLB interne + Endpoint Service PrivateLink
Rien
Autoriser le consommateur
modify-vpc-endpoint-service-permissions vers le compte baleine
Créer l'Interface Endpoint
Lire les objets
Task Role Fargate lit le bucket réel
Client S3 lit le bucket logique via endpoint custom
Couper l'accès
Arrêt ECS, révocation PrivateLink ou rotation credentials
Subit la coupure ; ne peut pas contourner par IAM S3
03 / 25
Schéma pédagogique du flux
Ce schéma résume le POC de manière visuelle et fidèle : baleine consomme un service S3-compatible privé, mais l'accès réel au bucket reste entièrement porté par gros chat. Les CIDR et adresses IP ont été volontairement anonymisés pour garder le document partageable sans exposer de détails d'adressage inutiles.
Schéma détaillé du POC : baleine consomme la passerelle privée, gros chat conserve le bucket et l’accès IAM réel. Ce choix déplace l’autorisation effective vers la passerelle contrôlée par le propriétaire. La flèche PrivateLink représente le flux applicatif baleine → gros chat. Le S3 Gateway Endpoint reste une connexion privée interne au VPC gros chat vers S3, pas une passerelle applicative exposée à baleine.
À droite, le client baleine (EC2 de test ou application) utilise un client S3 standard, des credentials applicatifs locales et une --endpoint-url privée.
Le trafic entre alors dans un Interface Endpoint PrivateLink déployé dans le VPC baleine.
PrivateLink transporte ensuite ce flux vers le compte gros chat, sans passage par Internet.
Dans gros chat, le trafic arrive sur un NLB interne, puis est distribué au service ECS Fargate qui exécute rclone serve s3 gateway:.
La task Fargate authentifie les credentials applicatifs, puis utilise son Task Role ECS pour lire le bucket S3 réel mab-documents.
Les objets reviennent ensuite vers le client baleine par le même chemin privé.
Ce qu'il faut retenir
Le bucket reste dans gros chat : il n'est jamais déplacé ni partagé directement avec baleine.
Pas d'IAM croisé : baleine ne reçoit aucun droit AWS sur le bucket.
Pas de credential AWS gros chat côté baleine : seules les credentials applicatives de la passerelle sont utilisées par le client.
Le rôle du service Fargate est central : c'est lui qui possède l'autorisation réelle de lecture sur mab-documents.
Les endpoints internes sont indispensables : S3 Gateway Endpoint, ECR API, ECR DKR et CloudWatch Logs permettent au service Fargate de fonctionner sans NAT.
Le point subtil rclone : le remote combine expose mab-documents comme bucket logique au travers de la passerelle.
04 / 25
Identités du POC : construction vs fonctionnement réel
Le lecteur n'a pas besoin d'avoir suivi les micro-étapes pas à pas pour comprendre cette partie. Il faut simplement distinguer deux moments : la phase de construction du POC, où l'on crée l'infrastructure, et la phase de fonctionnement, où la passerelle tourne et où baleine lit les objets.
Point de clarification. Les users admin temporaires ne font pas partie de l'architecture cible. Ils ont seulement servi de « tournevis » pour créer rapidement les ressources AWS pendant le POC. Une fois le POC construit, le flux de lecture baleine → gros chat → S3 ne dépend pas de ces users admin.
Phase 1 — Construction du POC
User temporaire gros chat
Créé pour exécuter les commandes CLI qui construisent la partie propriétaire : IAM, ECR, VPC, ECS/Fargate, NLB et Endpoint Service PrivateLink.
À supprimer ou remplacer par un rôle d'administration cadré après le POC.
User temporaire baleine
Créé pour exécuter les commandes CLI qui construisent la partie consommatrice : Security Group, Interface Endpoint, instance EC2 de test et rôle SSM.
À supprimer ou remplacer par un rôle/pipeline d'infrastructure plus propre.
Phase 2 — Fonctionnement de la passerelle
Credentials applicatifs
<LOCAL_ACCESS_KEY> / <LOCAL_SECRET_KEY>, fournis à baleine pour s'authentifier auprès de la passerelle S3-compatible.
Ce ne sont pas des credentials AWS : ils ne donnent aucun droit IAM dans gros chat.
Task Role Fargate
Identité AWS attachée à la task ECS Fargate dans gros chat.
C'est la seule identité AWS qui lit réellement s3://mab-documents.
Execution Role ECS
Rôle technique utilisé par ECS/Fargate pour tirer l'image depuis ECR et écrire les logs CloudWatch.
Il ne sert pas à lire le bucket métier ; il sert à faire démarrer correctement le conteneur.
Phrase à retenir : les users admin temporaires servent à construire le décor. Au runtime, baleine s'authentifie auprès de la passerelle avec des credentials applicatifs ; la passerelle lit le bucket avec le Task Role Fargate côté gros chat.
05 / 25
Pourquoi ce POC
Le besoin est simple : permettre à une application du compte baleine de lire des objets depuis un bucket S3 détenu par gros chat, tout en évitant une délégation IAM directe vers le bucket.
Ce que le pattern remplace.
Dans une architecture AWS native, l’accès inter-comptes à S3 peut être géré par des policies IAM, des bucket policies, des rôles assumés ou des points d’accès S3. Ici, le POC explore volontairement une autre option : ne pas déléguer de droit IAM direct au compte consommateur et faire porter l’accès réel par une passerelle contrôlée par le propriétaire du bucket.
Ce que l’on gagne
le bucket reste intégralement contrôlé par gros chat ;
baleine ne reçoit aucun rôle IAM ni secret AWS du compte propriétaire ;
le consommateur utilise un client S3 standard avec un endpoint privé ;
la coupure peut être centralisée côté propriétaire : arrêt ECS, révocation PrivateLink ou rotation des credentials applicatifs.
Ce que l’on paie
une passerelle supplémentaire à déployer, superviser et durcir ;
une attribution CloudTrail S3 qui se concentre sur le rôle de la passerelle, pas sur l’identité AWS finale de baleine ;
des credentials applicatifs à gérer et à rotater ;
une disponibilité de la passerelle à traiter sérieusement avant toute production.
Le pattern testé consiste à interposer une passerelle S3-compatible dans le compte propriétaire. Le client conserve ses outils standards — AWS CLI, SDK S3, endpoint custom — mais ne parle plus directement à AWS S3. Le flux réseau passe par PrivateLink, puis par un NLB interne, puis par une task ECS Fargate qui exécute rclone serve s3.
Résultat obtenu. Depuis baleine, le client a listé et copié les fichiers fifi.txt, loulou.txt et riri.txt du bucket mab-documents sans disposer de droits IAM sur ce bucket.
06 / 25
Bucket S3 réel et fichiers de test
Le bucket réel reste dans gros chat. Il s'appelle mab-documents et contient volontairement trois petits fichiers texte, suffisants pour valider le listing et la lecture d'objets via la passerelle.
Côté baleine, ces fichiers ne sont pas visibles dans un bucket S3 local. Ils peuvent en revanche être lus via l'endpoint PrivateLink, puis copiés sur le disque local d'une instance ou d'une application cliente, par exemple dans ~/mab-documents.
07 / 25
Architecture cible
L'architecture sépare clairement l'identité du consommateur, le transport réseau privé et l'identité AWS qui accède réellement au bucket.
Vue d'ensemble anonymiséeCompte A = baleine · Compte B = gros chat
Disque local de test copie des fichiers vers l'instance cliente
→PrivateLink TCP 8080
🐈 gros chat
Endpoint Service service PrivateLink exposé
NLB interne listener TCP 8080
ECS Fargate container rclone
S3 réel bucket mab-documents
Zone
Composant
Rôle
Information conservée
baleine
Application / AWS CLI
Client S3 standard avec endpoint custom
Endpoint privé + credentials locales
baleine
Interface Endpoint
Entrée PrivateLink vers gros chat
DNS privé VPC
gros chat
NLB interne
Répartition TCP vers Fargate
Pas d'exposition Internet
gros chat
ECS Fargate
Héberge la passerelle S3-compatible
Task Role IAM
gros chat
S3
Stockage réel
mab-documents
08 / 25
Pourquoi choisir rclone pour cette passerelle
Le choix de rclone concerne uniquement l’implémentation du POC : il fournit rapidement un serveur S3-compatible léger, conteneurisable et capable de s'appuyer sur l'identité AWS de la task Fargate via env_auth = true.
Ce que rclone apporte au POC
Interface S3 côté client : baleine peut utiliser AWS CLI ou un SDK S3 avec un simple --endpoint-url.
Pas de clés AWS côté baleine : les credentials locales ne sont pas des credentials IAM AWS.
Task Role Fargate : côté gros chat, env_auth = true permet à rclone d'utiliser la chaîne d'identité AWS de la task.
Image simple : un Dockerfile minimal suffit pour lancer rclone serve s3.
Mapping maîtrisé : le remote combine permet d'exposer mab-documents comme bucket logique.
Ce que rclone ne promet pas seul
Ce n'est pas un service AWS S3 managé : il faut tester les opérations nécessaires une par une.
La compatibilité doit être validée pour ListObjectsV2, GetObject, gros fichiers, range requests et éventuellement multipart.
Les credentials locales doivent être externalisées et rotatées avant toute industrialisation.
Le mapping racine/bucket est subtil : servir directement le bucket réel ne suffisait pas pour aws s3 ls s3://mab-documents.
Décision validée par le test. Avec gateway: et le remote combine, baleine liste fifi.txt, loulou.txt et riri.txt, puis copie fifi.txt sur son disque local.
09 / 25
Glossaire pédagogique
🔒
PrivateLink
Permet d'exposer un service d'un VPC à un autre compte sans passer par Internet, via un endpoint réseau privé.
⚖️
NLB
Load balancer niveau 4, adapté au TCP brut. Ici il transmet le trafic S3-compatible vers la task Fargate.
🚢
Fargate
Exécution serverless de conteneurs ECS : pas d'instance EC2 à gérer, mais des objets logiques ECS à déclarer.
🧰
rclone serve s3
Expose un backend rclone sous forme d'API S3-compatible. C'est le cœur de la passerelle.
🧩
combine remote
Remote rclone utilisé pour faire apparaître le bucket réel comme un bucket logique côté client S3.
🪪
Task Role
Identité IAM interne à gros chat. Elle lit le bucket réel, sans jamais être donnée à baleine.
10 / 25
Concepts ECS / Fargate à comprendre avant les commandes
Une grande partie de la difficulté du POC vient du vocabulaire ECS. Fargate est bien serverless : on ne crée pas de machines EC2 à administrer. En revanche, on décrit plusieurs objets logiques pour dire à AWS quoi lancer, où le lancer, avec quels droits et comment le garder disponible.
Concept
Définition simple
Dans ce POC
Cluster ECS
Conteneur logique qui regroupe des services et des tasks. Ce n'est pas un cluster de serveurs EC2 dans notre cas.
rclone-s3-gateway-cluster
Task Definition
La recette de lancement du conteneur : image Docker, CPU, mémoire, port, logs, rôles IAM.
Elle dit de lancer l'image rclone-s3-gateway:latest sur le port 8080.
Task
Une exécution concrète d'une Task Definition. C'est le conteneur réellement démarré par Fargate.
Une task exécute rclone serve s3 gateway:.
Service ECS
Le superviseur : il garde le nombre voulu de tasks en vie et redémarre une task si elle tombe.
desired-count = 1 signifie : garder une passerelle active.
Desired / Running / Pending
État du service : combien de tasks sont demandées, combien tournent, combien sont en démarrage.
État final attendu : Desired=1, Running=1, Pending=0.
Execution Role
Rôle technique utilisé par ECS pour tirer l'image ECR et écrire les logs CloudWatch.
ecsTaskExecutionRole
Task Role
Rôle applicatif disponible dans le conteneur. C'est lui qui donne à rclone le droit de lire S3.
rclone-s3-gateway-task-role avec s3:ListBucket et s3:GetObject.
Target Group IP
Groupe de cibles du NLB. En Fargate, on cible les IP privées des tasks, pas des instances EC2.
Target Group TCP 8080, type ip.
NLB interne
Point d'entrée réseau privé côté gros chat. Il relaie le TCP vers les tasks Fargate.
Listener TCP 8080 vers le Target Group.
Endpoint Service
Service PrivateLink exposé par gros chat à d'autres comptes.
Il expose le NLB à baleine.
Interface Endpoint
Endpoint créé côté baleine pour consommer le service PrivateLink.
Il fournit un DNS privé utilisable par l'application baleine.
Lecture mentale utile : Task Definition = recette, Task = conteneur en cours d'exécution, Service = gardien qui maintient la task en vie, Fargate = moteur serverless qui exécute la task sans EC2 à gérer.
Lecture d'état du service ECS · sortie à interpréter
# Desired : nombre de tasks demandées par le service.
# Running : nombre de tasks réellement en fonctionnement.
# Pending : nombre de tasks en cours de démarrage.
# Si Desired=1, Running=1, Pending=0, la passerelle est stable.
aws --profile groschat ecs describe-services --cluster rclone-s3-gateway-cluster --services rclone-s3-gateway-service --query 'services[0].{Desired:desiredCount,Running:runningCount,Pending:pendingCount,Status:status}' --output table
11 / 25
IAM et séparation des rôles
Attribution CloudTrail : limite volontaire du pattern
Dans ce POC, le bucket S3 réel est lu par le Task Role ECS de la passerelle côté gros chat.
Les événements CloudTrail S3 côté propriétaire attribuent donc l’accès à cette identité AWS de passerelle.
Cela signifie que l’on perd l’attribution native directe par identité AWS consommatrice. Si plusieurs consommateurs utilisent la même passerelle, CloudTrail ne suffit plus à lui seul pour distinguer finement l’utilisateur ou l’application appelante côté baleine.
Pour une version industrialisée, cette perte doit être compensée par des logs applicatifs de passerelle : identifiant de client applicatif, bucket logique, clé objet, opération, timestamp, résultat, source réseau et corrélation avec les logs NLB/CloudWatch.
Le point central du POC est la séparation entre l'authentification côté client et l'autorisation AWS réelle.
POC vs production. Des users administrateurs temporaires ont pu être utilisés pour accélérer la construction. En production, il faut remplacer ces identités par des rôles/policies minimales et supprimer toutes les clés longues durées non nécessaires.
12 / 25
Transmission des credentials à baleine
Les credentials transmis à baleine ne sont pas des credentials AWS. Ce sont des credentials applicatifs S3-compatible propres à la passerelle rclone serve s3.
Ce que gros chat transmet
Une access_key applicative.
Une secret_key applicative.
L'endpoint PrivateLink à utiliser côté baleine.
Le nom du bucket logique exposé : mab-documents.
Ce qu'il ne faut pas transmettre
Aucune clé AWS du compte gros chat.
Aucun rôle IAM ou droit direct sur le bucket réel.
Aucun secret en clair dans un mail, un dépôt Git ou une image Docker industrialisée.
Pour un POC, la paire local-access-key / local-secret-key a été utilisée pour aller vite. En environnement réel, gros chat devrait déposer ces informations dans un coffre-fort de secrets ou les transmettre par un canal sécurisé équivalent. Le mail peut annoncer l'existence de l'accès et pointer vers l'entrée du coffre-fort, mais il ne doit pas contenir le secret en clair.
La logique de sécurité est simple : baleine s'authentifie auprès de la passerelle ; la passerelle, elle, lit S3 avec le Task Role Fargate dans gros chat.
13 / 25
Configuration rclone
Le point subtil du POC : rclone serve s3 considère les répertoires à la racine du remote comme des buckets. Servir directement mabaws:mab-documents expose les fichiers à la racine et ne donne pas le comportement attendu avec aws s3 ls s3://mab-documents.
La correction consiste à créer un remote combine appelé gateway, qui mappe explicitement mab-documents vers le bucket réel.
rclone.conf
[mabaws]
type = s3
provider = AWS
env_auth = true
region = eu-west-3
location_constraint = eu-west-3
[gateway]
type = combine
upstreams = mab-documents=mabaws:mab-documents
La racine du remote S3 ne donnait pas le bucket logique attendu.
2
rclone serve s3 mabaws:mab-documents
Listing racine vide, fichiers non exposés comme bucket.
Les fichiers à la racine ne sont pas vus comme un bucket par le serveur S3 rclone.
3
rclone serve s3 gateway:
Listing et lecture OK depuis baleine.
Le remote combine expose mab-documents comme bucket logique.
Configuration validée. Les logs Fargate ont confirmé le démarrage avec rclone serve s3 gateway: puis la création du backend mabaws:mab-documents.
14 / 25
Image Docker rclone : ce qui est vraiment déployé
La configuration rclone.conf ne suffit pas seule : elle est embarquée dans une image Docker, puis cette image est poussée dans ECR côté gros chat. ECS Fargate ne lance pas directement un fichier de configuration ; il lance un conteneur construit à partir de cette image.
Cycle de vie de l'imagelocal → ECR gros chat → Fargate
🧾 1. Fichiers locaux
rclone.conf décrit les remotes mabaws et gateway. Le Dockerfile décrit comment lancer rclone serve s3.
→
🐳 2. Build Docker
On construit l'image rclone-s3-gateway:latest à partir de l'image officielle rclone/rclone:latest.
→
📦 3. Push ECR
L'image est taguée puis poussée dans le repository ECR rclone-s3-gateway du compte gros chat.
🚢 4. Task Definition
La task definition ECS référence l'image ECR : <ACCOUNT_GROSCHAT>.dkr.ecr.eu-west-3.amazonaws.com/rclone-s3-gateway:latest.
→
🔁 5. Déploiement
Après chaque rebuild/push, on force un nouveau déploiement ECS pour que Fargate tire la nouvelle image.
Point de vigilance. Tant que le tag latest est réutilisé, ECS ne sait pas magiquement qu'une nouvelle image a été poussée. Il faut forcer un nouveau déploiement, ou mieux en production : versionner l'image avec un tag immuable ou un digest sha256.
Dans le POC, les credentials applicatifs sont encore dans la commande de démarrage du conteneur pour aller vite. En version durcie, ils doivent être injectés depuis Secrets Manager, Parameter Store SecureString ou un coffre-fort interne, puis passés au démarrage de rclone.
15 / 25
Déploiement Fargate côté gros chat
Correction de reproductibilité.
Le runbook ne doit pas pointer vers rclone-s3-gateway:1 en dur. À chaque appel de register-task-definition, ECS crée une nouvelle révision. Le runbook capture donc l'ARN retourné dans TASK_DEF_ARN, puis utilise cette variable pour créer ou mettre à jour le service.
La passerelle tourne dans ECS Fargate. Le cluster ECS est un conteneur logique : aucune instance EC2 n'est gérée. La task Fargate est placée dans des subnets privés et utilise des VPC Endpoints AWS pour ECR, CloudWatch Logs et S3.
ECR
Repository rclone-s3-gateway, image Docker poussée avec le tag latest.
CloudWatch Logs
Log group /ecs/rclone-s3-gateway, stream par task ECS.
Task Definition
Mode awsvpc, compatibilité FARGATE, port container 8080.
Service ECS
Desired count à 1, relance automatique en cas d'arrêt de la task.
Target Group
Type ip, protocole TCP, health check TCP sur le port 8080.
Sens des flèches. Le flux applicatif est initié par baleine vers gros chat : client S3 → Interface Endpoint → PrivateLink → Endpoint Service → NLB → Fargate/rclone. La relation inverse existe seulement au niveau administratif : gros chat publie le service PrivateLink et autorise le compte baleine à créer son Interface Endpoint.
Côté gros chat, le NLB interne est exposé comme Endpoint Service PrivateLink. Côté baleine, un Interface VPC Endpoint consomme ce service. L'acceptation automatique a été activée pour le POC, avec une permission explicite donnée au compte baleine.
Point de vigilance pédagogique. Il y a deux familles d’endpoints à ne pas confondre : les endpoints AWS internes au VPC de gros chat pour que Fargate fonctionne sans Internet, et l’endpoint PrivateLink côté baleine qui permet au client de joindre la passerelle.
Où ?
Endpoint créé
Pourquoi ?
Type
gros chat
com.amazonaws.eu-west-3.s3
Permettre à rclone/Fargate de lire le bucket S3 réel sans NAT Gateway.
Gateway Endpoint
gros chat
com.amazonaws.eu-west-3.ecr.api
Permettre à ECS/Fargate d’appeler l’API ECR.
Interface Endpoint
gros chat
com.amazonaws.eu-west-3.ecr.dkr
Permettre à Fargate de tirer l’image Docker depuis ECR.
Interface Endpoint
gros chat
com.amazonaws.eu-west-3.logs
Permettre au conteneur d’écrire ses logs CloudWatch.
Interface Endpoint
gros chat
Endpoint Service PrivateLink
Publier le NLB interne comme service consommable par baleine.
Endpoint Service
baleine
Interface VPC Endpoint vers le service gros chat
Fournir au client baleine un DNS privé pour joindre la passerelle.
Interface Endpoint
Bloc complet · endpoints AWS internes côté gros chat
# Ces endpoints ne sont pas consommés par baleine.
# Ils servent à faire tourner Fargate dans des subnets privés, sans NAT Gateway.
# 1. Gateway Endpoint S3 : accès privé au bucket S3 réel.
aws --profile groschat ec2 create-vpc-endpoint --vpc-id <VPC_GROSCHAT> --service-name com.amazonaws.eu-west-3.s3 --vpc-endpoint-type Gateway --route-table-ids <ROUTE_TABLE_GROSCHAT> --tag-specifications 'ResourceType=vpc-endpoint,Tags=[{Key=Name,Value=poc-s3-gateway-s3-endpoint}]'
# 2. Interface Endpoint ECR API : accès privé à l’API ECR.
aws --profile groschat ec2 create-vpc-endpoint --vpc-id <VPC_GROSCHAT> --service-name com.amazonaws.eu-west-3.ecr.api --vpc-endpoint-type Interface --subnet-ids <SUBNET_GROSCHAT_A> <SUBNET_GROSCHAT_B> --security-group-ids <SG_ENDPOINTS_AWS> --private-dns-enabled --tag-specifications 'ResourceType=vpc-endpoint,Tags=[{Key=Name,Value=poc-s3-gateway-ecr-api-endpoint}]'
# 3. Interface Endpoint ECR DKR : accès privé au registry Docker ECR.
aws --profile groschat ec2 create-vpc-endpoint --vpc-id <VPC_GROSCHAT> --service-name com.amazonaws.eu-west-3.ecr.dkr --vpc-endpoint-type Interface --subnet-ids <SUBNET_GROSCHAT_A> <SUBNET_GROSCHAT_B> --security-group-ids <SG_ENDPOINTS_AWS> --private-dns-enabled --tag-specifications 'ResourceType=vpc-endpoint,Tags=[{Key=Name,Value=poc-s3-gateway-ecr-dkr-endpoint}]'
# 4. Interface Endpoint CloudWatch Logs : envoi privé des logs de la task.
aws --profile groschat ec2 create-vpc-endpoint --vpc-id <VPC_GROSCHAT> --service-name com.amazonaws.eu-west-3.logs --vpc-endpoint-type Interface --subnet-ids <SUBNET_GROSCHAT_A> <SUBNET_GROSCHAT_B> --security-group-ids <SG_ENDPOINTS_AWS> --private-dns-enabled --tag-specifications 'ResourceType=vpc-endpoint,Tags=[{Key=Name,Value=poc-s3-gateway-logs-endpoint}]'
Bloc complet · PrivateLink entre gros chat et baleine
# 5. Côté gros chat : publier le NLB interne comme Endpoint Service PrivateLink.
SERVICE_NAME=$(aws --profile groschat ec2 create-vpc-endpoint-service-configuration --network-load-balancer-arns <NLB_ARN> --no-acceptance-required --query 'ServiceConfiguration.ServiceName' --output text)
# Récupérer l’identifiant court du service, par exemple vpce-svc-xxxx.
SERVICE_ID=$(aws --profile groschat ec2 describe-vpc-endpoint-service-configurations --filters "Name=service-name,Values=$SERVICE_NAME" --query 'ServiceConfigurations[0].ServiceId' --output text)
# 6. Côté gros chat : autoriser le compte baleine à consommer ce service.
# Le suffixe :root signifie “le compte baleine”, pas l’utilisateur root.
aws --profile groschat ec2 modify-vpc-endpoint-service-permissions --service-id "$SERVICE_ID" --add-allowed-principals arn:aws:iam::<ACCOUNT_BALEINE>:root
# 7. Côté baleine : créer le Security Group de l’Interface Endpoint.
SG_BALEINE_VPCE=$(aws --profile baleine ec2 create-security-group --group-name baleine-s3-gateway-vpce-sg --description "PrivateLink endpoint to gros chat S3 gateway" --vpc-id <VPC_BALEINE> --query 'GroupId' --output text)
# Autoriser les clients du VPC baleine à joindre l’endpoint sur le port 8080.
aws --profile baleine ec2 authorize-security-group-ingress --group-id "$SG_BALEINE_VPCE" --protocol tcp --port 8080 --cidr <CIDR_VPC_BALEINE>
# 8. Côté baleine : créer l’Interface Endpoint consommateur.
VPCE_BALEINE=$(aws --profile baleine ec2 create-vpc-endpoint --vpc-id <VPC_BALEINE> --service-name "$SERVICE_NAME" --vpc-endpoint-type Interface --subnet-ids <SUBNET_BALEINE_A> <SUBNET_BALEINE_B> --security-group-ids "$SG_BALEINE_VPCE" --no-private-dns-enabled --tag-specifications 'ResourceType=vpc-endpoint,Tags=[{Key=Name,Value=baleine-to-groschat-s3-gateway}]' --query 'VpcEndpoint.VpcEndpointId' --output text)
# 9. Attendre que l’endpoint baleine soit disponible.
aws --profile baleine ec2 wait vpc-endpoint-available --vpc-endpoint-ids "$VPCE_BALEINE"
# 10. Récupérer le DNS privé que l’application baleine utilisera comme endpoint S3 custom.
DNS_PRIVATELINK_BALEINE=$(aws --profile baleine ec2 describe-vpc-endpoints --vpc-endpoint-ids "$VPCE_BALEINE" --query 'VpcEndpoints[0].DnsEntries[0].DnsName' --output text)
echo "$DNS_PRIVATELINK_BALEINE"
Le DNS PrivateLink obtenu côté baleine est privé au VPC. Il ne se teste donc pas directement depuis un poste local ; il faut tester depuis une ressource dans le VPC baleine, par exemple une instance EC2 temporaire pilotée par SSM.
Rôle exact du S3 Gateway Endpoint. Il s'agit d'une route privée attachée aux route tables du VPC gros chat pour atteindre le service S3 sans NAT ni Internet. Il n'est pas exposé à baleine et ne remplace pas la passerelle applicative rclone serve s3.
17 / 25
Validation fonctionnelle
Limite POC — credentials applicatifs dans SSM Run Command.
Dans les blocs de test, les variables LOCAL_ACCESS_KEY et LOCAL_SECRET_KEY sont injectées directement dans les commandes envoyées par SSM.
C'est acceptable pour un POC jetable avec des credentials applicatifs non AWS, mais ces paramètres peuvent rester visibles dans l'historique Run Command.
Une version durcie devra éviter ce passage en clair : Secrets Manager, SSM Parameter Store SecureString, injection contrôlée côté instance ou mécanisme d'identité applicative plus robuste.
Une instance EC2 temporaire a été lancée dans le VPC baleine, sans clé SSH, pilotée via AWS Systems Manager Session Manager. Elle a servi de client de test interne au VPC.
Test réseau TCP 8080
timeout 5 bash -c 'cat < /dev/null > /dev/tcp/<DNS_PRIVATELINK_BALEINE>/8080' \
&& echo OK || echo KO
Résultat :OK. Le chemin réseau PrivateLink jusqu'au service rclone est fonctionnel.
Ce que le POC prouve, et ce qu'il ne prouve pas encore
La validation a été volontairement progressive : DNS, TCP, réponse HTTP, listing S3, lecture d'objet, puis copie locale côté baleine.
✅ Ce qui est prouvé
baleine résout le DNS PrivateLink privé dans son VPC.
Le port 8080 est joignable jusqu'au NLB puis Fargate.
aws s3 ls s3://mab-documents liste bien fifi.txt, loulou.txt et riri.txt.
aws s3 cp s3://mab-documents/fifi.txt - lit le contenu Fifi mange une figue.
Les fichiers peuvent être copiés sur le disque local côté baleine.
baleine ne possède aucun droit IAM sur le bucket réel.
🧭 Ce qui reste à tester
Fichiers volumineux, streaming, range requests et reprise de téléchargement.
Multipart si un jour l'écriture est envisagée.
TLS de bout en bout ou terminaison TLS propre côté NLB / applicatif.
Externalisation et rotation des credentials applicatifs.
Restriction des users admin temporaires et durcissement IAM.
Supervision, alerting, logs structurés et version Terraform.
Test 1getent hosts <DNS_PRIVATELINK> : le DNS privé retourne une IP du VPC baleine.
Test 2/dev/tcp/<DNS>/8080 : la connectivité TCP vers la passerelle est validée.
Test 3curl -i : la passerelle répond comme une API S3-compatible.
Test 4aws s3 ls et aws s3 cp : le client S3 standard fonctionne avec endpoint custom.
Complément / cadrage honnête
Ce qui a réellement été réalisé, et ce qui relève du durcissement
Cette section évite de mélanger le POC effectivement déroulé avec les recommandations d'amélioration. Le POC a validé un flux privé inter-comptes fonctionnel ; les éléments ci-dessous marqués À prévoir ou Production sont des recommandations de durcissement, pas des choses prétendues comme déjà réalisées.
Formulation honnête du résultat
La conclusion du POC n’est pas : “ce pattern est naturellement meilleur que le partage IAM/S3 natif”.
La conclusion est plus précise : ce pattern fonctionne techniquement pour exposer un accès S3-compatible privé sans donner de droit IAM direct au compte consommateur, mais il transforme un sujet d’autorisation S3/IAM en sujet d’exploitation de passerelle.
L’intérêt positif du pattern n’est donc pas seulement d’éviter un grant direct sur le bucket : il est aussi
d’offrir à baleine une interface S3 stable et standard, pendant que gros chat
garde un point de contrôle centralisé sans multiplier les policies S3 par consommateur.
C’est donc un pattern à réserver aux contextes où la conservation du contrôle côté propriétaire, l’absence de droit IAM direct du consommateur sur le bucket et la simplicité d’exploitation côté consommateur priment sur l’attribution native CloudTrail par identité AWS consommatrice.
Élément
Statut dans ce POC
Commentaire
Deux comptes AWS distincts
RÉALISÉ
gros chat possède le bucket et la passerelle ; baleine consomme via PrivateLink.
Flux PrivateLink privé
RÉALISÉ
Le flux applicatif est bien initié par baleine vers le service exposé par gros chat.
Client S3 standard
RÉALISÉ
Validation avec aws s3 ls et aws s3 cp via --endpoint-url.
Bucket logique mab-documents
RÉALISÉ
Le remote rclone combine expose le bucket réel comme bucket logique côté client.
Lecture des fichiers de test
RÉALISÉ
fifi.txt, loulou.txt et riri.txt ont été listés/copés depuis baleine.
Absence de droit IAM direct pour baleine
RÉALISÉ
La lecture S3 réelle est portée par le Task Role ECS côté gros chat.
HTTP sur port 8080
RÉALISÉ POC
Acceptable pour la démonstration privée ; à remplacer par HTTPS avant tout usage durable.
Credentials applicatifs en clair dans le lancement rclone
RÉALISÉ POC
Choix de simplicité POC. Ce n'est pas une pratique de production.
Secrets Manager / SSM SecureString
À PRÉVOIR
À utiliser pour sortir les secrets de la task definition et de la ligne de commande.
TLS / certificat / DNS privé propre
PRODUCTION
À ajouter si la passerelle devient un service pérenne.
Tests gros fichiers / range / multipart / charge
À PRÉVOIR
Non validé par ce POC minimal ; nécessaire avant industrialisation.
RÉALISÉ
Validation démontrée
Le POC démontre la faisabilité du pattern : baleine appelle une passerelle S3-compatible privée, gros chat conserve le bucket et le contrôle IAM, et le client reste un client S3 standard.
À PRÉVOIR
Améliorations non encore réalisées
Les recommandations ci-dessous ne doivent pas être lues comme déjà implémentées. Elles servent à indiquer comment transformer le POC en solution plus robuste.
19 / 25
Couverture du déroulé pas à pas : du terminal au runbook reproductible
Cette section sert de pont entre le récit pédagogique et le runbook autonome. Elle ne renvoie plus à des numéros d’étapes internes à notre construction initiale : pour un lecteur externe, l’important est de comprendre les grandes familles d’actions à réaliser, dans quel compte AWS elles se déroulent, et quel point de contrôle permet de savoir que l’on peut continuer.
Le POC a bien été construit progressivement en terminal, avec de nombreux essais, vérifications et corrections. Cette page ne demande pas au lecteur de connaître ce déroulé historique. Elle regroupe maintenant ces actions en blocs opérationnels : préparer, construire côté gros chat, exposer via PrivateLink, consommer côté baleine, valider, puis nettoyer.
Bloc reproductible
Compte concerné
Ce que le lecteur doit faire
Checkpoint attendu
Point pédagogique à retenir
Préparer l’environnement CLI
Local + deux comptes
Installer les prérequis, configurer deux profils AWS isolés, vérifier l’identité avec sts get-caller-identity.
Les profils groschat et baleine répondent correctement.
On évite de polluer le profil AWS par défaut ; le POC reste isolé.
Préparer le bucket de test
gros chat
Vérifier ou créer mab-documents, puis y placer fifi.txt, loulou.txt et riri.txt.
aws s3 ls s3://mab-documents affiche les trois fichiers.
Le bucket réel reste toujours côté gros chat.
Créer les rôles IAM utiles
gros chat
Créer l’Execution Role ECS et le Task Role Fargate avec lecture minimale sur mab-documents.
Les deux rôles existent et la policy S3 est attachée au Task Role.
Le Task Role est la seule identité AWS qui lit réellement S3.
Construire l’image Docker rclone
Local + gros chat
Créer rclone.conf, créer le Dockerfile, builder l’image, la taguer et la pousser dans ECR.
Un digest ECR est retourné pour l’image rclone-s3-gateway:latest.
Fargate lance une image Docker ; il ne lance pas un fichier rclone.conf isolé.
Créer le réseau privé gros chat
gros chat
Créer VPC, subnets, route table et Security Groups nécessaires à ECS, aux endpoints AWS et au NLB.
Le VPC et les deux subnets sont disponibles.
La passerelle est privée ; elle n’a pas besoin d’être exposée à Internet.
Créer les endpoints AWS internes
gros chat
Créer le Gateway Endpoint S3 et les Interface Endpoints ECR API, ECR DKR et CloudWatch Logs.
Les endpoints d’interface sont available.
Sans ces endpoints, une task Fargate privée ne peut pas tirer l’image ECR ni écrire ses logs sans NAT.
Déployer la passerelle sur Fargate
gros chat
Créer le cluster ECS, le log group, la task definition, puis le service ECS Fargate.
Le service atteint Desired=1, Running=1, Pending=0.
Fargate est serverless, mais ECS reste décrit par des objets logiques : cluster, task definition, task et service.
Publier la passerelle via NLB
gros chat
Créer le Target Group de type ip, le NLB interne et le listener TCP 8080.
Le NLB est available et le service ECS est attaché au Target Group.
Avec Fargate, le Target Group doit cibler les IP privées des tasks.
Créer le service PrivateLink
gros chat
Créer l’Endpoint Service PrivateLink sur le NLB et autoriser le compte baleine.
Le service PrivateLink est Available.
Autoriser arn:aws:iam::<ACCOUNT_BALEINE>:root signifie autoriser le compte, pas donner un accès à l’utilisateur root.
Créer l’Interface Endpoint consommateur
baleine
Créer le Security Group et l’Interface VPC Endpoint vers le service PrivateLink publié par gros chat.
L’endpoint côté baleine est available et fournit un DNS privé.
Ce DNS est privé au VPC baleine ; il ne se teste pas depuis le poste local.
Créer un client de test dans baleine
baleine
Lancer une petite EC2 temporaire pilotée par SSM, sans clé SSH, pour tester depuis le VPC.
SSM indique PingStatus=Online.
Les tests réseau doivent être exécutés depuis une ressource située dans le VPC baleine.
Valider la chaîne réseau
baleine
Tester la résolution DNS PrivateLink, le port TCP 8080, puis une réponse HTTP brute avec curl.
Le DNS renvoie une IP privée, le test TCP retourne OK, et curl obtient une réponse de la passerelle.
Ces tests valident le réseau, mais pas encore la compatibilité S3 complète.
Valider le comportement S3
baleine
Utiliser aws s3 ls et aws s3 cp avec l’endpoint custom et les credentials applicatifs.
Le listing affiche fifi.txt, loulou.txt, riri.txt, puis fifi.txt est lu correctement.
Le client utilise une interface S3 standard, mais sans droit IAM sur le vrai bucket.
Comprendre et corriger le mapping rclone
gros chat
Utiliser le remote combinegateway: pour exposer mab-documents comme bucket logique.
Les logs Fargate montrent rclone serve s3 gateway:.
Les erreurs NoSuchBucket ont montré que le problème venait du mapping rclone, pas de PrivateLink.
Copier les fichiers côté client
baleine
Copier récursivement le bucket logique vers un répertoire local, par exemple ~/mab-documents.
Les fichiers existent sur le disque local du client de test.
Les fichiers ne sont pas créés dans un bucket S3 côté baleine ; ils sont simplement copiés sur le disque du client.
Prévoir la coupure et le nettoyage
gros chat + baleine
Documenter l’arrêt d’urgence, la révocation, la rotation des credentials et le nettoyage des ressources coûteuses.
Après nettoyage, plus de Fargate, NLB, VPC Endpoints, EC2, EIP, ECR ni logs du POC.
gros chat doit garder la capacité de couper rapidement l’accès.
Conclusion. Cette section n’est pas un historique numéroté de notre travail : c’est une checklist de reproductibilité. Le runbook autonome qui suit reprend ces blocs dans l’ordre, avec des variables, des commandes et des checkpoints.
20 / 25
Commandes commentées : comprendre ce que l'on fait
Cette section ne remplace pas le runbook complet : elle le rend plus lisible. Les commandes ci-dessous sont volontairement commentées en français pour expliquer les concepts et le rôle de chaque option importante.
0. Contexte CLI isolé : éviter de polluer le profil AWS par défaut
Création d'un environnement AWS CLI dédié au POC
# On crée un répertoire séparé pour ce POC.
# Objectif : ne pas modifier ~/.aws/config ni ~/.aws/credentials utilisés au quotidien.
mkdir -p ~/.aws-poc-s3-gateway
# Fichier de configuration isolé : deux profils, un par compte AWS.
# groschat = compte propriétaire du bucket et de la passerelle.
# baleine = compte consommateur de la passerelle.
cat > ~/.aws-poc-s3-gateway/config <<'EOF'
[profile groschat]
region = eu-west-3
output = json
[profile baleine]
region = eu-west-3
output = json
EOF
# Fichier de credentials isolé.
# Les access keys dédiées au POC seront mises ici, pas dans le profil par défaut.
touch ~/.aws-poc-s3-gateway/credentials
chmod 600 ~/.aws-poc-s3-gateway/credentials
# Petit fichier à sourcer dans le terminal pour dire à AWS CLI d'utiliser ces fichiers isolés.
cat > ~/aws-poc-s3-gateway.env <<'EOF'
export AWS_CONFIG_FILE=$HOME/.aws-poc-s3-gateway/config
export AWS_SHARED_CREDENTIALS_FILE=$HOME/.aws-poc-s3-gateway/credentials
export AWS_PAGER=""
EOF
# Activation dans le terminal courant.
source ~/aws-poc-s3-gateway.env
1. IAM côté gros chat : séparer rôle technique et rôle applicatif
Création du Task Role applicatif
# Trust policy : autorise ECS Tasks à assumer ce rôle.
# Sans cette relation de confiance, Fargate ne pourrait pas fournir ce rôle au conteneur.
cat > /tmp/trust-ecs-task.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "ecs-tasks.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}
EOF
# Task Role = rôle applicatif visible par le conteneur.
# C'est ce rôle que rclone utilise indirectement avec env_auth = true.
aws --profile groschat iam create-role --role-name rclone-s3-gateway-task-role --assume-role-policy-document file:///tmp/trust-ecs-task.json
# Policy minimale : rclone doit seulement lister le bucket et lire les objets.
# Il n'a pas besoin de PutObject, DeleteObject ou d'un accès à d'autres buckets.
cat > /tmp/rclone-read-mab-documents-policy.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListBucket",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::mab-documents"
},
{
"Sid": "ReadObjects",
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::mab-documents/*"
}
]
}
EOF
aws --profile groschat iam put-role-policy --role-name rclone-s3-gateway-task-role --policy-name ReadOnlyMabDocuments --policy-document file:///tmp/rclone-read-mab-documents-policy.json
Création de l'Execution Role ECS
# Execution Role = rôle technique d'ECS.
# Il sert à tirer l'image depuis ECR et à écrire les logs dans CloudWatch.
# Il ne doit pas être confondu avec le Task Role qui lit le bucket S3.
aws --profile groschat iam create-role --role-name ecsTaskExecutionRole --assume-role-policy-document file:///tmp/trust-ecs-task.json
aws --profile groschat iam attach-role-policy --role-name ecsTaskExecutionRole --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
2. Image Docker rclone : ce qui part vraiment dans Fargate
Configuration rclone commentée
# Remote mabaws : accès au vrai S3 AWS côté gros chat.
# env_auth = true signifie : ne pas mettre de clés AWS dans le fichier.
# rclone récupère ses credentials via le Task Role Fargate.
[mabaws]
type = s3
provider = AWS
env_auth = true
region = eu-west-3
location_constraint = eu-west-3
# Remote gateway : racine artificielle exposée par rclone serve s3.
# La ligne ci-dessous crée un bucket logique mab-documents côté client.
# Ce bucket logique pointe vers le vrai bucket AWS mabaws:mab-documents.
[gateway]
type = combine
upstreams = mab-documents=mabaws:mab-documents
Dockerfile commenté
# Image officielle rclone.
FROM rclone/rclone:latest
# On embarque la configuration rclone dans l'image.
# Pour un POC c'est simple ; en production, on préférerait monter/injecter la config.
COPY rclone.conf /config/rclone/rclone.conf
# Le conteneur écoute en TCP sur 8080.
EXPOSE 8080
# ENTRYPOINT fixe le binaire lancé.
ENTRYPOINT ["rclone"]
# CMD lance un serveur S3-compatible.
# gateway: est le remote combine qui expose mab-documents comme bucket logique.
# --auth-key protège la passerelle avec une paire access_key/secret_key applicative.
# Ces credentials ne sont pas des credentials AWS.
CMD ["serve", "s3", "gateway:", "--addr", ":8080", "--auth-key", "<LOCAL_ACCESS_KEY>,<LOCAL_SECRET_KEY>", "--vfs-cache-mode", "off", "-vv"]
3. Réseau gros chat : pourquoi tous ces VPC Endpoints ?
Endpoints AWS privés nécessaires à une task Fargate sans Internet
# La task Fargate est dans des subnets privés, sans NAT ni Internet Gateway.
# Elle a donc besoin d'endpoints privés pour contacter les services AWS nécessaires.
# Endpoint Gateway S3 : permet à rclone d'accéder au bucket S3 sans sortir sur Internet.
aws --profile groschat ec2 create-vpc-endpoint --vpc-id <VPC_GROSCHAT> --service-name com.amazonaws.eu-west-3.s3 --vpc-endpoint-type Gateway --route-table-ids <ROUTE_TABLE_GROSCHAT>
# Endpoint Interface ECR API : permet à ECS/Fargate de parler à l'API ECR.
aws --profile groschat ec2 create-vpc-endpoint --vpc-id <VPC_GROSCHAT> --service-name com.amazonaws.eu-west-3.ecr.api --vpc-endpoint-type Interface --subnet-ids <SUBNET_GROSCHAT_A> <SUBNET_GROSCHAT_B> --security-group-ids <SG_ENDPOINTS_AWS> --private-dns-enabled
# Endpoint Interface ECR DKR : permet de tirer les layers Docker de l'image.
aws --profile groschat ec2 create-vpc-endpoint --vpc-id <VPC_GROSCHAT> --service-name com.amazonaws.eu-west-3.ecr.dkr --vpc-endpoint-type Interface --subnet-ids <SUBNET_GROSCHAT_A> <SUBNET_GROSCHAT_B> --security-group-ids <SG_ENDPOINTS_AWS> --private-dns-enabled
# Endpoint Interface CloudWatch Logs : permet au conteneur d'envoyer ses logs.
aws --profile groschat ec2 create-vpc-endpoint --vpc-id <VPC_GROSCHAT> --service-name com.amazonaws.eu-west-3.logs --vpc-endpoint-type Interface --subnet-ids <SUBNET_GROSCHAT_A> <SUBNET_GROSCHAT_B> --security-group-ids <SG_ENDPOINTS_AWS> --private-dns-enabled
4. Service ECS : lancer et maintenir la passerelle
Création du service Fargate commentée
# create-service demande à ECS de maintenir une task active.
# --desired-count 1 : on veut exactement une passerelle en fonctionnement.
# --launch-type FARGATE : pas d'EC2 à gérer.
# networkConfiguration : la task reçoit une ENI privée dans les subnets indiqués.
# assignPublicIp=DISABLED : pas d'IP publique sur la task.
aws --profile groschat ecs create-service --cluster rclone-s3-gateway-cluster --service-name rclone-s3-gateway-service --task-definition "$TASK_DEF_ARN" --desired-count 1 --launch-type FARGATE --network-configuration "awsvpcConfiguration={subnets=[<SUBNET_GROSCHAT_A>,<SUBNET_GROSCHAT_B>],securityGroups=[<SG_ECS_TASK>],assignPublicIp=DISABLED}"
# Après ajout du NLB, on rattache le Target Group au service.
# ECS enregistrera automatiquement l'IP privée de la task dans le Target Group.
aws --profile groschat ecs update-service --cluster rclone-s3-gateway-cluster --service rclone-s3-gateway-service --load-balancers targetGroupArn=<TG_ARN>,containerName=rclone-s3-gateway,containerPort=8080 --force-new-deployment
5. PrivateLink : autoriser baleine puis consommer le service
Côté gros chat : exposer et autoriser
# gros chat expose son NLB comme Endpoint Service PrivateLink.
# --no-acceptance-required simplifie le POC : la connexion n'a pas besoin d'être acceptée manuellement.
aws --profile groschat ec2 create-vpc-endpoint-service-configuration --network-load-balancer-arns <NLB_ARN> --no-acceptance-required
# gros chat autorise le compte baleine à créer un Interface Endpoint vers ce service.
# :root signifie ici "le compte AWS baleine", pas l'utilisateur root utilisé en console.
aws --profile groschat ec2 modify-vpc-endpoint-service-permissions --service-id <VPCE_SERVICE_ID> --add-allowed-principals arn:aws:iam::<ACCOUNT_BALEINE>:root
Côté baleine : créer l'Interface Endpoint
# baleine crée un Interface Endpoint dans son VPC.
# Le DNS retourné par AWS sera privé au VPC baleine.
# --no-private-dns-enabled : on ne remplace pas le DNS AWS S3 global ; on utilise un endpoint explicite.
aws --profile baleine ec2 create-vpc-endpoint --vpc-id <VPC_BALEINE> --service-name com.amazonaws.vpce.eu-west-3.<VPCE_SERVICE_ID> --vpc-endpoint-type Interface --subnet-ids <SUBNET_BALEINE_A> <SUBNET_BALEINE_B> --security-group-ids <SG_VPCE_BALEINE> --no-private-dns-enabled
6. Tests depuis baleine : SSM, DNS, TCP, S3
Pourquoi tester via SSM ?
# Le DNS PrivateLink est privé au VPC baleine.
# Depuis ton PC local, il peut ne pas se résoudre ou ne pas être joignable.
# On lance donc les commandes depuis une EC2 temporaire dans le VPC baleine,
# pilotée par Systems Manager Session Manager, sans clé SSH.
# Test DNS : doit retourner une IP privée 172.31.x.x côté baleine.
getent hosts <DNS_PRIVATELINK_BALEINE>
# Test TCP : vérifie que le port 8080 répond jusqu'à rclone.
timeout 5 bash -c 'cat < /dev/null > /dev/tcp/<DNS_PRIVATELINK_BALEINE>/8080' && echo OK || echo KO
# Test HTTP brut : une erreur XML S3 est acceptable ici.
# Elle prouve que le service répond, même si curl ne signe pas correctement la requête S3.
curl -i --max-time 10 http://<DNS_PRIVATELINK_BALEINE>:8080/ | head -40
Test S3 final côté baleine
# Credentials applicatifs transmis à baleine.
# Ce ne sont pas des clés AWS et elles ne donnent aucun droit IAM sur gros chat.
export AWS_ACCESS_KEY_ID=<LOCAL_ACCESS_KEY>
export AWS_SECRET_ACCESS_KEY=<LOCAL_SECRET_KEY>
export AWS_EC2_METADATA_DISABLED=true
# Listing du bucket logique exposé par la passerelle.
aws s3 ls s3://mab-documents --endpoint-url http://<DNS_PRIVATELINK_BALEINE>:8080 --region eu-west-3
# Lecture d'un objet : le contenu vient du vrai bucket S3 dans gros chat.
aws s3 cp s3://mab-documents/fifi.txt - --endpoint-url http://<DNS_PRIVATELINK_BALEINE>:8080 --region eu-west-3
# Copie des fichiers sur le disque local de l'instance cliente baleine.
mkdir -p ~/mab-documents
aws s3 cp s3://mab-documents ~/mab-documents --recursive --endpoint-url http://<DNS_PRIVATELINK_BALEINE>:8080 --region eu-west-3
21 / 25
Runbook CLI autonome : reproduire le POC de bout en bout
Correction de robustesse du runbook. Les tests côté baleine ne doivent pas utiliser un simple sleep 3 après ssm send-command. L'agent SSM peut répondre plus lentement, surtout au premier appel. Le runbook utilise donc aws ssm wait command-executed avant de lire le résultat avec get-command-invocation.
Helper robuste pour exécuter une commande côté baleine via SSM
Cette version du runbook est pensée pour un lecteur externe qui n'a pas suivi la construction pas à pas. Elle centralise les variables, indique dans quel compte lancer chaque bloc, stocke les IDs retournés par AWS et ajoute des checkpoints après les étapes critiques.
Vérification de reproductibilité : les points bloquants classiques sont maintenant explicités dans le runbook — ordre de création des profils, création éventuelle du bucket de test, attente du NLB, endpoints AWS internes, sélection dynamique des AZ PrivateLink côté baleine et checkpoints de validation.
Important. Le runbook est volontairement pédagogique et orienté POC. Il crée des users de construction temporaires avec des droits larges pour aller vite. Avant industrialisation, remplacer ces users par des rôles/policies minimales, externaliser les credentials applicatifs dans un coffre de secrets, ajouter TLS et traduire l'ensemble en Terraform.
Bloc
Compte
But
Sortie attendue
Pré-requis
Local
AWS CLI v2, Docker, région, variables.
Commandes disponibles.
Profils
gros chat / baleine
Créer deux identités de construction et deux profils isolés.
sts get-caller-identity fonctionne pour les deux comptes.
Bucket
gros chat
Vérifier mab-documents et les fichiers de test.
fifi.txt, loulou.txt, riri.txt.
IAM + Docker
gros chat
Créer les rôles Fargate et l'image rclone dans ECR.
Lancer la passerelle Fargate derrière un NLB interne.
Service ECS Running = 1, target healthy.
PrivateLink
gros chat / baleine
Exposer le NLB, autoriser baleine, créer l'Interface Endpoint consommateur.
DNS PrivateLink côté baleine.
Validation
baleine
Tester DNS, TCP, HTTP, aws s3 ls, aws s3 cp.
Lecture de fifi.txt.
Runbook exécutable et commenté
Pré-requis locaux
# À vérifier sur le poste qui pilote le POC.
# Le runbook évite les alias : toutes les commandes utilisent explicitement
# --profile groschat ou --profile baleine.
aws --version
# attendu : AWS CLI v2
docker version
# attendu : Docker fonctionnel, car l'image rclone est construite localement
# Optionnel, mais pratique pour générer des secrets applicatifs de test
openssl version
# Région utilisée dans le POC
export REGION=eu-west-3
export AWS_PAGER=""
Variables centrales du runbook
# Fichier de variables réutilisable entre les blocs.
# Important : à ce stade, on ne récupère pas encore les IDs de comptes.
# Les commandes sts seront lancées après la configuration des profils groschat et baleine.
mkdir -p ~/poc-s3-gateway/rclone-s3-gateway
cat > ~/poc-s3-gateway/poc-vars.env <<'EOF'
export REGION=eu-west-3
export BUCKET_NAME=mab-documents
export GROSCHAT_PROFILE=groschat
export BALEINE_PROFILE=baleine
export AWS_PAGER=""
EOF
source ~/poc-s3-gateway/poc-vars.env
cat ~/poc-s3-gateway/poc-vars.env
CloudShell gros chat : créer un user de construction temporaire
# À lancer dans AWS CloudShell du compte gros chat.
# Ce user sert uniquement de "tournevis" pour construire le POC.
# Il ne fait pas partie de l'architecture cible.
aws sts get-caller-identity
aws iam create-user \
--user-name poc-s3-gateway-admin
aws iam attach-user-policy \
--user-name poc-s3-gateway-admin \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess
aws iam create-access-key \
--user-name poc-s3-gateway-admin
# Conserver AccessKeyId et SecretAccessKey dans un coffre temporaire sécurisé.
# Ne pas les coller dans un dépôt Git ni dans une page publique.
CloudShell baleine : créer un user de construction temporaire
# À lancer dans AWS CloudShell du compte baleine.
# Même logique : user temporaire de construction, pas identité cible.
aws sts get-caller-identity
aws iam create-user \
--user-name poc-s3-gateway-client-admin
aws iam attach-user-policy \
--user-name poc-s3-gateway-client-admin \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess
aws iam create-access-key \
--user-name poc-s3-gateway-client-admin
# Conserver AccessKeyId et SecretAccessKey de baleine.
Poste local : contexte AWS CLI isolé
# On n'utilise pas le profil AWS par défaut.
# Les credentials du POC sont stockés dans un dossier dédié.
mkdir -p ~/.aws-poc-s3-gateway
cat > ~/.aws-poc-s3-gateway/config <<'EOF'
[profile groschat]
region = eu-west-3
output = json
[profile baleine]
region = eu-west-3
output = json
EOF
touch ~/.aws-poc-s3-gateway/credentials
chmod 600 ~/.aws-poc-s3-gateway/credentials
cat > ~/aws-poc-s3-gateway.env <<'EOF'
export AWS_CONFIG_FILE=$HOME/.aws-poc-s3-gateway/config
export AWS_SHARED_CREDENTIALS_FILE=$HOME/.aws-poc-s3-gateway/credentials
export AWS_PAGER=""
EOF
source ~/aws-poc-s3-gateway.env
source ~/poc-s3-gateway/poc-vars.env
# Renseigner les clés obtenues dans CloudShell gros chat.
aws configure set aws_access_key_id "<ACCESS_KEY_GROSCHAT>" --profile groschat
aws configure set aws_secret_access_key "<SECRET_KEY_GROSCHAT>" --profile groschat
# Renseigner les clés obtenues dans CloudShell baleine.
aws configure set aws_access_key_id "<ACCESS_KEY_BALEINE>" --profile baleine
aws configure set aws_secret_access_key "<SECRET_KEY_BALEINE>" --profile baleine
# Checkpoint : les deux profils doivent pointer vers deux comptes différents.
aws --profile groschat sts get-caller-identity
aws --profile baleine sts get-caller-identity
# Maintenant seulement, on récupère et on persiste les IDs de comptes.
export GROSCHAT_ACCOUNT_ID=$(aws --profile groschat sts get-caller-identity --query Account --output text)
export BALEINE_ACCOUNT_ID=$(aws --profile baleine sts get-caller-identity --query Account --output text)
cat >> ~/poc-s3-gateway/poc-vars.env <<EOF
export GROSCHAT_ACCOUNT_ID=$GROSCHAT_ACCOUNT_ID
export BALEINE_ACCOUNT_ID=$BALEINE_ACCOUNT_ID
EOF
cat ~/poc-s3-gateway/poc-vars.env
Checkpoint 1 : bucket de test côté gros chat
source ~/aws-poc-s3-gateway.env
source ~/poc-s3-gateway/poc-vars.env
# Le bucket réel doit exister côté gros chat.
# Si le bucket existe déjà, cette commande réussit.
# S'il n'existe pas, le bloc le crée en eu-west-3.
if ! aws --profile groschat s3api head-bucket --bucket "$BUCKET_NAME" 2>/dev/null; then
aws --profile groschat s3api create-bucket --bucket "$BUCKET_NAME" --region "$REGION" --create-bucket-configuration LocationConstraint="$REGION"
fi
# Créer ou remettre les fichiers de test minimaux.
printf 'Fifi mange une figue
' > /tmp/fifi.txt
printf 'Loulou lit le livre
' > /tmp/loulou.txt
printf 'Riri rit encore
' > /tmp/riri.txt
aws --profile groschat s3 cp /tmp/fifi.txt s3://$BUCKET_NAME/fifi.txt
aws --profile groschat s3 cp /tmp/loulou.txt s3://$BUCKET_NAME/loulou.txt
aws --profile groschat s3 cp /tmp/riri.txt s3://$BUCKET_NAME/riri.txt
# Sortie attendue : fifi.txt, loulou.txt, riri.txt
aws --profile groschat s3 ls s3://$BUCKET_NAME
Bloc 2 : IAM côté gros chat
source ~/aws-poc-s3-gateway.env
source ~/poc-s3-gateway/poc-vars.env
# Trust policy commune aux rôles ECS Task.
cat > /tmp/trust-ecs-task.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "ecs-tasks.amazonaws.com" },
"Action": "sts:AssumeRole"
}
]
}
EOF
# Task Role applicatif : c'est ce rôle qui lit vraiment le bucket S3.
aws --profile groschat iam create-role \
--role-name rclone-s3-gateway-task-role \
--assume-role-policy-document file:///tmp/trust-ecs-task.json
cat > /tmp/rclone-read-mab-documents-policy.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListBucket",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::mab-documents"
},
{
"Sid": "ReadObjects",
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::mab-documents/*"
}
]
}
EOF
aws --profile groschat iam put-role-policy \
--role-name rclone-s3-gateway-task-role \
--policy-name ReadOnlyMabDocuments \
--policy-document file:///tmp/rclone-read-mab-documents-policy.json
# Execution Role technique : permet à ECS/Fargate de tirer l'image ECR
# et d'écrire les logs CloudWatch.
aws --profile groschat iam create-role \
--role-name ecsTaskExecutionRole \
--assume-role-policy-document file:///tmp/trust-ecs-task.json
aws --profile groschat iam attach-role-policy \
--role-name ecsTaskExecutionRole \
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
export TASK_ROLE_ARN=$(aws --profile groschat iam get-role --role-name rclone-s3-gateway-task-role --query 'Role.Arn' --output text)
export EXEC_ROLE_ARN=$(aws --profile groschat iam get-role --role-name ecsTaskExecutionRole --query 'Role.Arn' --output text)
cat >> ~/poc-s3-gateway/poc-vars.env <<EOF
export TASK_ROLE_ARN=$TASK_ROLE_ARN
export EXEC_ROLE_ARN=$EXEC_ROLE_ARN
EOF
echo "$TASK_ROLE_ARN"
echo "$EXEC_ROLE_ARN"
Bloc 3 : image Docker rclone et ECR côté gros chat
source ~/aws-poc-s3-gateway.env
source ~/poc-s3-gateway/poc-vars.env
cd ~/poc-s3-gateway/rclone-s3-gateway
# Credentials applicatifs de la passerelle.
# Ce ne sont pas des credentials AWS.
export LOCAL_ACCESS_KEY="local-$(openssl rand -hex 8)"
export LOCAL_SECRET_KEY="$(openssl rand -hex 24)"
cat >> ~/poc-s3-gateway/poc-vars.env <<EOF
export LOCAL_ACCESS_KEY=$LOCAL_ACCESS_KEY
export LOCAL_SECRET_KEY=$LOCAL_SECRET_KEY
EOF
# rclone remote AWS : env_auth=true signifie que rclone utilise le Task Role Fargate.
# gateway: combine expose mab-documents comme bucket logique côté client S3.
cat > rclone.conf <<'EOF'
[mabaws]
type = s3
provider = AWS
env_auth = true
region = eu-west-3
location_constraint = eu-west-3
[gateway]
type = combine
upstreams = mab-documents=mabaws:mab-documents
EOF
# Pour le POC, les credentials applicatifs sont injectés dans la commande.
# En production, les sortir du Dockerfile et passer par Secrets Manager / variables ECS.
cat > Dockerfile <<EOF
FROM rclone/rclone:latest
COPY rclone.conf /config/rclone/rclone.conf
EXPOSE 8080
ENTRYPOINT ["rclone"]
CMD ["serve", "s3", "gateway:", "--addr", ":8080", "--auth-key", "$LOCAL_ACCESS_KEY,$LOCAL_SECRET_KEY", "--vfs-cache-mode", "off", "-vv"]
EOF
aws --profile groschat ecr create-repository \
--repository-name rclone-s3-gateway \
--image-scanning-configuration scanOnPush=true \
--encryption-configuration encryptionType=AES256
export ECR_URI=$(aws --profile groschat ecr describe-repositories \
--repository-names rclone-s3-gateway \
--query 'repositories[0].repositoryUri' \
--output text)
cat >> ~/poc-s3-gateway/poc-vars.env <<EOF
export ECR_URI=$ECR_URI
EOF
docker build -t rclone-s3-gateway:latest .
docker tag rclone-s3-gateway:latest "$ECR_URI:latest"
aws --profile groschat ecr get-login-password --region "$REGION" \
| docker login --username AWS --password-stdin "$GROSCHAT_ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com"
docker push "$ECR_URI:latest"
aws --profile groschat ecr describe-images \
--repository-name rclone-s3-gateway \
--image-ids imageTag=latest \
--query 'imageDetails[0].imageDigest' \
--output text
Bloc 11 : tests DNS, TCP, HTTP et S3 depuis baleine
source ~/aws-poc-s3-gateway.env
source ~/poc-s3-gateway/poc-vars.env
# Helper : envoyer une commande shell dans l'EC2 baleine via SSM.
run_baleine() {
local comment="$1"
local command="$2"
local cid
cid=$(aws --profile baleine ssm send-command \
--instance-ids "$INSTANCE_TEST_BALEINE" \
--document-name "AWS-RunShellScript" \
--comment "$comment" \
--parameters "commands=[\"$command\"]" \
--query 'Command.CommandId' \
--output text)
aws --profile baleine ssm wait command-executed \
--command-id "$cid" \
--instance-id "$INSTANCE_TEST_BALEINE"
aws --profile baleine ssm get-command-invocation \
--command-id "$cid" \
--instance-id "$INSTANCE_TEST_BALEINE" \
--query '{Status:Status,Output:StandardOutputContent,Error:StandardErrorContent}' \
--output json
}
# Test DNS : doit retourner une IP privée du VPC baleine.
run_baleine "Test PrivateLink DNS" \
"getent hosts $DNS_PRIVATELINK_BALEINE"
# Test TCP : doit afficher OK.
run_baleine "Test PrivateLink TCP 8080" \
"timeout 5 bash -c 'cat < /dev/null > /dev/tcp/$DNS_PRIVATELINK_BALEINE/8080' && echo OK || echo KO"
# Test HTTP brut : une erreur XML S3 est acceptable ; elle prouve que rclone répond.
run_baleine "Test HTTP S3-compatible" \
"curl -i --max-time 10 http://$DNS_PRIVATELINK_BALEINE:8080/ | head -40"
# Test S3 final : listing du bucket logique.
run_baleine "Test aws s3 ls" \
"AWS_ACCESS_KEY_ID=$LOCAL_ACCESS_KEY AWS_SECRET_ACCESS_KEY=$LOCAL_SECRET_KEY AWS_EC2_METADATA_DISABLED=true aws s3 ls s3://mab-documents --endpoint-url http://$DNS_PRIVATELINK_BALEINE:8080 --region eu-west-3"
# Test GetObject.
run_baleine "Test aws s3 cp fifi" \
"AWS_ACCESS_KEY_ID=$LOCAL_ACCESS_KEY AWS_SECRET_ACCESS_KEY=$LOCAL_SECRET_KEY AWS_EC2_METADATA_DISABLED=true aws s3 cp s3://mab-documents/fifi.txt - --endpoint-url http://$DNS_PRIVATELINK_BALEINE:8080 --region eu-west-3"
# Copie sur le disque local de l'instance baleine.
run_baleine "Copy files to local disk" \
"mkdir -p ~/mab-documents && AWS_ACCESS_KEY_ID=$LOCAL_ACCESS_KEY AWS_SECRET_ACCESS_KEY=$LOCAL_SECRET_KEY AWS_EC2_METADATA_DISABLED=true aws s3 cp s3://mab-documents ~/mab-documents --recursive --endpoint-url http://$DNS_PRIVATELINK_BALEINE:8080 --region eu-west-3 && ls -l ~/mab-documents"
Checkpoints attendus
# 1. DNS PrivateLink
# getent hosts <DNS_PRIVATELINK_BALEINE>
# -> retourne une IP privée du VPC baleine, par exemple 172.31.x.x
# 2. TCP
# -> OK
# 3. HTTP brut
# -> HTTP 400 / XML S3, acceptable avec curl non signé
# 4. Listing S3
# -> fifi.txt, loulou.txt, riri.txt
# 5. Lecture d'objet
# -> Fifi mange une figue
# 6. Copie locale
# -> les fichiers existent dans ~/mab-documents côté instance baleine
En cas de ressource déjà existante
# Si une commande create-role / create-repository / create-security-group échoue avec AlreadyExists,
# deux options propres :
# 1. réutiliser la ressource si elle appartient bien au POC ;
# 2. la supprimer puis relancer le bloc.
# Exemple : vérifier un rôle existant
aws --profile groschat iam get-role --role-name rclone-s3-gateway-task-role
# Exemple : vérifier un repository ECR existant
aws --profile groschat ecr describe-repositories --repository-names rclone-s3-gateway
# Exemple : retrouver une ressource par tag ou par nom
aws --profile groschat ec2 describe-vpcs \
--filters "Name=tag:Name,Values=poc-s3-gateway-vpc" \
--query 'Vpcs[*].VpcId' \
--output text
Complément / fiabilisation
Corrections apportées au runbook de construction et de test
Ces corrections ne changent pas l'architecture du POC. Elles fiabilisent le déroulé CLI pour éviter les faux négatifs, les réexécutions fragiles et les incohérences avec le discours sur les secrets.
CORRIGÉ
SSM Run Command
Le runbook n'utilise plus un simple sleep 3 après send-command. Il attend explicitement la fin avec aws ssm wait command-executed avant de lire le résultat.
CORRIGÉ
Révision ECS
Le runbook ne dépend plus de rclone-s3-gateway:1. Il capture la task definition retournée par AWS dans TASK_DEF_ARN.
LIMITE POC
Credentials applicatifs dans SSM
Les tests injectent encore les credentials applicatifs dans les commandes SSM pour rester simples. C'est documenté comme limite POC, à remplacer par Secrets Manager, SecureString ou un autre mécanisme d'injection sécurisé en version durable.
22 / 25
Checkpoints de reproductibilité du runbook
Cette section remplace l'ancien “audit de conversation”. Pour un lecteur externe, l'objectif n'est pas de connaître nos 94 micro-étapes de terminal, mais de savoir si chaque grande phase du runbook laisse une trace vérifiable avant de passer à la suivante.
Règle de lecture. Après chaque bloc CLI autonome, le lecteur doit obtenir soit un identifiant AWS stocké dans une variable, soit un état attendu clairement vérifiable. Si un checkpoint échoue, il ne faut pas continuer : il faut corriger la brique concernée.
Fargate ne peut démarrer que si l'image existe dans ECR.
VPC endpoints AWS gros chat
describe-vpc-endpoints
s3, ecr.api, ecr.dkr, logs en available
La task Fargate en subnet privé doit pouvoir tirer l'image et écrire ses logs sans NAT.
ECS/Fargate
describe-services
Desired=1, Running=1, Pending=0
La passerelle doit réellement tourner.
Logs rclone
logs get-log-events
rclone serve s3 gateway:
Confirme que le conteneur sert le remote combine, pas un mauvais mapping.
NLB / Target Group
describe-target-health
Target healthy sur le port 8080
PrivateLink ne servira à rien si le NLB ne voit pas la task.
Endpoint Service gros chat
describe-vpc-endpoint-service-configurations
ServiceState=Available
Le service doit être consommable par baleine.
Interface Endpoint baleine
describe-vpc-endpoints
State=available + DNS PrivateLink
Le point d'entrée privé doit être créé dans le VPC baleine.
Client de test baleine
ssm describe-instance-information
PingStatus=Online
Les tests doivent être lancés depuis le VPC baleine, pas depuis le poste local.
Validation S3
aws s3 ls puis aws s3 cp avec --endpoint-url
Listing des 3 fichiers puis contenu de fifi.txt
C'est la preuve fonctionnelle du POC.
Les pièges explicitement couverts par la page
🧩
Mapping rclone
Le remote combine est indispensable pour exposer mab-documents comme bucket logique côté client.
🔐
Credentials locales
La paire transmise à baleine n'est pas une paire AWS IAM. Elle sert uniquement à entrer dans la passerelle.
🌐
DNS privé
Le DNS PrivateLink ne se teste pas depuis le poste local, mais depuis une ressource dans le VPC baleine.
23 / 25
Révocation et coupure rapide de l'accès
gros chat garde le contrôle opérationnel de la passerelle. Il peut couper globalement le service, couper spécifiquement baleine, ou révoquer les credentials applicatifs transmis au client.
Les commandes ci-dessous supposent que les variables du runbook autonome sont chargées, notamment VPCE_SERVICE_ID, BALEINE_ACCOUNT_ID et, si besoin, VPCE_BALEINE. Elles ne sont à lancer que si l'infrastructure existe encore.
Plus rapide
Mettre le service ECS à desired-count = 0. C'est la coupure globale la plus simple : plus aucune task Fargate ne sert la passerelle.
Plus ciblé
Retirer baleine des principals autorisés PrivateLink et/ou rejeter sa connexion existante, puis faire tourner les credentials applicatifs si nécessaire.
Niveau 1 — coupure globale immédiate côté gros chat
Pour invalider l'ancienne paire <LOCAL_ACCESS_KEY> / <LOCAL_SECRET_KEY>, il faut modifier le secret ou la commande de démarrage rclone, reconstruire l'image si nécessaire, puis forcer un redéploiement ECS. En production, ces valeurs doivent venir d'un coffre-fort de secrets, pas du Dockerfile.
À retenir. Retirer le principal PrivateLink empêche surtout les nouveaux endpoints. Pour une coupure immédiate, le plus fiable reste desired-count = 0 ou la rotation de la paire applicative si l'on veut garder le service ouvert à d'autres consommateurs.
24 / 25
Durcissement à prévoir
SPOF Fargate et composant rclone : statut exact
Le POC a volontairement utilisé un déploiement minimal pour valider le flux. Il ne faut donc pas présenter cette version comme hautement disponible.
Une seule task Fargate, un service minimal ou un paramétrage de test suffisent à démontrer la faisabilité, mais ne suffisent pas à qualifier une architecture de production.
Pour une version production, il faudrait au minimum : service ECS multi-AZ, plusieurs tasks, health checks TCP robustes, autoscaling, stratégie de déploiement, logs applicatifs, supervision, alerting, rotation des credentials applicatifs, TLS, tests de charge et décision claire sur le niveau de support acceptable de rclone comme composant de passerelle.
Important — ne pas confondre POC et production. Les points ci-dessous sont des améliorations à prévoir. Ils n'ont pas tous été réalisés pendant le POC.
PRODUCTION
HTTPS et endpoint privé propre
Le POC a utilisé HTTP sur :8080. Pour une version durable, prévoir TLS, un nom DNS privé stable et une gestion propre du certificat. Même sur PrivateLink, il vaut mieux éviter de faire circuler des credentials applicatifs en HTTP.
PRODUCTION
Secrets hors task definition
Ne pas laisser local-access-key,local-secret-key en clair dans le Dockerfile, l'image ou la task definition. Utiliser Secrets Manager ou SSM Parameter Store SecureString, avec KMS et une policy minimale.
Si Secrets Manager est utilisé dans des subnets privés, prévoir aussi l'endpoint privé com.amazonaws.eu-west-3.secretsmanager, et éventuellement KMS selon le mode de chiffrement retenu.
À PRÉVOIR
Path-style S3 explicite
Avec un endpoint S3 custom, forcer ou documenter le mode path-style évite que certains clients tentent un appel de type mab-documents.<endpoint>.
Configuration recommandée côté client
aws configure set profile.baleine.s3.addressing_style path
À PRÉVOIR
Health check NLB
Pour le POC, privilégier un health check TCP sur 8080. Un health check HTTP n'est pertinent qu'après avoir vérifié précisément le code retourné par rclone serve s3 sur le chemin choisi.
À PRÉVOIR
AZ et PrivateLink
Ne pas supposer aveuglément que eu-west-3a correspond physiquement à la même zone dans deux comptes. Le runbook doit s'appuyer sur les AZ exposées par le service PrivateLink, et en production raisonner en AZ IDs quand c'est possible.
À PRÉVOIR
Tests complémentaires
Valider les gros fichiers, les range requests, le streaming, les timeouts, les reprises, le comportement SDK applicatif et le niveau de logs CloudWatch. Le mode -vv est utile en POC, mais trop verbeux en production.
Le POC démontre le pattern. Pour le transformer en socle réutilisable, plusieurs points doivent être durcis.
🔐
Secrets
Remplacer les credentials locales codées dans l'image par AWS Secrets Manager ou un mécanisme équivalent.
🧯
IAM minimal
Supprimer les users admin temporaires et conserver uniquement les rôles nécessaires, avec droits strictement limités.
🔭
Observabilité
Exploiter CloudWatch Logs, métriques ECS, health checks NLB et alertes en cas d'échec de task.
🔏
TLS
Ajouter une terminaison TLS adaptée : NLB TLS avec ACM ou terminaison applicative selon les contraintes client.
🚦
Filtrage
Restreindre les Security Groups à la source exacte attendue et limiter les opérations aux besoins réels.
🔄
Rotation
Prévoir la rotation des credentials locales et un mécanisme de révocation côté passerelle.
🏗️
Terraform
Rejouer l'ensemble du POC en Infrastructure as Code quand le temps le permettra, pour éviter les écarts de configuration CLI.
🔍
Attribution
Avec un Task Role unique et une auth-key partagée, CloudTrail ne distingue pas quel appelant de baleine a lu quel objet : tous les accès se confondent en une seule identité. Prévoir une clé applicative par consommateur, voire un log applicatif côté passerelle, si la traçabilité par appelant est exigée.
🌐
Haute disponibilité
Le service tourne avec desired-count = 1 sur une seule task, donc une seule AZ effective : toute panne coupe la passerelle le temps qu'ECS relance. Passer à au moins deux tasks réparties sur plusieurs zones de disponibilité pour un usage continu.
🩺
Santé réelle
Le health check TCP ne valide que l'ouverture du port 8080, pas la santé applicative de rclone : un process figé qui garde le socket ouvert passe le check tout en servant des erreurs. Ajouter une sonde plus représentative du service S3 rendu.
Limite assumée du POC. Le service est validé en lecture avec ListObjectsV2 et GetObject. Les scénarios d'écriture, multipart upload, gros fichiers et range requests doivent être testés séparément avant production.
Composant frontière expérimental.rclone serve s3 est documenté comme expérimental en amont. Or c'est précisément lui qui porte la traduction d'identité (clé applicative → Task Role) et donc la frontière de sécurité du pattern. Avant industrialisation, valider sa stabilité sous charge réelle, ou évaluer une brique alternative (proxy S3-compatible dédié, MinIO) selon les exigences de l'équipe sécurité.
25 / 25
Nettoyage complet après démonstration
Le nettoyage doit supprimer les ressources coûteuses du POC sans supprimer par erreur les ressources métier conservées :
le bucket mab-documents côté gros chat et le site statique www.mabillot.com côté baleine s'ils existaient déjà.
Section corrigée. Le nettoyage utilise bien les variables créées par le runbook :
TG_ARN pour le Target Group et VPCE_BALEINE pour l'Interface Endpoint côté baleine.
Les anciens noms TARGET_GROUP_ARN et BALEINE_VPCE_ID ne doivent pas être utilisés.
Point de vigilance. Les commandes de suppression AWS sont souvent asynchrones. En particulier, les Interface Endpoints peuvent laisser des ENI quelques minutes après leur suppression. Cette section ajoute donc des boucles d'attente réelles avant de supprimer les Security Groups et le VPC.
Préparer le terminal de nettoyage
source ~/aws-poc-s3-gateway.env
source ~/poc-s3-gateway/poc-vars.env
export AWS_PAGER=""
# Pour éviter les surprises, on vérifie les variables critiques.
echo "TG_ARN=$TG_ARN"
echo "TASK_DEF_ARN=$TASK_DEF_ARN"
echo "VPCE_BALEINE=$VPCE_BALEINE"
echo "VPC_GROSCHAT=$VPC_GROSCHAT"
echo "SG_VPCE_BALEINE=$SG_VPCE_BALEINE"
1. Couper puis supprimer ECS/Fargate côté gros chat
Cette étape arrête la facturation Fargate en ramenant le service à zéro task, puis supprime le service et le cluster.
# Les ENI des Interface Endpoints peuvent rester quelques minutes.
# On attend qu'il ne reste plus aucune ENI dans le VPC du POC avant de supprimer SG/VPC.
while aws --profile groschat ec2 describe-network-interfaces \
--filters "Name=vpc-id,Values=$VPC_GROSCHAT" \
--query 'NetworkInterfaces[*].NetworkInterfaceId' \
--output text | grep -q .; do
echo "ENI encore présentes côté gros chat, attente 15 secondes..."
aws --profile groschat ec2 describe-network-interfaces \
--filters "Name=vpc-id,Values=$VPC_GROSCHAT" \
--query 'NetworkInterfaces[*].{Eni:NetworkInterfaceId,Status:Status,Description:Description}' \
--output table
sleep 15
done
4. Supprimer l'instance de test et l'Interface Endpoint côté baleine
C'est le point de nettoyage le plus important côté coût : l'Interface Endpoint PrivateLink côté baleine est facturé tant qu'il existe. Il faut utiliser VPCE_BALEINE.
Nettoyage du client de test baleine
# Termine l'EC2 temporaire utilisée pour tester depuis le VPC baleine.
aws --profile baleine ec2 terminate-instances \
--instance-ids "$INSTANCE_TEST_BALEINE" || echo "Instance de test déjà absente"
aws --profile baleine ec2 wait instance-terminated \
--instance-ids "$INSTANCE_TEST_BALEINE" || echo "Instance de test déjà terminée"
# Supprime l'Interface Endpoint consommateur côté baleine.
# Attention : la bonne variable est VPCE_BALEINE, pas BALEINE_VPCE_ID.
aws --profile baleine ec2 delete-vpc-endpoints \
--vpc-endpoint-ids "$VPCE_BALEINE" || echo "Endpoint baleine déjà supprimé"
# Attend que l'endpoint ne soit plus visible.
while aws --profile baleine ec2 describe-vpc-endpoints \
--vpc-endpoint-ids "$VPCE_BALEINE" >/dev/null 2>&1; do
echo "Endpoint baleine encore visible, attente 15 secondes..."
sleep 15
done
# Attend que les ENI associées au SG de l'endpoint disparaissent.
while aws --profile baleine ec2 describe-network-interfaces \
--filters "Name=group-id,Values=$SG_VPCE_BALEINE" \
--query 'NetworkInterfaces[*].NetworkInterfaceId' \
--output text | grep -q .; do
echo "ENI encore présentes côté baleine, attente 15 secondes..."
aws --profile baleine ec2 describe-network-interfaces \
--filters "Name=group-id,Values=$SG_VPCE_BALEINE" \
--query 'NetworkInterfaces[*].{Eni:NetworkInterfaceId,Status:Status,Description:Description}' \
--output table
sleep 15
done
# Supprime les Security Groups du POC côté baleine.
aws --profile baleine ec2 delete-security-group \
--group-id "$SG_TEST_EC2_BALEINE" || echo "SG EC2 baleine déjà supprimé"
aws --profile baleine ec2 delete-security-group \
--group-id "$SG_VPCE_BALEINE" || echo "SG endpoint baleine déjà supprimé"
5. Supprimer Security Groups, route table, subnets et VPC du POC côté gros chat
Cette étape ne doit être lancée qu'après disparition des ENI. Sinon AWS renvoie souvent DependencyViolation.
Nettoyage réseau gros chat
# Supprime d'abord les Security Groups quand les ENI ont disparu.
aws --profile groschat ec2 delete-security-group \
--group-id "$SG_VPCE_GROSCHAT" || echo "SG VPCE gros chat déjà supprimé"
aws --profile groschat ec2 delete-security-group \
--group-id "$SG_ECS" || echo "SG ECS déjà supprimé"
# Désassocie les subnets de la route table dédiée.
aws --profile groschat ec2 disassociate-route-table \
--association-id "$RTB_ASSOC_A" || echo "Association route table A déjà absente"
aws --profile groschat ec2 disassociate-route-table \
--association-id "$RTB_ASSOC_B" || echo "Association route table B déjà absente"
# Supprime route table, subnets puis VPC du POC.
aws --profile groschat ec2 delete-route-table \
--route-table-id "$RTB_GROSCHAT" || echo "Route table déjà supprimée"
aws --profile groschat ec2 delete-subnet \
--subnet-id "$SUBNET_GROSCHAT_A" || echo "Subnet A déjà supprimé"
aws --profile groschat ec2 delete-subnet \
--subnet-id "$SUBNET_GROSCHAT_B" || echo "Subnet B déjà supprimé"
aws --profile groschat ec2 delete-vpc \
--vpc-id "$VPC_GROSCHAT" || echo "VPC gros chat déjà supprimé"
6. Supprimer ECR, CloudWatch Logs et task definitions
Ressources hors VPC côté gros chat
# Supprime les logs du conteneur rclone.
aws --profile groschat logs delete-log-group \
--log-group-name /ecs/rclone-s3-gateway || echo "Log group déjà supprimé"
# Supprime l'image Docker et le repository ECR du POC.
aws --profile groschat ecr delete-repository \
--repository-name rclone-s3-gateway \
--force || echo "Repository ECR déjà supprimé"
# Les task definitions ne coûtent pas, mais on peut désenregistrer les révisions actives.
for TD in $(aws --profile groschat ecs list-task-definitions \
--family-prefix rclone-s3-gateway \
--status ACTIVE \
--query 'taskDefinitionArns[]' \
--output text); do
aws --profile groschat ecs deregister-task-definition \
--task-definition "$TD"
done
7. Supprimer les rôles techniques créés pour le POC
Rôles IAM techniques gros chat et baleine
# Côté gros chat : rôle applicatif Fargate.
aws --profile groschat iam delete-role-policy \
--role-name rclone-s3-gateway-task-role \
--policy-name ReadOnlyMabDocuments || echo "Policy inline déjà supprimée"
aws --profile groschat iam delete-role \
--role-name rclone-s3-gateway-task-role || echo "Task Role déjà supprimé"
# Côté gros chat : execution role ECS.
aws --profile groschat iam detach-role-policy \
--role-name ecsTaskExecutionRole \
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy || echo "Policy execution role déjà détachée"
aws --profile groschat iam delete-role \
--role-name ecsTaskExecutionRole || echo "Execution role déjà supprimé"
# Côté baleine : rôle EC2 SSM utilisé par l'instance temporaire.
aws --profile baleine iam remove-role-from-instance-profile \
--instance-profile-name poc-s3-gateway-ec2-ssm-profile \
--role-name poc-s3-gateway-ec2-ssm-role || echo "Rôle déjà retiré de l'instance profile"
aws --profile baleine iam delete-instance-profile \
--instance-profile-name poc-s3-gateway-ec2-ssm-profile || echo "Instance profile déjà supprimé"
aws --profile baleine iam detach-role-policy \
--role-name poc-s3-gateway-ec2-ssm-role \
--policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore || echo "Policy SSM déjà détachée"
aws --profile baleine iam delete-role \
--role-name poc-s3-gateway-ec2-ssm-role || echo "Rôle SSM baleine déjà supprimé"
8. Supprimer les users admin temporaires du POC
Risque sécurité supérieur au risque coût. Les users poc-s3-gateway-admin et
poc-s3-gateway-client-admin ne coûtent rien, mais s'ils conservent une clé longue durée avec
AdministratorAccess, ils deviennent le principal résidu dangereux du POC.
À lancer depuis un autre accès administrateur, par exemple CloudShell ou un profil racine d'administration, car supprimer l'utilisateur qui porte le profil CLI courant peut couper l'accès en cours.
Suppression des users admin temporaires
# À lancer depuis un autre accès admin que les users à supprimer.
# gros chat
for KEY in $(aws --profile groschat iam list-access-keys \
--user-name poc-s3-gateway-admin \
--query 'AccessKeyMetadata[*].AccessKeyId' \
--output text); do
aws --profile groschat iam delete-access-key \
--user-name poc-s3-gateway-admin \
--access-key-id "$KEY"
done
aws --profile groschat iam detach-user-policy \
--user-name poc-s3-gateway-admin \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess || echo "Policy admin gros chat déjà détachée"
aws --profile groschat iam delete-user \
--user-name poc-s3-gateway-admin || echo "User admin gros chat déjà supprimé"
# baleine
for KEY in $(aws --profile baleine iam list-access-keys \
--user-name poc-s3-gateway-client-admin \
--query 'AccessKeyMetadata[*].AccessKeyId' \
--output text); do
aws --profile baleine iam delete-access-key \
--user-name poc-s3-gateway-client-admin \
--access-key-id "$KEY"
done
aws --profile baleine iam detach-user-policy \
--user-name poc-s3-gateway-client-admin \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess || echo "Policy admin baleine déjà détachée"
aws --profile baleine iam delete-user \
--user-name poc-s3-gateway-client-admin || echo "User admin baleine déjà supprimé"
9. Vérification finale exhaustive des coûts et résidus
Cette vérification compense le caractère best-effort du teardown. Les sorties doivent être vides ou indiquer explicitement des ressources déjà supprimées.
Limite POC à garder en tête. Les tests SSM du runbook injectent les credentials applicatifs dans les paramètres de send-command. Pour un POC jetable, c'est acceptable si l'environnement est nettoyé. Pour une version durable, ces credentials doivent être externalisés et l'historique SSM ne doit pas devenir le lieu de stockage involontaire des secrets.
Ce qui peut rester sans être un problème de coût : le bucket mab-documents avec ses trois fichiers, le VPC default vide de baleine, et une task definition ECS INACTIVE. En revanche, un Interface Endpoint côté baleine, un NLB, une EC2, une Elastic IP, une NAT Gateway ou des clés admin temporaires actives ne doivent pas rester après le POC.