Pular para conteúdo

RFC-010: Secrets Management Strategy

Campo Valor
Status Draft
Author Time de Tecnologia
Created 2026-02-05
Updated 2026-02-05
Reunião A ser agendada
Deciders Todo o time técnico

Status

🟡 Draft - RFC em elaboração, aguardando apresentação

Contexto e Problema

AWS Secrets Manager documentado em docs/security/secrets-management.md, mas falta:

  • Rotação automática de secrets
  • Acesso granular (IAM policies específicas)
  • Secrets para desenvolvimento local (processo claro)
  • Auditoria de acesso a secrets
  • Emergency access procedures

Por que resolver isso agora?

  • Secrets não são rotacionados regularmente
  • Risco de secrets comprometidos não detectados
  • Developers têm acesso excessivo
  • Processo manual e propenso a erros
  • Falta auditoria de quem acessa o quê

Impacto de não resolver

  • Secrets comprometidos não rotacionados
  • Acesso não autorizado a sistemas críticos
  • Violation de compliance
  • Dificuldade em revogar acesso
  • Secrets hardcoded em código

Documentação relacionada: - docs/security/secrets-management.md - Processo atual - docs/infrastructure/iam-policies.md - Políticas de acesso - docs/development/local-development.md - Setup local - docs/security/secure-coding.md - Nunca commitar secrets

Proposta de Solução

Secrets management com AWS Secrets Manager, rotação automática, e least privilege access.

Hierarquia de Secrets

AWS Secrets Manager (Production): - Database credentials - API keys externas - JWT signing keys - Third-party service credentials - Rotação automática habilitada

AWS SSM Parameter Store (Configuration): - URLs de serviços - Feature flags - Configurações não-sensíveis - Valores públicos

Environment Variables (.env.local): - Desenvolvimento local apenas - Nunca commitado - .env.example com template

Naming Convention

# Secrets Manager
/prod/database/master-password
/prod/api-keys/stripe-secret
/prod/jwt/signing-key
/staging/database/master-password
/staging/api-keys/stripe-test-key

# SSM Parameter Store
/prod/config/api-base-url
/prod/config/max-connections
/staging/config/api-base-url

Rotação Automática

Database credentials (a cada 90 dias):

# Lambda function para rotação
import boto3
import json

secrets = boto3.client('secretsmanager')
rds = boto3.client('rds')

def lambda_handler(event, context):
    """Rotate RDS master password."""
    secret_arn = event['SecretId']
    token = event['ClientRequestToken']
    step = event['Step']

    if step == "createSecret":
        # Gerar nova senha
        new_password = generate_secure_password()

        # Criar nova versão do secret
        secrets.put_secret_value(
            SecretId=secret_arn,
            ClientRequestToken=token,
            SecretString=json.dumps({
                "password": new_password
            }),
            VersionStages=['AWSPENDING']
        )

    elif step == "setSecret":
        # Atualizar senha no RDS
        rds.modify_db_instance(
            DBInstanceIdentifier='prod-db',
            MasterUserPassword=new_password,
            ApplyImmediately=True
        )

    elif step == "testSecret":
        # Testar conexão com nova senha
        test_db_connection(new_password)

    elif step == "finishSecret":
        # Marcar nova versão como CURRENT
        secrets.update_secret_version_stage(
            SecretId=secret_arn,
            VersionStage='AWSCURRENT',
            MoveToVersionId=token
        )

def generate_secure_password():
    """Generate 32-char secure password."""
    import secrets
    import string
    alphabet = string.ascii_letters + string.digits + string.punctuation
    return ''.join(secrets.choice(alphabet) for _ in range(32))

API keys (rotação manual com checklist):

  1. Gerar nova key no serviço externo
  2. Adicionar nova key ao Secrets Manager (versão PENDING)
  3. Deploy código que tenta PENDING, fallback para CURRENT
  4. Validar funcionamento
  5. Marcar PENDING como CURRENT
  6. Revogar key antiga no serviço externo

Acesso Granular (IAM)

Lambda functions (least privilege):

# SAM template
UserFunctionRole:
  Type: AWS::IAM::Role
  Properties:
    Policies:
      - PolicyName: SecretsAccess
        PolicyDocument:
          Statement:
            - Effect: Allow
              Action:
                - secretsmanager:GetSecretValue
              Resource:
                - !Sub 'arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:/prod/database/*'
                - !Sub 'arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:/prod/jwt/*'
              # Não tem acesso a API keys de third-party services

PaymentFunctionRole:
  Type: AWS::IAM::Role
  Properties:
    Policies:
      - PolicyName: SecretsAccess
        PolicyDocument:
          Statement:
            - Effect: Allow
              Action:
                - secretsmanager:GetSecretValue
              Resource:
                - !Sub 'arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:/prod/api-keys/stripe-*'
              # Apenas Stripe, não tem acesso a outros secrets

Developers (read-only staging):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue",
        "secretsmanager:DescribeSecret"
      ],
      "Resource": "arn:aws:secretsmanager:*:*:secret:/staging/*"
    },
    {
      "Effect": "Deny",
      "Action": "secretsmanager:*",
      "Resource": "arn:aws:secretsmanager:*:*:secret:/prod/*"
    }
  ]
}

DevOps/Tech Lead (production access):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:*"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:RequestedRegion": "us-east-1"
        }
      }
    }
  ]
}

Desenvolvimento Local

Setup process:

# 1. Criar .env.local (nunca commitado)
cp .env.example .env.local

