Distribuer du contenu S3 privé avec CloudFront

AWSBeginner
Pratiquer maintenant

Introduction

Votre équipe veut permettre aux clients de lire une version via CloudFront tout en gardant son origine S3 privée. Chargez la page fournie, connectez une distribution et un contrôle d’accès à l’origine, puis autorisez uniquement cette distribution à lire les objets. Testez le chemin client fonctionnel et le chemin direct vers l’origine refusé.

Terminez d’abord AWS Foundations, les opérations sur les objets S3 et les concepts de politiques de ressources IAM. Cette VM indépendante fournit un accès AWS CLI configuré, index.html et un compartiment de référence distinct. Observez la distribution et l’origine dans AWS View, en haut, et travaillez dans Terminal, en bas. Aucun compte AWS personnel ni domaine public n’est nécessaire. Le laboratoire utilise une origine S3 classique ; le cache côté client et HTTPS seront abordés séparément.

Lien avec la certification

Certification Tâche de l’examen Pratique
Solutions Architect – Associate (SAA-C03) Tâche 1.1 Utiliser une politique de ressources pour limiter les lectures S3 à la distribution CloudFront prévue.

Aperçu du laboratoire

Schéma conceptuel : le client contacte CloudFront, qui peut lire l’origine S3 privée ; une requête anonyme directe vers l’origine est refusée.

Préparer le contenu de l’origine privée

Dans cette étape, créez un compartiment S3 privé et chargez la page préparée.

L’origine stocke le contenu récupéré par CloudFront. Un viewer est un client demandant du contenu à CloudFront. L’accès client et l’accès à l’origine relèvent de permissions différentes : une page peut être lisible via la distribution alors que les lectures S3 anonymes directes restent refusées.

Travaillez dans le répertoire du projet fourni. Conservez le compartiment de référence labex-n02-reference intact :

cd /home/labex/project
cat index.html
aws s3api create-bucket \
  --bucket labex-n02-content

Définissez la propriété des objets sur Bucket owner enforced. Le propriétaire du compartiment conserve la propriété et les autorisations par ACL sont désactivées ; OAC utilise la politique du compartiment. Activez les quatre protections Block Public Access pour empêcher les autorisations publiques par ACL ou politique :

aws s3api put-bucket-ownership-controls \
  --bucket labex-n02-content \
  --ownership-controls '{"Rules":[{"ObjectOwnership":"BucketOwnerEnforced"}]}'
aws s3api put-public-access-block \
  --bucket labex-n02-content \
  --public-access-block-configuration '{"BlockPublicAcls":true,"IgnorePublicAcls":true,"BlockPublicPolicy":true,"RestrictPublicBuckets":true}'

Chargez la page fournie. --content-type text/html décrit l’objet comme un document HTML :

aws s3api put-object \
  --bucket labex-n02-content \
  --key index.html \
  --body index.html \
  --content-type text/html
aws s3api head-object \
  --bucket labex-n02-content \
  --key index.html

Examinez la taille de l’objet et ContentType. La requête CLI est authentifiée avec l’opérateur configuré. Comparez-la à une requête HTTP anonyme, sans identifiants AWS :

curl --noproxy '*' \
  --output /dev/null \
  --write-out 'Direct origin: HTTP %{http_code}\n' \
  http://127.0.0.1:5000/labex-n02-content/index.html

Attendez Direct origin: HTTP 403. --output /dev/null ignore le corps de l’erreur ; --write-out affiche le statut HTTP. Ce point d’accès explicite d’exercice teste l’accès direct à l’objet S3 sans rendre le compartiment public. Lancez la vérification de l’origine privée.

Exemple après chargement : l’objet d’origine de 91 octets apparaît à côté de la référence intacte ; aucune distribution n’est encore créée.

Connecter la distribution à son origine

Dans cette étape, reliez une distribution CloudFront au compartiment S3 et observez que la connexion seule ne donne pas de permission sur l’origine.

Un contrôle d’accès à l’origine (OAC) indique comment CloudFront authentifie ses requêtes vers l’origine. Choisissez S3, Signature Version 4 et la signature always. Écrivez sa configuration CLI classique :

cat > oac.json <<'JSON'
{
  "Name": "labex-n02-oac",
  "Description": "Read the private release origin",
  "SigningProtocol": "sigv4",
  "SigningBehavior": "always",
  "OriginAccessControlOriginType": "s3"
}
JSON
OAC_ID=$(aws cloudfront create-origin-access-control \
  --origin-access-control-config file://oac.json \
  --query OriginAccessControl.Id \
  --output text)

