Bucket Policies: Controle de Acesso em Escala
As Bucket Policies são documentos JSON que definem permissões granulares para recursos no S3. Diferente das ACLs (Access Control Lists), elas oferecem controle declarativo e escalável sobre quem pode fazer o quê. Em produção, uma política bem estruturada é essencial para evitar vazamento de dados e garantir conformidade regulatória.
Uma política segue a estrutura padrão: Principal (quem), Action (o quê), Resource (onde) e Effect (permitir/negar). O exemplo abaixo demonstra uma política que permite acesso público apenas a objetos dentro de um prefixo específico:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::meu-bucket/publico/*"
},
{
"Sid": "DenyUnencryptedObjectUploads",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::meu-bucket/*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "AES256"
}
}
}
]
}
Na prática, sempre negue por padrão e permita explicitamente. Use condições para forçar HTTPS, criptografia obrigatória e limitar IPs. Nunca deixe um bucket inteiro público sem motivo comercial explícito — a maioria dos vazamentos ocorre por falhas de configuração, não por ataques sofisticados.
Versionamento: Proteção contra Exclusões e Rollbacks
Ativando e Entendendo Versioning
O Versioning permite que cada objeto no S3 mantenha múltiplas versões. Quando ativado, exclusões não deletam o objeto — apenas marcam-no como deletado com um delete marker. Isso é crítico em produção para recuperação de dados e auditoria.
import boto3
s3_client = boto3.client('s3')
# Ativar versionamento
bucket_name = 'meu-bucket-producao'
s3_client.put_bucket_versioning(
Bucket=bucket_name,
VersioningConfiguration={'Status': 'Enabled'}
)
# Fazer upload e recuperar histórico de versões
response = s3_client.put_object(
Bucket=bucket_name,
Key='config/database.json',
Body=b'{"version": 1}'
)
version_id_v1 = response['VersionId']
# Atualizar o arquivo
response = s3_client.put_object(
Bucket=bucket_name,
Key='config/database.json',
Body=b'{"version": 2}'
)
version_id_v2 = response['VersionId']
# Listar todas as versões
versions = s3_client.list_object_versions(Bucket=bucket_name)
for version in versions.get('Versions', []):
print(f"Key: {version['Key']}, VersionId: {version['VersionId']}, IsLatest: {version['IsLatest']}")
# Restaurar versão anterior
s3_client.get_object(
Bucket=bucket_name,
Key='config/database.json',
VersionId=version_id_v1
)
Custos e Gerenciamento
Versionamento aumenta o consumo de armazenamento porque todas as versões são mantidas. Em produção, combine sempre com Lifecycle Rules para arquivar ou deletar versões antigas automaticamente. Sem isso, seus custos podem crescer exponencialmente.
Lifecycle Rules: Automatização de Retenção e Economia
Configurando Regras de Ciclo de Vida
As Lifecycle Rules automatizam transições entre classes de armazenamento e exclusão de objetos com base em data de criação ou age. Uma estratégia típica em produção é: 30 dias em STANDARD, 90 dias em STANDARD_IA, depois GLACIER, e exclusão após 1 ano.
lifecycle_policy = {
'Rules': [
{
'Id': 'ArchiveOldVersions',
'Filter': {'Prefix': 'logs/'},
'NoncurrentVersionTransitions': [
{
'NoncurrentDays': 30,
'StorageClass': 'STANDARD_IA'
},
{
'NoncurrentDays': 90,
'StorageClass': 'GLACIER'
}
],
'NoncurrentVersionExpiration': {
'NoncurrentDays': 365
},
'Status': 'Enabled'
},
{
'Id': 'DeleteIncompleteMultipartUploads',
'Filter': {},
'AbortIncompleteMultipartUpload': {
'DaysAfterInitiation': 7
},
'Status': 'Enabled'
},
{
'Id': 'TransitionCurrentVersion',
'Filter': {'Prefix': 'dados-frios/'},
'Transitions': [
{
'Days': 30,
'StorageClass': 'INTELLIGENT_TIERING'
}
],
'Expiration': {
'Days': 730
},
'Status': 'Enabled'
}
]
}
s3_client.put_bucket_lifecycle_configuration(
Bucket=bucket_name,
LifecycleConfiguration=lifecycle_policy
)
Boas Práticas em Produção
Aplique regras diferentes por prefixo — logs e backups têm retenção distinta de dados operacionais. Sempre teste com um bucket não-crítico primeiro. Use AbortIncompleteMultipartUpload para limpar uploads falhos (economiza 10-15% em muitos cenários). Monitore transições via CloudTrail e CloudWatch para garantir que as regras operam como esperado.
Integração Completa: Um Caso Real
def setup_s3_production(bucket_name):
"""Configure S3 bucket como padrão de produção"""
s3 = boto3.client('s3')
# 1. Ativar versionamento
s3.put_bucket_versioning(
Bucket=bucket_name,
VersioningConfiguration={'Status': 'Enabled'}
)
# 2. Aplicar bucket policy restrictiva
policy = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
f"arn:aws:s3:::{bucket_name}",
f"arn:aws:s3:::{bucket_name}/*"
],
"Condition": {"Bool": {"aws:SecureTransport": "false"}}
}
]
}
s3.put_bucket_policy(Bucket=bucket_name, Policy=json.dumps(policy))
# 3. Configurar lifecycles
s3.put_bucket_lifecycle_configuration(
Bucket=bucket_name,
LifecycleConfiguration={
'Rules': [{
'Id': 'ProdRule',
'Filter': {},
'NoncurrentVersionExpiration': {'NoncurrentDays': 90},
'AbortIncompleteMultipartUpload': {'DaysAfterInitiation': 7},
'Status': 'Enabled'
}]
}
)
print(f"✓ Bucket {bucket_name} configurado para produção")
setup_s3_production('meu-bucket-producao')
Conclusão
Dominar S3 em produção exige três competências fundamentais: (1) Bucket Policies garantem segurança granular — nunca confie em configurações padrão, sempre negue e permita explicitamente; (2) Versionamento protege contra exclusões acidentais e oferece auditoria completa — combine obrigatoriamente com Lifecycle Rules para evitar custos galopantes; (3) Lifecycle Rules automatizam retenção e economia — uma estratégia de tiers (STANDARD → IA → GLACIER → exclusão) pode reduzir custos em até 70%. A maioria dos problemas em produção ocorre por configuração inadequada, não por limitações técnicas. Teste suas políticas, documente-as e revise trimestralmente.