Introducción
Tu equipo quiere que los clientes lean una versión mediante CloudFront manteniendo privado su origen S3. Sube la página proporcionada, conecta una distribución y un control de acceso al origen, y concede solo a esa distribución permiso para leer los objetos. Prueba la ruta del cliente que funciona y la ruta directa al origen que se deniega.
Completa primero AWS Foundations, las operaciones de objetos S3 y los conceptos de políticas de recursos de IAM. Esta VM independiente proporciona acceso AWS CLI configurado, index.html y un bucket de referencia separado. Observa la distribución y el origen en AWS View, arriba, y trabaja en Terminal, abajo. No necesitas una cuenta AWS personal ni un dominio público. Se utiliza un origen S3 normal; la caché del cliente y HTTPS se enseñan en otros laboratorios.
Relevancia para la certificación
| Certificación | Tarea del examen | Práctica |
|---|---|---|
| Solutions Architect – Associate (SAA-C03) | Tarea 1.1 | Utilizar una política de recursos para limitar las lecturas de S3 a la distribución CloudFront prevista. |
Vista general del laboratorio

Preparar el contenido del origen privado
En este paso, crea un bucket S3 privado y sube la página preparada.
El origen almacena el contenido que obtiene CloudFront. Un viewer es un cliente que solicita contenido a CloudFront. El acceso del cliente y el del origen son permisos distintos: una página puede leerse mediante la distribución mientras las lecturas anónimas directas de S3 siguen denegadas.
Trabaja en el directorio del proyecto proporcionado. Mantén intacto el bucket de referencia labex-n02-reference:
cd /home/labex/project
cat index.html
aws s3api create-bucket \
--bucket labex-n02-content
Establece la propiedad de objetos Bucket owner enforced. La propiedad queda con el propietario del bucket y se desactivan las concesiones mediante ACL; OAC utiliza la política del bucket. Activa las cuatro protecciones Block Public Access para impedir concesiones públicas mediante ACL o políticas:
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}'
Sube la página proporcionada. --content-type text/html identifica el objeto como documento 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
Inspecciona el tamaño del objeto y ContentType. La solicitud CLI está autenticada con el operador configurado. Compárala con una solicitud HTTP anónima, que no proporciona credenciales 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
Espera Direct origin: HTTP 403. --output /dev/null descarta el cuerpo del error y --write-out imprime el estado HTTP. Este endpoint explícito de práctica comprueba el acceso directo al objeto S3; no hace público el bucket. Ejecuta la comprobación del origen privado.

Conectar la distribución con su origen
En este paso, conecta una distribución CloudFront al bucket S3 y observa que la conexión por sí sola no concede permiso sobre el origen.
Un control de acceso al origen (OAC) indica a CloudFront cómo autenticar sus solicitudes al origen. Selecciona S3, Signature Version 4 y firma always. Escribe su configuración CLI normal:
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)
$(...) guarda el ID de OAC devuelto en OAC_ID. --query selecciona únicamente el ID para referenciarlo en la siguiente configuración. Mantén este Terminal abierto durante todo el laboratorio.
La configuración conecta content-origin con el endpoint normal del bucket S3, no con uno de sitio web S3. TargetOriginId selecciona ese origen; DefaultRootObject asigna / a index.html. El bloque heredado ForwardedValues evita reenviar cookies o cadenas de consulta. Todos los TTL son cero para que la prueba de permisos no reutilice un éxito en caché. allow-all permite la solicitud HTTP del cliente utilizada aquí; HTTPS se enseña por separado.
El siguiente here-document está sin comillas, por lo que el shell expande $OAC_ID en el archivo 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)
Inspecciona el origen conectado:
aws cloudfront get-distribution-config \
--id "$DIST_ID" \
--query DistributionConfig.Origins
El dominio S3 y OriginAccessControlId deben corresponder al bucket y al OAC. Espera el despliegue del plano de control antes de probar la distribución. El waiter oficial consulta repetidamente el estado; no genera tráfico de clientes:
aws cloudfront wait distribution-deployed \
--id "$DIST_ID"
Prueba ahora la ruta del cliente. --resolve dirige este nombre exacto de distribución y el puerto de práctica al endpoint de entrega proporcionado en la VM; no modifica el DNS del sistema ni registra un dominio:
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"
Espera HTTP 403. Crear la distribución y seleccionar un OAC describe una conexión; S3 todavía necesita una política que conceda acceso a esa distribución. No hagas público el bucket para corregirlo. Ejecuta la comprobación de conexión.

Conceder acceso a la distribución prevista
En este paso, permite que la distribución CloudFront seleccionada lea los objetos, manteniendo denegado el acceso anónimo directo al origen.
Un ARN identifica un recurso AWS y su cuenta. Lee el ARN de la distribución:
DIST_ARN=$(aws cloudfront get-distribution \
--id "$DIST_ID" \
--query Distribution.ARN \
--output text)
La política del bucket identifica cloudfront.amazonaws.com como principal de servicio, permite solo s3:GetObject y limita la concesión a los objetos del bucket. La condición AWS:SourceArn restringe la solicitud del servicio a tu distribución concreta. Es distinta de una política pública con Principal: "*". Escríbela con el ARN obtenido:
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
Solicita el objeto otra vez. Ahora --fail hace fallar el comando ante un estado HTTP no satisfactorio y --include muestra las cabeceras junto al documento real:
curl --fail --include --noproxy '*' \
--resolve "${DIST_DOMAIN}:8082:127.0.0.1" \
"http://${DIST_DOMAIN}:8082/index.html"
Espera HTTP 200, tipo de contenido text/html y una página que contiene Release one. Has observado los bytes del objeto a través de la distribución, además de una respuesta de configuración satisfactoria.
Repite inmediatamente la prueba anónima directa al origen:
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
Debe seguir devolviendo HTTP 403. La ruta del cliente funciona y el origen directo permanece privado. AWS View muestra el origen conectado y el último resultado del cliente. Ejecuta la comprobación de acceso.

Eliminar únicamente tus recursos de entrega
En este paso, desactiva y elimina la distribución antes de eliminar su OAC y contenido S3. Mantén intacto el bucket de referencia.
CloudFront utiliza un ETag como token de versión para los cambios de configuración. Obtén la configuración y su ETag actuales sin adivinar el token:
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)
En esta configuración, Enabled de la distribución es la única propiedad de ese nombre con valor true. La sustitución estándar con sed crea una copia desactivada y conserva la configuración del origen:
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
Espera el despliegue de la configuración desactivada. En AWS la propagación puede llevar tiempo; este ejercicio no mide la latencia global de despliegue:
aws cloudfront wait distribution-deployed \
--id "$DIST_ID"
La actualización cambia el ETag. Lee el token más reciente antes de eliminar la distribución desactivada:
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"
Elimina el OAC con su propio ETag. El ID de distribución y el ID de OAC identifican recursos distintos:
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"
Elimina solo el objeto y el bucket que creaste:
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
El bucket de referencia debe permanecer y el de contenido debe estar ausente. AWS View debe mostrar ninguna distribución de contenido y solo el objeto de referencia. Ejecuta la comprobación de limpieza. Una solicitud API fallida no demuestra que se haya eliminado un recurso.

Resumen
Conectaste una distribución con un origen S3 privado normal, configuraste solicitudes OAC siempre firmadas y concediste lectura únicamente a la distribución prevista. Las pruebas HTTP reales distinguieron el acceso permitido del cliente del acceso anónimo al origen denegado. Después usaste ETags actuales para desactivar y eliminar los recursos propios, conservando el contenido ajeno. A continuación, observa la reutilización de caché e invalida un objeto actualizado.



