⚠ Auditoria de Seguridad Activa
🐕

NutriPaw SaaS

Nutricion personalizada para mascotas · Stack: Node.js + AWS + GitHub Actions

Gestion de Secretos en CI/CD

Plan de implementacion para eliminar credenciales hardcodeadas, centralizar en AWS Secrets Manager y establecer rotacion automatica + escaneo pre-commit.

5 secretos criticos
GitHub Actions · AWS Secrets Manager
Rotacion automatica cada 30 dias
TruffleHog pre-commit
🚨

Problema detectado: credenciales expuestas en historial git

El commit a3f9b12 del 2024-11-03 contenia .env.staging con NUTRIPAW_DB_PASSWORD y STRIPE_SECRET_KEY en texto plano. Aunque fue eliminado del HEAD, persiste en el historial. Rotar inmediatamente y migrar a AWS Secrets Manager.

1Arquitectura de Secretos Propuesta
Flujo: GitHub Actions → AWS Secrets Manager → ECS / RDS
👨‍💻
Dev Push / PR
🔒
Pre-commit TruffleHog
GitHub Actions Pipeline
🛡
AWS Secrets Manager
🚀
ECS Task / RDS
2Inventario de Secretos
Secreto Servicio Entorno Criticidad Rotacion Ruta en AWS SM
NUTRIPAW_DB_PASSWORD PostgreSQL RDS prod staging ⚡ Critica 30 dias (auto) nutripaw/prod/db/password
NUTRIPAW_STRIPE_SECRET_KEY Stripe Payments prod ⚡ Critica 90 dias (manual) nutripaw/prod/stripe/secret
NUTRIPAW_OPENAI_API_KEY OpenAI API prod staging ⚠ Alta 60 dias (manual) nutripaw/prod/openai/key
NUTRIPAW_SENDGRID_API_KEY SendGrid Email prod staging ⚠ Alta 90 dias (manual) nutripaw/prod/sendgrid/key
NUTRIPAW_JWT_SECRET Sesiones de usuario prod staging ✓ Media 180 dias (auto) nutripaw/prod/jwt/secret
3GitHub Actions — Pipeline Seguro
Produccion
Staging
Escaneo Pre-commit
YAML 📄 .github/workflows/deploy-production.yml
name: Deploy NutriPaw — Production

on:
  push:
    branches: [main]

permissions:
  id-token: write   # Necesario para OIDC — sin claves AWS en GitHub Secrets
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production     # Requiere aprobacion manual en GitHub

    steps:
      - uses: actions/checkout@v4

      ## 1) Autenticacion con AWS via OIDC (sin claves hardcodeadas)
      - name: Configure AWS credentials (OIDC)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789:role/nutripaw-github-actions
          aws-region: eu-west-1

      ## 2) Recuperar secretos de AWS Secrets Manager
      - name: Retrieve secrets from AWS Secrets Manager
        run: |
          DB_PASSWORD=$(aws secretsmanager get-secret-value \
            --secret-id nutripaw/prod/db/password \
            --query SecretString --output text)
          STRIPE_KEY=$(aws secretsmanager get-secret-value \
            --secret-id nutripaw/prod/stripe/secret \
            --query SecretString --output text)
          OPENAI_KEY=$(aws secretsmanager get-secret-value \
            --secret-id nutripaw/prod/openai/key \
            --query SecretString --output text)

          # Enmascarar en logs — OBLIGATORIO
          echo "::add-mask::$DB_PASSWORD"
          echo "::add-mask::$STRIPE_KEY"
          echo "::add-mask::$OPENAI_KEY"

          # Inyectar como variables de entorno para pasos siguientes
          echo "NUTRIPAW_DB_PASSWORD=$DB_PASSWORD" >> $GITHUB_ENV
          echo "NUTRIPAW_STRIPE_SECRET_KEY=$STRIPE_KEY" >> $GITHUB_ENV
          echo "NUTRIPAW_OPENAI_API_KEY=$OPENAI_KEY" >> $GITHUB_ENV

      ## 3) Build y push imagen Docker a ECR
      - name: Build & push Docker image to ECR
        run: |
          aws ecr get-login-password | docker login --username AWS \
            --password-stdin 123456789.dkr.ecr.eu-west-1.amazonaws.com
          docker build -t nutripaw-api:${{ github.sha }} .
          docker push 123456789.dkr.ecr.eu-west-1.amazonaws.com/nutripaw-api:${{ github.sha }}

      ## 4) Deploy en ECS (los secretos se pasan como Secrets, NO como env vars en la task def)
      - name: Deploy ECS service
        run: |
          aws ecs update-service \
            --cluster nutripaw-prod \
            --service nutripaw-api \
            --force-new-deployment
YAML 📄 .github/workflows/deploy-staging.yml
name: Deploy NutriPaw — Staging

on:
  push:
    branches: [develop]