$(...) enregistre l’ID d’OAC retourné dans OAC_ID. --query sélectionne uniquement cet ID pour la configuration suivante. Gardez ce Terminal ouvert durant le laboratoire.

La configuration relie content-origin au point d’accès S3 classique du compartiment, et non à un point d’accès de site web S3. TargetOriginId choisit cette origine ; DefaultRootObject associe / à index.html. Le bloc ancien ForwardedValues évite de transmettre cookies ou chaînes de requête. Tous les TTL sont nuls pour que les tests de permissions ne réutilisent pas un succès en cache. allow-all autorise la requête HTTP client utilisée ici ; HTTPS sera enseigné séparément.

Le here-document suivant est sans guillemets : le shell remplace donc $OAC_ID dans le fichier JSON :

cat > distribution.json <<JSON
{
  "CallerReference": "labex-n02-release",
  "Comment": "labex-n02:private-content",
  "Enabled": true,
  "DefaultRootObject": "index.html",
  "Origins": {
    "Quantity": 1,
    "Items": [{
      "Id": "content-origin",
      "DomainName": "labex-n02-content.s3.amazonaws.com",
      "S3OriginConfig": {"OriginAccessIdentity": ""},
      "OriginAccessControlId": "$OAC_ID"
    }]
  },
  "DefaultCacheBehavior": {
    "TargetOriginId": "content-origin",
    "ViewerProtocolPolicy": "allow-all",
    "TrustedSigners": {"Enabled": false, "Quantity": 0},
    "ForwardedValues": {"QueryString": false, "Cookies": {"Forward": "none"}},
    "MinTTL": 0,
    "DefaultTTL": 0,
    "MaxTTL": 0
  }
}
JSON
DIST_ID=$(aws cloudfront create-distribution \
  --distribution-config file://distribution.json \
  --query Distribution.Id \
  --output text)
DIST_DOMAIN=$(aws cloudfront get-distribution \
  --id "$DIST_ID" \
  --query Distribution.DomainName \
  --output text)

Examinez l’origine connectée :

aws cloudfront get-distribution-config \
  --id "$DIST_ID" \
  --query DistributionConfig.Origins

Le domaine S3 et OriginAccessControlId doivent correspondre à votre compartiment et à votre OAC. Attendez le déploiement du plan de contrôle avant le test. Le waiter officiel lit plusieurs fois le statut sans produire de trafic client :

aws cloudfront wait distribution-deployed \
  --id "$DIST_ID"

Testez maintenant le chemin client. --resolve envoie ce nom exact de distribution et le port d’exercice vers le point de diffusion fourni dans la VM ; il ne modifie pas le DNS système et n’enregistre aucun domaine :

curl --noproxy '*' \
  --resolve "${DIST_DOMAIN}:8082:127.0.0.1" \
  --output /dev/null \
  --write-out 'Viewer before permission: HTTP %{http_code}\n' \
  "http://${DIST_DOMAIN}:8082/index.html"

Attendez HTTP 403. La création d’une distribution et la sélection d’un OAC décrivent une connexion ; S3 a encore besoin d’une politique autorisant cette distribution. Ne rendez pas le compartiment public pour corriger le résultat. Lancez la vérification de connexion.

Exemple avant l’autorisation d’origine : la distribution connectée retourne HTTP 403 à la requête réelle du client.

Autoriser la distribution prévue

Dans cette étape, permettez à la distribution CloudFront sélectionnée de lire les objets tout en maintenant le refus des lectures anonymes directes.

Un ARN identifie une ressource AWS et son compte. Lisez l’ARN de votre distribution :

DIST_ARN=$(aws cloudfront get-distribution \
  --id "$DIST_ID" \
  --query Distribution.ARN \
  --output text)

La politique du compartiment désigne cloudfront.amazonaws.com comme principal de service, n’autorise que s3:GetObject et limite l’autorisation aux objets de votre compartiment. La condition AWS:SourceArn restreint la requête du service à votre distribution précise. Elle diffère d’une politique publique avec Principal: "*". Écrivez-la avec l’ARN obtenu :