# 2. Obter secrets do staging (se tem acesso)
aws secretsmanager get-secret-value \
  --secret-id /staging/database/master-password \
  --query SecretString \
  --output text > .secrets

# 3. Ou usar valores de desenvolvimento
# .env.local
DATABASE_URL=postgresql://user:pass@localhost:5432/dev_db
JWT_SECRET=dev_secret_key_not_for_production
STRIPE_KEY=sk_test_xxxxx  # Test key, ok para dev

.gitignore obrigatório:

.env.local
.env.*.local
.secrets
*.pem
*.key
credentials.json

Pre-commit hook (detectar secrets):

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/Yelp/detect-secrets
    rev: v1.4.0
    hooks:
      - id: detect-secrets
        args: ['--baseline', '.secrets.baseline']

Auditoria

CloudTrail logging:

# Habilitar CloudTrail para Secrets Manager
AuditTrail:
  Type: AWS::CloudTrail::Trail
  Properties:
    EventSelectors:
      - DataResources:
          - Type: AWS::SecretsManager::Secret
            Values:
              - !Sub 'arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:*'
        ReadWriteType: All
    S3BucketName: !Ref AuditBucket

Alertas para acesso suspeito:

# Lambda function processando CloudTrail logs
def detect_suspicious_access(event):
    """Alert on suspicious secrets access."""

    # Alert se:
    # - Acesso fora de horário comercial
    # - Acesso de IP não reconhecido
    # - Multiple failed attempts
    # - Acesso a prod secrets por dev account

    if is_suspicious(event):
        send_alert_to_security_team(event)

Emergency Access

Break-glass procedure:

  1. Declarar emergência (P0 incident)
  2. Obter aprovação (Tech Lead ou superior)
  3. Usar role especial (emergency-access role)
  4. Executar ação necessária
  5. Documentar tudo em incident log
  6. Revogar acesso imediatamente após
  7. Audit de todas as ações tomadas
  8. Postmortem obrigatório
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789:role/EmergencyAccessRole"
      },
      "Action": "secretsmanager:*",
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:PrincipalTag/Emergency": "true"
        }
      }
    }
  ]
}

Accessing Secrets no Código

Python (FastAPI/Django):

import boto3
import json
from functools import lru_cache

secrets_client = boto3.client('secretsmanager')

@lru_cache(maxsize=128)
def get_secret(secret_name: str) -> dict:
    """Get secret from Secrets Manager with caching."""
    try:
        response = secrets_client.get_secret_value(SecretId=secret_name)
        return json.loads(response['SecretString'])
    except ClientError as e:
        logger.error(f"Failed to get secret {secret_name}: {e}")
        raise

# Uso
db_creds = get_secret('/prod/database/master-password')
DATABASE_URL = f"postgresql://{db_creds['username']}:{db_creds['password']}@{db_creds['host']}/db"

Evitar:

# ❌ Nunca hardcode
API_KEY = "sk_live_abc123..."

# ❌ Nunca em environment variables (Lambda)
# Use Secrets Manager integration do Lambda

# ❌ Nunca em logs
logger.info(f"Using API key: {api_key}")  # BAD

# ✅ Correto
logger.info("API key loaded successfully")  # GOOD

Alternativas Consideradas

Opção 1: HashiCorp Vault

Prós: - Feature-rich - Open source - Multi-cloud

Contras: - Infraestrutura adicional para manter - Custo operacional - Secrets Manager já integrado com AWS

Por que não escolhemos: Overhead não justificado. Secrets Manager é suficiente.

Opção 2: Environment variables apenas

Prós: - Simples - Sem dependencies

Contras: - Sem rotação automática - Sem auditoria - Expostos em process list - Não escalável

Por que não escolhemos: Inseguro e não compliance.

Opção 3: Git-crypt

Prós: - Secrets em repositório (conveniente) - Versionados com código

Contras: - Ainda são commitados (risco) - Sem rotação automática - Difícil revogação - Não é best practice

Por que não escolhemos: Anti-pattern. Secrets não devem estar em repositório.

Análise de Impacto

Impacto Técnico

  • Secrets Manager para todos os secrets críticos
  • Rotação automática a cada 90 dias
  • IAM policies granulares
  • CloudTrail audit

Impacto em Negócio

  • ✅ Risco de secrets comprometidos reduzido
  • ✅ Compliance com SOC2/ISO27001
  • ✅ Auditoria completa de acesso
  • ⚠️ Custo Secrets Manager: ~$0.40/secret/mês

Riscos

Risco: Rotação automática quebra aplicação

Mitigação: Testing rigoroso em staging, monitoramento, rollback rápido.

Plano de Implementação

Fase 1: Migração (Semana 1-2)

  • Migrar secrets críticos para Secrets Manager
  • Atualizar código para usar Secrets Manager
  • Testar em staging

Fase 2: Rotação (Semana 3)

  • Configurar rotação automática (database)
  • Testar processo de rotação
  • Documentar

Fase 3: IAM (Semana 4)

  • Implementar least privilege policies
  • Audit current access
  • Revogar acesso desnecessário

Fase 4: Auditoria (Mês 2)

  • Habilitar CloudTrail
  • Configurar alertas
  • Training do time

Métricas de Sucesso

Após 1 mês: - ✅ 100% secrets críticos no Secrets Manager - ✅ Rotação automática configurada - ✅ Zero secrets hardcoded no código

Após 3 meses: - ✅ Secrets rotacionados automaticamente - ✅ Zero incidentes de secrets comprometidos - ✅ Audit logs funcionais

Referências