AWS — S3 & KMS¶
Rôle¶
Usage AWS ciblé pour Indio, sur un compte AWS dédié Indio (région principale eu-west-3), réparti sur deux dépôts Terraform indépendants, sans lien technique entre eux :
/root/aws-s3(repo GitLabindio/iac/aws-s3) — socle cloud « core » de l'infra : bucket S3 pour le state Terraform distant + bucket S3 d'archivage (logs/sauvegardes) avec cycle de vie complet. Appliqué et vérifié en direct à ce jour (voir Architecture)./root/aws-s3-siem— projet séparé, un lab pédagogique de détection SIEM (« Lab étudiant », selon sa propre documentation) démontrant la remontée d'un événement de sécurité S3 jusqu'à une alerte Kibana. Le code Terraform est complet et cohérent, mais rien n'indique qu'il ait jamais été réellement déployé dans le compte AWS (voir Architecture) — à ne pas confondre avec le socle core.
La clé KMS de l'auto-unseal Vault n'est pas ici
Ni aws-s3 ni aws-s3-siem ne définissent de ressource aws_kms_key (vérifié directement dans les .tf). La clé KMS utilisée par Vault pour son auto-unseal (seal "awskms") est référencée par ARN directement dans les group_vars Ansible du dépôt vault (alias alias/vault-unseal, région eu-west-3) — elle a donc été créée hors Terraform, probablement à la main. Vérifié en direct (aws kms describe-key --key-id alias/vault-unseal) : la clé existe, Enabled, KeyManager=CUSTOMER, créée le 2026-05-15 — soit près de deux mois avant le premier projet Terraform AWS de l'infra (aws-s3, 2026-07-15). Voir Vault & PKI ; hors périmètre de cette page.
Architecture¶
aws-s3 — socle core (appliqué, vérifié en direct)¶
| Bucket | Ressource | Caractéristiques |
|---|---|---|
tfstate |
indio-tfstate-${var.aws_account_id} |
Versioning activé, chiffrement SSE-S3 (AES256), Public Access Block complet (4/4) |
backup_archive |
indio-backup-archive-${var.aws_account_id} |
SSE-S3, Public Access Block complet, pas de versioning (écriture unique), cycle de vie complet |
Cycle de vie de backup_archive (transitions configurables, valeurs par défaut) :
Standard --30j--> STANDARD_IA --90j--> GLACIER --180j--> DEEP_ARCHIVE --2555j (~7 ans)--> expiration
flowchart LR
CTL["Nœud de contrôle<br/>terraform apply<br/>profils AWS locaux default/s3"]
SEG["seg-sauvegarde<br/>10.100.3.16/28<br/>agents backup"]
FW["T0-Edge / FortiGate"]
subgraph AWS["Compte AWS dédié Indio — eu-west-3"]
TFSTATE[("Bucket S3 tfstate<br/>versionné, SSE-S3")]
ARCHIVE[("Bucket S3 backup_archive<br/>SSE-S3")]
SIA["STANDARD_IA"]
GL["GLACIER"]
DA["DEEP_ARCHIVE"]
EXP(["Expiration"])
end
CTL -->|"apply (aws-s3)"| TFSTATE
CTL -->|"apply (aws-s3)"| ARCHIVE
SEG --> FW
FW -->|"HTTPS — règle DFW<br/>sauvegarde-to-aws-s3"| ARCHIVE
ARCHIVE -->|30j| SIA
SIA -->|90j| GL
GL -->|180j| DA
DA -->|"2555j (~7 ans)"| EXP
Vérifié en direct le 2026-07-19 (lecture seule, aws s3api get-bucket-versioning / get-bucket-lifecycle-configuration, profil IAM s3) : la configuration réelle des deux buckets correspond exactement au code — versioning Enabled sur tfstate, cycle de vie 30j → STANDARD_IA, 90j → GLACIER, 180j → DEEP_ARCHIVE, expiration 2555j, identique à main.tf. Aucune dérive.
Les deux buckets sont vides (0 objet), également vérifié en direct :
tfstate: aucun projet Terraform de l'infra (une trentaine de dépôts) n'utilise encore ce backend distant — le state reste 100 % local partout (voir Points de vigilance).backup_archive: aucune tâche applicative n'y écrit encore, malgré le chemin réseau déjà ouvert côté NSX (règle DFWsauvegarde-to-aws-s3, zone admin → Internet en HTTPS depuisseg-sauvegarde, voir NSX) — la cible de consommation réelle (Veeam ? un script d'archivage dédié ?) n'a jamais été précisée ni implémentée à ce jour.
backend.tf.example fournit le modèle pour qu'un autre projet Terraform utilise ce bucket comme backend distant : verrouillage natif S3 (use_lockfile = true, sans DynamoDB, nécessite Terraform ≥ 1.10), chiffrement activé — non encore adopté par aucun projet.
aws-s3-siem — lab de détection (état de déploiement non confirmé)¶
Objectif du lab : journaliser, transporter et détecter un événement de sécurité S3 jusqu'à une alerte Kibana, avec deux chemins d'alerte volontairement redondants.
flowchart TD
ATTACK["attack/simulate.sh<br/>3 scénarios simulés"]
DATA[("Bucket S3 data<br/>cible simulée")]
CT["CloudTrail<br/>mono-région<br/>management + data events (bucket data)"]
LOGS[("Bucket S3 logs<br/>rétention 7j")]
SQS{{"SQS notifications<br/>+ DLQ (5 échecs)"}}
AGENT["Elastic Agent on-prem<br/>intégration AWS S3/SQS<br/>via proxy Squid 10.100.10.4:3128"]
ES[("Elasticsearch")]
KIB["Kibana<br/>alerte Elastic Security"]
EB["EventBridge<br/>StopLogging/DeleteTrail/PutEventSelectors"]
SNS{{"SNS topic alerts"}}
MAIL["Email (alert_email)"]
BUDGET["Budget guardrail<br/>5 USD/mois — 80% et 100%"]
ATTACK --> DATA
DATA --> CT
CT --> LOGS
LOGS -->|"S3 Event Notification"| SQS
SQS --> AGENT
AGENT --> ES
ES --> KIB
CT -.->|"évènement anti-forensics"| EB
EB --> SNS
SNS --> MAIL
BUDGET -.-> MAIL
- CloudTrail mono-région : management events complets + data events S3 filtrés au seul bucket
data(reste dans le free tier). - IAM : un utilisateur dédié
elastic_agent, strictement scopé en lecture seule (sqs:ReceiveMessage/DeleteMessage/ChangeMessageVisibility/GetQueueAttributessur la queue +s3:GetObjectsur le bucketlogsuniquement — jamais deResource: "*"). - Chemin anti-forensics indépendant : une règle EventBridge sur
StopLogging/DeleteTrail/PutEventSelectorspublie sur un topic SNS (abonnement email) — volontairement séparé du chemin SIEM, car couper CloudTrail couperait aussi la détection côté Elastic. - Garde-fou budget :
aws_budgets_budgetde typeCOST, 5 USD/mois, notifications à 80 % et 100 %. Sans filtre de coût (aucuncost_filterdansbudget.tf) : s'il était appliqué, ce budget surveillerait la dépense totale du compte AWS, pas seulement les ressources de ce lab — donc aussi, entre autres, l'usage KMS/S3 du socle core. - Simulation d'attaque (
attack/simulate.sh+cleanup.sh) : 3 scénarios — bucket rendu public, 500×GetObject(exfiltration simulée), arrêt de CloudTrail — avec remise en état automatisée. - Règles de détection fournies en NDJSON (
detections/s3-threat-rules.ndjson, 3 règles Elastic Security, mappées MITRE ATT&CK T1530/T1020). - Ce lab a sa propre documentation MkDocs (
aws-s3-siem/mkdocs.yml, thème readthedocs) sousdocs/— seules 3 pages sur 7 annoncées dans la nav existent réellement à ce jour (index.md,architecture.md,deployment.md).
Aucune preuve que ce lab ait été réellement déployé
/root/aws-s3-siem/terraform contient un dossier .terraform/ et un .terraform.lock.hcl (donc terraform init a été exécuté au moins une fois), mais aucun fichier terraform.tfstate n'existe nulle part dans le projet — à la différence d'aws-s3, qui en a un et dont l'état a été vérifié en direct ci-dessus. Aucun bloc backend n'est déclaré dans versions.tf : un apply réussi laisserait donc nécessairement un state local. La documentation du lab elle-même (docs/deployment.md) est explicite : « Je n'exécute jamais terraform apply moi-même — c'est vous qui déclenchez le déploiement. » Un sondage direct de quelques noms de bucket plausibles (sans le suffixe aléatoire réel du nom, donc non concluant à 100 %) n'a rien trouvé côté AWS. À vérifier avant de s'appuyer sur cette page pour un TP ou une démonstration : le code est prêt et cohérent, mais l'infrastructure qu'il décrit n'est vraisemblablement pas active dans le compte AWS à ce jour.
Provisioning Terraform¶
aws-s3 (main.tf, variables.tf, versions.tf, backend.tf.example) :
variables.tf:aws_region(défauteu-west-3),aws_account_id(identifiant du compte — non reproduit dans cette page), paramètres de transition du cycle de vie (30/90/180/2555jours par défaut).- Provider
hashicorp/aws >= 5.0.0, Terraform>= 1.5.0. - Sorties :
tfstate_bucket,backup_archive_bucket. - 2 commits seulement (
6ddf55fcréation,dc4155btiering complet du cycle de vie) — projet stable depuis son premier apply le 2026-07-15.
aws-s3-siem/terraform (9 fichiers) :
| Fichier | Contenu |
|---|---|
s3.tf |
Buckets data + logs, policy bucket scopée par aws:SourceArn (le trail précis, pas n'importe quel trail du compte) |
cloudtrail.tf |
Trail mono-région, advanced_event_selector (management + data events scopés) |
sqs.tf |
Queue + DLQ (14 j de rétention, maxReceiveCount = 5), notification S3 → SQS |
iam.tf |
Utilisateur elastic_agent + clé d'accès + policy lecture seule |
alerting.tf |
Topic SNS + abonnement email + règle EventBridge anti-forensics |
budget.tf |
Garde-fou coût (aws_budgets_budget, sans filtre — voir Architecture) |
outputs.tf |
Dont iam_secret_access_key marqué sensitive |
variables.tf |
name_prefix (défaut indio-s3lab), log_retention_days=7, budget_limit_usd=5, alert_email |
versions.tf |
Provider aws >= 5.0.0 + random >= 3.5.0 ; data.aws_caller_identity + random_id (suffixe d'unicité des noms de bucket) |
Aucune ressource KMS dans l'un ou l'autre dépôt (voir Rôle).
Configuration Ansible¶
Aucune dans les deux dépôts — Terraform seul, pas de dossier ansible/. La consommation applicative des ressources créées ici (Elastic Agent, Vault) est configurée dans les dépôts elk et vault, hors périmètre de cette page.
Procédure manuelle¶
Plusieurs étapes de ce périmètre AWS sont volontairement hors Terraform, à la main :
- Clé KMS
alias/vault-unseal: créée hors Terraform (voir Rôle), jamais reprise en IaC depuis. Aucune rotation ni suppression possible depuisaws-s3/aws-s3-siempour cette clé. - Pattern IAM récurrent : à chaque nouveau besoin AWS, c'est le user (pas l'agent Terraform/l'automatisation) qui crée l'utilisateur IAM dédié et attache la policy scopée fournie. Confirmé en direct aujourd'hui : ni le profil local
default(user/kms) ni le profils3(user/s3) n'ont le moindre droitiam:*—AccessDeniedsystématique, y compris pour lire leur propre policy attachée (iam:ListAttachedUserPolicies,iam:GetUserPolicy). C'est un choix de scoping strict assumé, pas une négligence : ces comptes ne peuvent agir que dans leur périmètre (KMS seul pour l'un, S3/SQS/IAM du lab pour l'autre côté code), jamais s'auto-inspecter ni s'élargir eux-mêmes. - Configuration locale des profils AWS (
~/.aws/credentials) : deux profils distincts,default(usage KMS) ets3(usage buckets), jamais un seul profil réutilisé pour les deux usages. - Côté
aws-s3-siem, une foisterraform applyréellement lancé (voir avertissement en Architecture) :- Confirmer l'abonnement SNS reçu par email (topic
<name_prefix>-alerts) — aucune erreur visible côté Terraform si cette étape est oubliée, l'abonnement reste juste enPendingConfirmationet aucune alerte anti-forensics ni notification budgétaire n'est reçue. - Configurer l'intégration AWS côté Elastic Agent (Fleet → Add integration → AWS → input S3 en mode SQS, avec
proxy_url = http://10.100.10.4:3128obligatoire — voir Proxy Squid & DNS). - Importer les règles de détection NDJSON dans Kibana.
- Lancer
attack/simulate.shpuiscleanup.shpour valider et nettoyer le scénario.
- Confirmer l'abonnement SNS reçu par email (topic
Procédure de déploiement¶
aws-s3 :
aws-s3-siem :
cd /root/aws-s3-siem/terraform
terraform init
terraform plan -var="alert_email=<email>"
terraform apply -var="alert_email=<email>"
Les étapes de configuration post-apply (confirmation SNS, intégration Elastic Agent, import des règles, simulation) sont détaillées ci-dessus en Procédure manuelle.
Points d'attention¶
- Ne pas confondre les deux dépôts :
aws-s3est le socle infra core, appliqué et vérifié en direct (tfstate + archivage, deux buckets vides mais existants et configurés) ;aws-s3-siemest un lab pédagogique dont le code est prêt mais dont le déploiement réel dans le compte AWS n'est pas confirmé — buckets, IAM et cycle de vie totalement distincts et non partagés entre les deux de toute façon. - La clé KMS de l'auto-unseal Vault ne vit dans aucun des deux dépôts documentés ici (voir encadré en Rôle) : aucune rotation ni suppression possible depuis
aws-s3/aws-s3-siempour cette clé. iam_secret_access_key(aws-s3-siem) est un output Terraform marquésensitive: toujours le récupérer viaterraform output -raw iam_secret_access_key, jamais via unterraform outputsimple, et ne jamais le committer.- L'abonnement SNS (anti-forensics + budget) reste en
PendingConfirmationtant que l'email de confirmation n'est pas validé manuellement — silence total côté Terraform si on l'oublie, pas d'alerte visible. - Le garde-fou budget (
aws-s3-siem, 5 USD/mois) n'a aucun filtre de coût : s'il était un jour appliqué, il surveillerait la dépense de tout le compte AWS, pas seulement les ressources du lab — à garder en tête avant de considérer que « le lab reste dans son budget » couvre aussi le socle core (aws-s3). - Les data events CloudTrail du lab sont volontairement restreints au seul bucket
data(pas tout le compte) pour rester dans le free tier — un usage hors lab nécessiterait de revoir ce périmètre. - L'Elastic Agent du lab dépend entièrement du proxy Squid (
10.100.10.4:3128) pour joindre AWS (aucun accès direct) : un changement de whitelist ou une panne du proxy casse silencieusement la collecte — voir Proxy Squid & DNS et son piège de sous-domaine déjà rencontré. - Aucun projet Terraform de l'infra (aws-s3 y compris) n'utilise le bucket
tfstatecomme backend distant : tous les states restent locaux à ce jour, y compris celui d'aws-s3lui-même. Voir Points de vigilance. aws-s3-siemn'a aucune trace git locale (git rev-parseéchoue, pas de.git) et n'existe pas non plus sur GitLab (indio/iac/aws-s3-siemintrouvable, vérifié) — contrairement àaws-s3, correctement versionné. Voir Points de vigilance.- Ne jamais recopier l'identifiant de compte AWS ni un ARN complet dans une future mise à jour de cette page (règle du projet suite à l'incident de fuite de clés TLS committées) — utiliser « compte AWS dédié Indio ».