permissions:
  id-token: write
  contents: read

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging   # Secretos de staging — distintos a los de prod

    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS credentials (OIDC — staging role)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789:role/nutripaw-github-staging
          aws-region: eu-west-1

      - name: Retrieve staging secrets
        run: |
          # Staging usa clave Stripe TEST, nunca la clave LIVE
          STRIPE_TEST=$(aws secretsmanager get-secret-value \
            --secret-id nutripaw/staging/stripe/test-secret \
            --query SecretString --output text)
          echo "::add-mask::$STRIPE_TEST"
          echo "NUTRIPAW_STRIPE_SECRET_KEY=$STRIPE_TEST" >> $GITHUB_ENV

      - name: Deploy to ECS staging
        run: |
          aws ecs update-service \
            --cluster nutripaw-staging \
            --service nutripaw-api-staging \
            --force-new-deployment
BASH 📄 .git/hooks/pre-commit
#!/bin/bash
# NutriPaw — Pre-commit hook: escaneo de secretos con TruffleHog
# Instalar: chmod +x .git/hooks/pre-commit

echo "[NutriPaw] Escaneando secretos antes del commit..."

# Verificar si Docker esta disponible
if ! command -v docker &>/dev/null; then
  echo "[ERROR] Docker no encontrado. Instala Docker para usar TruffleHog."
  exit 1
fi

# Ejecutar TruffleHog solo sobre los archivos staged (no todo el repo)
STAGED_FILES=$(git diff --cached --name-only)

docker run --rm \
  -v "$(pwd):/repo" \
  trufflesecurity/trufflehog:3.88 \
  git file:///repo \
  --since-commit HEAD \
  --only-verified \
  --fail

if [ $? -ne 0 ]; then
  echo ""
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "  COMMIT BLOQUEADO: se detectaron secretos  "
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "  Elimina las credenciales del archivo y usa"
  echo "  AWS Secrets Manager o variables de entorno."
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  exit 1
fi

echo "[OK] Ningun secreto detectado. Commit permitido."
exit 0
4Lambda de Rotacion Automatica — RDS
PYTHON 📄 lambdas/rotate_db_password.py
import boto3
import json
import string
import secrets

def generate_secure_password(length=32) -> str:
    """Genera una contrasena criptograficamente segura."""
    alphabet = string.ascii_letters + string.digits + "!@#$%^&*()"
    return "".join(secrets.choice(alphabet) for _ in range(length))

def lambda_handler(event, context):
    """
    Lambda para rotacion automatica de la contrasena RDS de NutriPaw.
    Configurar en AWS Secrets Manager con schedule de 30 dias.
    """
    client = boto3.client('secretsmanager', region_name='eu-west-1')
    rds_client = boto3.client('rds', region_name='eu-west-1')

    secret_id = 'nutripaw/prod/db/password'

    # 1. Obtener secreto actual
    response = client.get_secret_value(SecretId=secret_id)
    current = json.loads(response['SecretString'])

    # 2. Generar nueva contrasena
    new_password = generate_secure_password()

    # 3. Actualizar contrasena en RDS (NutriPaw usa PostgreSQL)
    rds_client.modify_db_instance(
        DBInstanceIdentifier='nutripaw-prod-postgres',
        MasterUserPassword=new_password,
        ApplyImmediately=True
    )

    # 4. Actualizar secreto en AWS Secrets Manager
    client.put_secret_value(
        SecretId=secret_id,
        SecretString=json.dumps({
            'username': current['username'],
            'password': new_password,
            'host': current['host'],
            'port': current['port'],
            'dbname': current['dbname'],
            'rotated_at': str(context.aws_request_id)
        })
    )

    return {'statusCode': 200, 'body': 'Rotacion completada con exito'}
5Plan de Implementacion y Checklist
📋Checklist de Seguridad
  • Inventario completo de todos los secretos del proyecto
  • Rotar inmediatamente las credenciales del commit a3f9b12
  • Crear secretos en AWS Secrets Manager (5 paths)
  • Configurar OIDC entre GitHub Actions y AWS IAM (sin claves estaticas)
  • Instalar pre-commit hook TruffleHog en todos los dev machines
  • Configurar Lambda de rotacion RDS cada 30 dias
  • Habilitar audit logging en AWS CloudTrail para accesos a Secrets Manager
  • Revisar historial git con BFG Repo Cleaner para purgar .env del pasado
  • Separar secretos por entorno: staging nunca usa claves live
  • Documentar ubicacion de cada secreto y responsable de rotacion
🕐Timeline de Implementacion
🚨

Dia 1 — Rotacion de emergencia

Rotar NUTRIPAW_DB_PASSWORD y STRIPE_SECRET_KEY expuestos en el historial git.

🛡

Dias 2-3 — Setup AWS Secrets Manager

Crear los 5 paths, configurar politicas IAM con least-privilege, habilitar CloudTrail.

Dias 4-5 — Pipelines GitHub Actions

Migrar pipelines a OIDC + AWS Secrets Manager. Configurar entornos staging y production con aprobacion manual.

🔒

Dia 6 — Pre-commit TruffleHog

Instalar hook en repositorio y en todas las maquinas del equipo. Verificar con git commit de prueba.

🔄

Dia 7 — Rotacion automatica Lambda

Deploy Lambda de rotacion RDS, configurar schedule de 30 dias, test en staging primero.