Cómo conectar Django de forma segura a AWS S3 — Dos enfoques recomendados
Cuando despliegas aplicaciones Django en AWS, manejar el acceso a S3 de forma segura es fundamental. Dejar las credenciales expuestas en el código es un gran riesgo. Por suerte, AWS ofrece dos formas seguras y efectivas para autenticar tu backend cuando trabaja con S3:
🔐 Dos opciones seguras para acceder a S3 desde Django
Opción | Descripción | Ideal para | Seguridad |
---|---|---|---|
1. Variables de entorno en Elastic Beanstalk | Defines tus claves de AWS directamente como variables en EB | Configuración rápida, entornos de staging o desarrollo | Segura (si tu consola está protegida) |
2. IAM Role asignado a EC2 | EC2 recibe credenciales temporales automáticamente | Producción, cumplimiento normativo, escalabilidad | La mejor práctica |
Opción 1: Usar variables de entorno
1. El problema: Credenciales escritas en el código
Evita hacer esto en tu settings.py
:
AWS_ACCESS_KEY_ID = "AKIA123..."
AWS_SECRET_ACCESS_KEY = "abcd123..."
2. Cargar las credenciales desde variables de entorno
import os
AWS_ACCESS_KEY_ID = os.environ.get("AWS_ACCESS_KEY_ID")
AWS_SECRET_ACCESS_KEY = os.environ.get("AWS_SECRET_ACCESS_KEY")
AWS_STORAGE_BUCKET_NAME = os.environ.get("AWS_STORAGE_BUCKET_NAME")
3. Crear un script de carga: set_env.sh
#!/bin/bash
export AWS_ACCESS_KEY_ID="AKIA..."
export AWS_SECRET_ACCESS_KEY="abcd..."
export AWS_STORAGE_BUCKET_NAME="nombre-de-tu-bucket"
Luego:
chmod +x set_env.sh
4. Agregarlo a .gitignore
# Secretos del entorno
set_env.sh
5. Automatizar la carga al activar el entorno virtual
# Al final del archivo envs/bin/activate
source /ruta/completa/a/tu/proyecto/set_env.sh
6. Prueba final
source envs/bin/activate
echo $AWS_ACCESS_KEY_ID
💡 ¿Por qué es importante?
Las variables de entorno mantienen tus claves fuera del código y evitan filtraciones accidentales. Es una excelente solución para desarrollo o staging.
Opción 2: Usar IAM Roles (recomendado para producción)
Con IAM Roles, AWS gestiona la autenticación automáticamente mediante credenciales temporales, sin necesidad de guardar nada.
Paso 1: Crear un IAM Role
- Ve a AWS Console > IAM > Roles
- Haz clic en Crear rol
- Selecciona: AWS Service → EC2
- Asocia la política
AmazonS3FullAccess
(o una personalizada) - Asigna un nombre como
DjangoStarsUp-InstanceRole-S3Only
- Haz clic en Crear rol
Paso 2: Asignar el rol a Elastic Beanstalk
- Ve a Elastic Beanstalk > tu entorno > Configuration
- Edita la sección Service access
- Selecciona el rol creado en EC2 instance profile
- Haz clic en Apply
Problema común: Falla en el health check
Elastic Beanstalk valida la salud de las instancias accediendo a /
. Si Django no responde con 200 OK, marcará la instancia como fallida.
Paso 3: Agregar un endpoint de health check en Django
from django.http import JsonResponse
from django.urls import path
urlpatterns += [
path("health/", lambda request: JsonResponse({"status": "ok"})),
]
Paso 4: Cambiar el health check path en EB
- Ve a Configuration > Instance traffic and scaling
- Edita el proceso llamado
default
- Cambia el campo
Health check path
a/health/
- Guarda los cambios
Paso 5: Ajustar settings.py
para usar IAM Role
import os
AWS_STORAGE_BUCKET_NAME = os.environ.get("AWS_STORAGE_BUCKET_NAME")
AWS_ACCESS_KEY_ID = os.environ.get("AWS_ACCESS_KEY_ID", None)
AWS_SECRET_ACCESS_KEY = os.environ.get("AWS_SECRET_ACCESS_KEY", None)
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
AWS_S3_REGION_NAME = 'us-east-1'
AWS_QUERYSTRING_AUTH = False
if not AWS_ACCESS_KEY_ID or not AWS_SECRET_ACCESS_KEY:
print("Usando IAM Role desde el perfil de instancia EC2")
🔧 Resolución de Problemas Real: Actualizar el IAM Role sin Romper el Entorno
Cuando cambias el EC2 Instance Profile en Elastic Beanstalk a un nuevo IAM Role (por ejemplo, DjangoStarsUp-InstanceRole-S3Only), tu entorno entra en modo de actualización continua (rolling update). Esto hace que se lance una nueva instancia EC2 con el nuevo perfil.
Este proceso puede fallar si no se prepara correctamente:
❗ Problema común: Error 4xx y estado “Severe”
Si tu archivo settings.py en Django no incluye la IP privada de la nueva instancia EC2 dentro de ALLOWED_HOSTS, entonces Django rechazará las peticiones del Load Balancer, y Elastic Beanstalk marcará la instancia como “unhealthy”.
Solución Comprobada (Usada en Producción)
Paso 1: Permitir todas las IPs temporalmente
Antes de cambiar el IAM Role, comenta tu lista de ALLOWED_HOSTS y reemplázala por:
python Copy EditALLOWED_HOSTS = ['*']
Esto permitirá que cualquier origen acceda temporalmente, evitando errores mientras actualizas.
Paso 2: Ejecuta eb deploy
Esto iniciará la nueva instancia EC2 con el nuevo IAM Role, y te permitirá verla activa en el panel de EC2 o ver sus logs para obtener la IP.
Paso 3: Agrega la nueva IP privada a ALLOWED_HOSTS
Ejemplo:
python Copy EditALLOWED_HOSTS = [
'127.0.0.1',
'localhost',
'su-env.us-east-2.elasticbeanstalk.com',
'tu-dominio.com',
'api.tu-dominio.com',
'abcd.cloudfront.net',
'172.31.XX.XX', # IP privada de la nueva EC2
]
Puedes obtener esta IP ejecutando dentro de la instancia (vía SSH):
bash Copy Editcurl http://169.254.169.254/latest/meta-data/local-ipv4
Paso 4: Haz un nuevo eb deploy
Una vez que hayas agregado la IP correctamente, puedes quitar el ['*'] y dejar ALLOWED_HOSTS configurado de forma segura.
Verifica si tu app usa IAM Role o variables de entorno
Para confirmar si boto3 está usando el IAM Role y no variables de entorno, entra por SSH a la instancia EC2 y ejecuta:
env | grep AWS
Si no aparece ninguna variable, significa que boto3 está usando correctamente las credenciales temporales generadas por el IAM Role.
Permisos mínimos recomendados para el IAM Role
Además del acceso a S3, Elastic Beanstalk necesita permisos para operar correctamente. Agrega esta política al rol:
json Copy Edit[
"cloudwatch:PutMetricData",
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"ec2:DescribeInstances",
"elasticloadbalancing:DescribeTargetHealth"
]
Puedes omitir permisos relacionados con contenedores como ecr:* si no usas Docker.
Resultado final
Tu backend Django ahora se conecta a S3 de forma totalmente segura:
- Sin claves en el código ni en disco
- Con credenciales rotadas automáticamente
- Escalable y en línea con buenas prácticas de AWS
- Con un health check funcional para EB
Política personalizada de ejemplo
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject", "s3:ListBucket"],
"Resource": [
"arn:aws:s3:::nombre-de-tu-bucket",
"arn:aws:s3:::nombre-de-tu-bucket/*"
]
}
]
}
¿Qué sigue?
En la Parte 3 te mostraremos cómo almacenar secretos y claves de forma segura usando AWS Secrets Manager en producción.