cat > bucket-policy.json <<JSON
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {"Service": "cloudfront.amazonaws.com"},
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::labex-n02-content/*",
    "Condition": {"StringEquals": {"AWS:SourceArn": "$DIST_ARN"}}
  }]
}
JSON
aws s3api put-bucket-policy \
  --bucket labex-n02-content \
  --policy file://bucket-policy.json

Demandez à nouveau l’objet. --fail fait désormais échouer la commande pour un statut HTTP non réussi, et --include affiche les en-têtes avec le document réel :

curl --fail --include --noproxy '*' \
  --resolve "${DIST_DOMAIN}:8082:127.0.0.1" \
  "http://${DIST_DOMAIN}:8082/index.html"

Attendez HTTP 200, le type text/html et une page contenant Release one. Vous avez observé les octets de l’objet via la distribution, au-delà d’une simple réponse de configuration réussie.

Répétez immédiatement le test anonyme direct vers l’origine :

curl --noproxy '*' \
  --output /dev/null \
  --write-out 'Direct origin after viewer success: HTTP %{http_code}\n' \
  http://127.0.0.1:5000/labex-n02-content/index.html

Il doit toujours retourner HTTP 403. Le chemin client fonctionne et l’origine directe reste privée. AWS View affiche l’origine connectée et le dernier résultat client. Lancez la vérification d’accès.

Exemple après autorisation : la requête réelle du client retourne HTTP 200 via la distribution connectée.

Supprimer uniquement vos ressources de diffusion

Dans cette étape, désactivez et supprimez la distribution avant son OAC et le contenu S3. Conservez le compartiment de référence intact.

CloudFront utilise un ETag comme jeton de version pour les changements de configuration. Récupérez la configuration actuelle et son ETag sans deviner le jeton :

aws cloudfront get-distribution-config \
  --id "$DIST_ID" \
  --query DistributionConfig \
  --output json > distribution-current.json
DIST_ETAG=$(aws cloudfront get-distribution-config \
  --id "$DIST_ID" \
  --query ETag \
  --output text)

Dans cette configuration, la propriété Enabled de la distribution est la seule de ce nom valant true. La substitution standard avec sed produit une copie désactivée en conservant les paramètres d’origine :

sed 's/"Enabled": true/"Enabled": false/' distribution-current.json > distribution-disabled.json
aws cloudfront update-distribution \
  --id "$DIST_ID" \
  --if-match "$DIST_ETAG" \
  --distribution-config file://distribution-disabled.json

Attendez le déploiement de la configuration désactivée. Sur AWS, la propagation peut prendre du temps ; l’exercice ne mesure pas la latence mondiale de déploiement :

aws cloudfront wait distribution-deployed \
  --id "$DIST_ID"

La mise à jour change l’ETag. Lisez le jeton le plus récent avant de supprimer la distribution désactivée :

DIST_ETAG=$(aws cloudfront get-distribution-config \
  --id "$DIST_ID" \
  --query ETag \
  --output text)
aws cloudfront delete-distribution \
  --id "$DIST_ID" \
  --if-match "$DIST_ETAG"

Supprimez l’OAC avec son propre ETag. L’ID de distribution et celui d’OAC identifient des ressources différentes :

OAC_ETAG=$(aws cloudfront get-origin-access-control \
  --id "$OAC_ID" \
  --query ETag \
  --output text)
aws cloudfront delete-origin-access-control \
  --id "$OAC_ID" \
  --if-match "$OAC_ETAG"

Supprimez uniquement l’objet et le compartiment que vous avez créés :

aws s3api delete-object \
  --bucket labex-n02-content \
  --key index.html
aws s3api delete-bucket \
  --bucket labex-n02-content
aws s3api list-buckets \
  --query Buckets[].Name

Le compartiment de référence doit rester, celui de contenu doit être absent. AWS View ne doit afficher aucune distribution de contenu et conserver uniquement l’objet de référence. Lancez la vérification du nettoyage. Une requête API échouée ne prouve pas la suppression d’une ressource.

Exemple après nettoyage : la distribution et le compartiment de contenu ont disparu ; l’objet de référence indépendant reste présent.

Résumé

Vous avez relié une distribution à une origine S3 privée classique, configuré des requêtes OAC toujours signées et autorisé uniquement la distribution prévue à lire. Les tests HTTP réels ont distingué l’accès client autorisé de l’accès anonyme à l’origine refusé. Vous avez ensuite utilisé les ETags actuels pour désactiver et supprimer vos ressources en conservant le contenu indépendant. Observez ensuite la réutilisation du cache et invalidez un objet mis à jour.