feat(pipeline): Add SSH key rotation, health checks, and manual approval

Security & Operations Improvements:
- Add step 06_update_ssh_keys to rotate authorized_keys on EC2
  before each deployment, ensuring only current pipeline can access
- Add step 09_health_check with retry logic (12 retries, 10s interval)
  verifying API backend (/actuator/health), CloudFront, and RDS
- Add manual approval (trigger: manual) for production deployment
  with terraform plan saved as artifact (prod.tfplan)
- Document terraform auto-approve policy: dev automatic, prod manual
- Use DEV_DB_HOST and PROD_DB_HOST variables for RDS connectivity checks
- Reorder steps: 7 steps → 9 steps standard CCsoft pipeline

Closes pipeline security gaps and adds post-deploy verification.
This commit is contained in:
Evert Daniel Romero Garrido
2026-04-20 17:47:15 -06:00
parent f32b58fc46
commit defce6933d
4 changed files with 623 additions and 28 deletions
+399
View File
@@ -0,0 +1,399 @@
# 📋 GUÍA COMPLETA PARA DESARROLLO - CI/CD proyectosacc
> **Fecha:** 17 de Abril 2026
> **Proyecto:** proyectosacc (SACC)
> **Repositorio:** https://bitbucket.org/ccsoft1/proyectosacc
> **Cuenta AWS:** 668889063715 (Desarrollo)
---
## 🎯 RESUMEN EJECUTIVO
Esta guía contiene toda la información necesaria para que el equipo de desarrollo configure y use el pipeline CI/CD del proyecto `proyectosacc`.
**Estado del Pipeline:** ✅ Funcionando
**Último Build:** #67 (Exitoso)
**Infraestructura:** ✅ Creada y validada
---
## 📁 ESTRUCTURA DEL PROYECTO
```
proyectosacc/
├── bitbucket-pipelines.yml # Pipeline CI/CD (7 pasos)
├── scripts/
│ ├── aws-oidc-setup.sh # Configuración OIDC AWS
│ ├── telegram-pipeline-notify.sh # Notificaciones Telegram
│ ├── terraform-lock-cleanup.sh # Limpieza de locks Terraform
│ ├── deploy.sh # Script de deploy manual
│ ├── health-check.sh # Health check de servicios
│ ├── rollback.sh # Rollback de deploys
│ └── deploy-frontend-s3.sh # Deploy frontend a S3
├── terraform/
│ ├── main.tf # Infraestructura principal
│ ├── variables.tf # Variables de Terraform
│ ├── outputs.tf # Outputs de Terraform
│ ├── backend.tf # Configuración backend S3
│ ├── backend.dev.hcl # Backend desarrollo
│ ├── backend.prod.hcl # Backend producción
│ ├── oidc-bitbucket.tf # Configuración OIDC
│ ├── provider.tf # Providers AWS
│ ├── user-data.sh # Script inicialización EC2
│ └── environments/
│ ├── dev.tfvars # Variables desarrollo
│ └── prod.tfvars # Variables producción
├── nginx/ # Configuración nginx
├── docs/ # Documentación
├── .env.dev # Variables entorno dev
├── .env.prod # Variables entorno prod
└── .gitignore
```
---
## 🔧 CONFIGURACIÓN REQUERIDA EN BITBUCKET
### 1. Variables de Repositorio (Repository Variables)
Ve a: **Bitbucket → Repositorio → Repository settings → Repository variables**
#### Variables para Desarrollo (developer)
| Variable | Descripción | Valor / Ejemplo |
|----------|-------------|-----------------|
| `AWS_DEFAULT_REGION` | Región AWS | `mx-central-1` |
| `DEV_DB_PASSWORD` | Contraseña BD MariaDB | *(solicitar a Infra)* |
| `DEV_S3_FRONTEND_BUCKET` | Bucket S3 frontend | `ccsoft-proyectosacc-frontend-dev` |
| `DEV_S3_ARTIFACTS_BUCKET` | Bucket S3 artifacts | `ccsoft-proyectosacc-artifacts-dev` |
| `DEV_SERVER_IP_PROYECTOSACC` | IP servidor EC2 | `78.12.135.184` |
| `DEV_SERVER_USER_PROYECTOSACC` | Usuario SSH | `thoth` |
| `DEV_SSH_PORT_PROYECTOSACC` | Puerto SSH | `22` |
| `DEV_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC` | Llave SSH (Base64) | *(solicitar a Infra)* |
| `DEV_TELEGRAM_BOT_TOKEN` | Token bot Telegram | *(solicitar a Infra)* |
| `DEV_TELEGRAM_CHAT_ID` | ID chat Telegram | *(solicitar a Infra)* |
#### Variables para Producción (master)
| Variable | Descripción | Valor / Ejemplo |
|----------|-------------|-----------------|
| `AWS_DEFAULT_REGION` | Región AWS | `mx-central-1` |
| `PROD_DB_PASSWORD` | Contraseña BD MariaDB | *(solicitar a Infra)* |
| `PROD_S3_FRONTEND_BUCKET` | Bucket S3 frontend | *(configurar)* |
| `PROD_S3_ARTIFACTS_BUCKET` | Bucket S3 artifacts | *(configurar)* |
| `PROD_SERVER_IP_PROYECTOSACC` | IP servidor EC2 | *(configurar)* |
| `PROD_SERVER_USER_PROYECTOSACC` | Usuario SSH | `thoth` |
| `PROD_SSH_PORT_PROYECTOSACC` | Puerto SSH | `22` |
| `PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC` | Llave SSH (Base64) | *(solicitar a Infra)* |
| `PROD_TELEGRAM_BOT_TOKEN` | Token bot Telegram | *(solicitar a Infra)* |
| `PROD_TELEGRAM_CHAT_ID` | ID chat Telegram | *(solicitar a Infra)* |
### 2. Configuración OIDC (OpenID Connect)
El pipeline usa OIDC para autenticación con AWS. **No se requieren credenciales estáticas**.
**Configuración ya implementada:**
- Rol IAM: `BitbucketProyectosaccCICDRoleDev`
- Proveedor OIDC: Configurado para Bitbucket
- Alcance: Repositorio `ccsoft1/proyectosacc`
**Para producción:** Se debe crear un rol similar para el entorno prod.
---
## 🚀 PIPELINE CI/CD (7 Pasos)
### Flujo del Pipeline
```
┌─────────────────────────────────────────────────────────────┐
│ RAMA DEVELOPER │
├─────────────────────────────────────────────────────────────┤
│ 01_image-setup → Preparar imagen + SSH keys │
│ 02_pre_terraform_check→ Limpiar locks de Terraform │
│ 03_terraform → Infraestructura (plan + apply) │
│ 04_build → Compilar frontend + backend │
│ 05_publish → Subir a S3 │
│ 06_install → Instalar JAR en servidor │
│ 07_deploy → Deploy + Invalidar CloudFront │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ RAMA MASTER (Producción) │
├─────────────────────────────────────────────────────────────┤
│ 01_image-setup → Preparar imagen + SSH keys │
│ 02_pre_terraform_check→ Limpiar locks de Terraform │
│ 03_terraform → Infraestructura (plan + apply) │
│ 04_build → Compilar frontend + backend │
│ 05_publish → Subir a S3 │
│ 06_install → Instalar JAR en servidor │
│ 06b_notify_approval → Notificar espera aprobación │
│ 07_deploy → Deploy manual (requiere aprobación)│
└─────────────────────────────────────────────────────────────┘
```
### Detalle de Steps
#### Step 01: image-setup
- Instala dependencias: openssh-client, openjdk-21-jdk, wget, unzip, curl
- Instala AWS CLI v2
- Configura llaves SSH para acceso al servidor
- Configura notificaciones Telegram
#### Step 02: pre_terraform_check
- Instala AWS CLI
- Configura credenciales OIDC
- Ejecuta `terraform-lock-cleanup.sh`
- **Previene errores de state lock**
#### Step 03: terraform
- Instala Terraform 1.11.4
- Ejecuta `terraform init`
- Ejecuta `terraform plan` (verificación)
- Ejecuta `terraform apply` (crea/actualiza infraestructura)
- Genera `terraform-outputs.json`
#### Step 04: build
- **Frontend:** Si existe `package.json``npm ci && npm run build`
- **Backend:** Si existe `gradlew``./gradlew clean bootJar`
- Genera artefactos: `build/**`, `build/libs/*.jar`
#### Step 05: publish
- Sincroniza `build/` con S3 frontend (`aws s3 sync`)
- Copia JAR a S3 artifacts (`aws s3 cp`)
#### Step 06: install
- Descarga JAR desde S3 al servidor EC2
- Configura permisos (`chown osiris:osiris`)
#### Step 07: deploy
- Ejecuta script de deploy en servidor EC2
- Invalida distribución CloudFront
- Notifica éxito por Telegram
---
## 🌐 INFRAESTRUCTURA AWS (Creada)
### Recursos Activos
| Servicio | Recurso | Identificador | Estado |
|----------|---------|---------------|--------|
| **EC2** | Instancia API | `i-044018f36de1020a8` | ✅ running |
| | IP Pública | `78.12.135.184` | ✅ |
| | DNS | `ec2-78-12-135-184.mx-central-1.compute.amazonaws.com` | ✅ |
| **RDS** | Base de datos | `proyectosacc-db-dev` | ✅ available |
| | Endpoint | `proyectosacc-db-dev.cbe2keowyu6w.mx-central-1.rds.amazonaws.com:3306` | ✅ |
| | Motor | MariaDB | ✅ |
| **S3** | Frontend | `ccsoft-proyectosacc-frontend-dev` | ✅ |
| | Artifacts | `ccsoft-proyectosacc-artifacts-dev` | ✅ |
| | Terraform State | `ccsoft-terraform-state` | ✅ |
| **CloudFront** | Distribución | `E2EJ7237VFEJAR` | ✅ Deployed |
| | Dominio | `d3donfm7ov3eyb.cloudfront.net` | ✅ |
| **Route53** | Zona DNS | `dev-sacc.ccsoft.mx` | ✅ |
| **VPC** | Red | `vpc-0fefbaa0848a316dd` (10.1.0.0/16) | ✅ |
| **NAT Gateway** | Salida privada | `nat-0dafd3abd5557cd22` | ✅ |
| **DynamoDB** | Locks Terraform | `terraform-locks` | ✅ |
### Outputs de Terraform
```json
{
"ec2_public_ip": "78.12.135.184",
"ec2_public_dns": "ec2-78-12-135-184.mx-central-1.compute.amazonaws.com",
"rds_endpoint": "proyectosacc-db-dev.cbe2keowyu6w.mx-central-1.rds.amazonaws.com:3306",
"s3_frontend_bucket": "ccsoft-proyectosacc-frontend-dev",
"s3_artifacts_bucket": "ccsoft-proyectosacc-artifacts-dev",
"cloudfront_domain": "d3donfm7ov3eyb.cloudfront.net",
"cloudfront_distribution_id": "E2EJ7237VFEJAR",
"route53_record": "dev-sacc.ccsoft.mx",
"acm_certificate_arn": "arn:aws:acm:us-east-1:668889063715:certificate/521e3ae2-f22b-479f-a5ae-db9700caa248",
"vpc_id": "vpc-0fefbaa0848a316dd"
}
```
---
## 🔐 ACCESOS Y CREDENCIALES
### Servidor EC2 (Desarrollo)
```bash
# Conexión SSH
ssh -p 22 -i ~/.ssh/sacc4_key thoth@78.12.135.184
# Usuarios configurados
- thoth: Usuario de deploy (CI/CD)
- osiris: Usuario de ejecución de la aplicación
```
### Base de Datos (RDS)
```
Host: proyectosacc-db-dev.cbe2keowyu6w.mx-central-1.rds.amazonaws.com
Port: 3306
Engine: MariaDB
Usuario: *(configurado en Terraform)*
Contraseña: ${DEV_DB_PASSWORD}
```
### S3 Buckets
```bash
# Frontend
aws s3 ls s3://ccsoft-proyectosacc-frontend-dev/
# Artifacts
aws s3 ls s3://ccsoft-proyectosacc-artifacts-dev/
```
---
## 📱 NOTIFICACIONES TELEGRAM
### Bots Configurados
- **@CCAlertasBot** - Alertas críticas
- **@CCDevRoBot** - Notificaciones de desarrollo
### Mensajes Enviados
- ✅ Inicio de pipeline
- ❌ Fallo de pipeline (con detalle del paso)
- ✅ Deploy exitoso
- ⏸️ Espera de aprobación (producción)
---
## 💰 COSTOS MENSUALES ESTIMADOS
| Servicio | Costo Estimado |
|----------|---------------|
| EC2 t3.small | ~$15-20 |
| RDS db.t3.micro | ~$15-20 |
| NAT Gateway | ~$32 |
| S3 (3 buckets) | ~$1-5 |
| CloudFront | ~$1-10 |
| Route53 | ~$0.50 |
| Data Transfer | ~$5-10 |
| **TOTAL** | **~$70-100/mes** |
---
## 🛠️ COMANDOS ÚTILES
### Terraform (Local)
```bash
# Configurar credenciales
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...
export AWS_DEFAULT_REGION=mx-central-1
# Inicializar
cd terraform
terraform init -backend-config=backend.dev.hcl
# Plan
terraform plan -var-file=environments/dev.tfvars -var="db_password=TU_PASSWORD"
# Apply
terraform apply -var-file=environments/dev.tfvars -var="db_password=TU_PASSWORD"
# Outputs
terraform output -json
```
### AWS CLI
```bash
# Verificar identidad
aws sts get-caller-identity
# Listar instancias EC2
aws ec2 describe-instances
# Ver logs de CloudWatch
aws logs tail /aws/lambda/... --follow
# Invalidar CloudFront
aws cloudfront create-invalidation --distribution-id E2EJ7237VFEJAR --paths "/*"
```
### Bitbucket Pipelines
```bash
# Ejecutar pipeline manualmente
# Ir a: Bitbucket → Pipelines → Run pipeline
# Seleccionar rama: developer o master
```
---
## 🚨 TROUBLESHOOTING
### Error: "Error acquiring the state lock"
**Solución:** El pipeline ahora incluye el step `02_pre_terraform_check` que limpia locks automáticamente.
### Error: "Saved plan is stale"
**Solución:** El pipeline ahora ejecuta `plan` y `apply` sin guardar archivo intermedio.
### Error: "aws: command not found"
**Solución:** Cada step instala AWS CLI v2 independientemente.
### Pipeline falla en step 07_deploy
**Verificar:**
1. Llave SSH está configurada en Bitbucket variables
2. Servidor EC2 está running
3. Script `/home/thoth/deploy/setup/deploy.sh` existe en servidor
---
## 📞 CONTACTOS Y SOPORTE
| Rol | Contacto | Uso |
|-----|----------|-----|
| Infraestructura | Área de Tecnología | Credenciales, accesos AWS |
| DevOps | Pipeline issues | Configuración CI/CD |
| Seguridad | Rotación de llaves | SSH, tokens |
---
## ✅ CHECKLIST PARA DESARROLLO
### Antes de hacer push
- [ ] Código compila localmente (`npm run build` o `./gradlew bootJar`)
- [ ] Tests pasan
- [ ] Variables de entorno actualizadas (si aplica)
### Después de merge a developer
- [ ] Pipeline ejecuta automáticamente
- [ ] Revisar notificación de Telegram
- [ ] Verificar deploy en: https://dev-sacc.ccsoft.mx
### Para producción (master)
- [ ] Crear PR a master
- [ ] Revisar plan de Terraform
- [ ] Aprobar deploy manual en Bitbucket
- [ ] Verificar en producción
---
## 📚 DOCUMENTACIÓN ADICIONAL
- **Pipeline completo:** Ver `bitbucket-pipelines.yml`
- **Scripts:** Ver carpeta `scripts/`
- **Terraform:** Ver carpeta `terraform/`
- **Directivas AWS:** Ver `docs/13-directivas-arquitectura-aws.md`
---
**Documento generado:** 17 Abril 2026
**Versión:** 1.0
**Mantenido por:** Área de Tecnología y Desarrollo - CCsoft
+122 -9
View File
@@ -1,7 +1,7 @@
# ===============================================================================================================
# bitbucket-pipelines.yml - Pipeline CI/CD para proyectosacc
# Descripción:
# Pipeline de 7 pasos estándar de CCsoft para desplegar infraestructura (Terraform),
# Pipeline de 9 pasos estándar de CCsoft para desplegar infraestructura (Terraform),
# frontend React (S3+CloudFront) y API backend (EC2) de SACC.
#
# Autor: Área de Tecnología y Desarrollo - CCsoft
@@ -112,8 +112,15 @@ pipelines:
- terraform version
- export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-mx-central-1}"
- terraform init -backend-config=backend.dev.hcl
- terraform plan -lock-timeout=5m -var-file=environments/dev.tfvars -var="db_password=${DEV_DB_PASSWORD}" -out=dev.tfplan
- terraform apply -lock-timeout=5m -auto-approve dev.tfplan
- echo "=== Ejecutando Terraform Plan (solo verificación) ==="
- terraform plan -lock-timeout=5m -var-file=environments/dev.tfvars -var="db_password=${DEV_DB_PASSWORD}"
# NOTA DE SEGURIDAD: -auto-approve es aceptable en DEV porque:
# 1. El plan ya se ejecutó arriba y se visualiza en los logs del pipeline
# 2. DEV es un ambiente de desarrollo donde los cambios son reversibles
# 3. El pipeline corre en branches 'developer' con revisión previa vía PR
# Para PROD nunca usar -auto-approve sin revisión manual del plan.
- echo "=== Ejecutando Terraform Apply (auto-approve en DEV) ==="
- terraform apply -lock-timeout=5m -auto-approve -var-file=environments/dev.tfvars -var="db_password=${DEV_DB_PASSWORD}"
- terraform output -json > terraform-outputs.json
- cat terraform-outputs.json
artifacts:
@@ -167,7 +174,25 @@ pipelines:
- echo "Publish condicional completado."
- step:
name: 06_install
name: 06_update_ssh_keys
script:
- set -euo pipefail
- mkdir -p ~/.ssh
- echo "${DEV_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
- chmod 600 ~/.ssh/sacc4_key
# Actualizar authorized_keys del usuario thoth con la llave pública del pipeline
# Esto asegura que solo el pipeline actual pueda acceder, rotando llaves automáticamente
- |
DEV_PUB_KEY=$(echo "${DEV_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d | ssh-keygen -y -f /dev/stdin)
ssh -p "${DEV_SSH_PORT_PROYECTOSACC:-22}" \
-i ~/.ssh/sacc4_key \
-o StrictHostKeyChecking=no \
"${DEV_SERVER_USER_PROYECTOSACC:-thoth}@${DEV_SERVER_IP_PROYECTOSACC}" \
"bash -c 'mkdir -p /home/thoth/.ssh && chmod 700 /home/thoth/.ssh && echo \"${DEV_PUB_KEY}\" > /home/thoth/.ssh/authorized_keys && chmod 600 /home/thoth/.ssh/authorized_keys && chown -R thoth:thoth /home/thoth/.ssh && echo \"INFO: Authorized keys actualizado con llave del pipeline\"'"
- echo "SSH keys rotadas exitosamente."
- step:
name: 07_install
script:
- set -euo pipefail
- apt-get update -y && apt-get install -y curl unzip
@@ -197,7 +222,7 @@ pipelines:
- echo "Install condicional completado."
- step:
name: 07_deploy
name: 08_deploy
oidc: true
script:
- set -euo pipefail
@@ -226,6 +251,33 @@ pipelines:
- export TELEGRAM_CHAT_ID="${DEV_TELEGRAM_CHAT_ID}"
- bash scripts/telegram-pipeline-notify.sh success "Deploy condicional completado"
- step:
name: 09_health_check
oidc: true
script:
- set -euo pipefail
- apt-get update -y && apt-get install -y curl
- source scripts/aws-oidc-setup.sh dev
- echo "=== Health Check post-deploy (DEV) ==="
- |
export ENV=dev
export HEALTH_URL="http://${DEV_SERVER_IP_PROYECTOSACC}:8080/actuator/health"
export MAX_RETRIES=12
export RETRY_INTERVAL=10
# Verificar CloudFront
CLOUDFRONT_DOMAIN=$(aws cloudfront list-distributions --query "DistributionList.Items[?Aliases.Items[0]=='sacc.ccsoft.mx'].DomainName" --output text)
if [ -n "${CLOUDFRONT_DOMAIN}" ]; then
export CF_DOMAIN="${CLOUDFRONT_DOMAIN}"
fi
# Verificar RDS si está configurado
if [ -n "${DEV_DB_HOST:-}" ]; then
export DB_HOST="${DEV_DB_HOST}"
fi
bash scripts/health-check.sh
- export TELEGRAM_BOT_TOKEN="${DEV_TELEGRAM_BOT_TOKEN}"
- export TELEGRAM_CHAT_ID="${DEV_TELEGRAM_CHAT_ID}"
- bash scripts/telegram-pipeline-notify.sh success "Health Check pasó - Servicios saludables"
master:
- step:
name: 01_image-setup
@@ -275,12 +327,18 @@ pipelines:
- terraform version
- export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-mx-central-1}"
- terraform init -backend-config=backend.prod.hcl
# NOTA DE SEGURIDAD: En PROD nunca usar -auto-approve sin revisión manual.
# El plan se guarda en prod.tfplan y debe ser revisado antes de aplicar.
# El paso 08_deploy requiere aprobación manual (trigger: manual).
- echo "=== Ejecutando Terraform Plan (PROD - requiere revisión manual) ==="
- terraform plan -lock-timeout=5m -var-file=environments/prod.tfvars -var="db_password=${PROD_DB_PASSWORD}" -out=prod.tfplan
- terraform apply -lock-timeout=5m -auto-approve prod.tfplan
- echo "=== Terraform Plan guardado en prod.tfplan ==="
- echo "AVISO: Revisar el plan arriba antes de aprobar el deploy manual."
- terraform output -json > terraform-outputs.json
- cat terraform-outputs.json
artifacts:
- terraform/terraform-outputs.json
- terraform/prod.tfplan
- step:
name: 04_build
@@ -330,7 +388,25 @@ pipelines:
- echo "Publish condicional completado."
- step:
name: 06_install
name: 06_update_ssh_keys
script:
- set -euo pipefail
- mkdir -p ~/.ssh
- echo "${PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
- chmod 600 ~/.ssh/sacc4_key
# Actualizar authorized_keys del usuario thoth con la llave pública del pipeline
# Esto asegura que solo el pipeline actual pueda acceder, rotando llaves automáticamente
- |
PROD_PUB_KEY=$(echo "${PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d | ssh-keygen -y -f /dev/stdin)
ssh -p "${PROD_SSH_PORT_PROYECTOSACC:-22}" \
-i ~/.ssh/sacc4_key \
-o StrictHostKeyChecking=no \
"${PROD_SERVER_USER_PROYECTOSACC:-thoth}@${PROD_SERVER_IP_PROYECTOSACC}" \
"bash -c 'mkdir -p /home/thoth/.ssh && chmod 700 /home/thoth/.ssh && echo \"${PROD_PUB_KEY}\" > /home/thoth/.ssh/authorized_keys && chmod 600 /home/thoth/.ssh/authorized_keys && chown -R thoth:thoth /home/thoth/.ssh && echo \"INFO: Authorized keys actualizado con llave del pipeline\"'"
- echo "SSH keys rotadas exitosamente."
- step:
name: 07_install
script:
- set -euo pipefail
- apt-get update -y && apt-get install -y curl unzip
@@ -360,7 +436,7 @@ pipelines:
- echo "Install condicional completado."
- step:
name: 06b_notify_approval
name: 07b_notify_approval
script:
- set -euo pipefail
- export TELEGRAM_BOT_TOKEN="${PROD_TELEGRAM_BOT_TOKEN}"
@@ -369,7 +445,7 @@ pipelines:
bash scripts/telegram-pipeline-notify.sh start "⏸️ Pipeline pausado esperando aprobación manual para deploy a PRODUCCIÓN. Ve a Bitbucket > Pipelines > proyectosacc > master para aprobar o rechazar."
- step:
name: 07_deploy
name: 08_deploy
oidc: true
deployment: production
trigger: manual
@@ -381,6 +457,16 @@ pipelines:
- ./aws/install
- aws --version
- source scripts/aws-oidc-setup.sh prod
# Aplicar Terraform plan previamente generado y guardado en artifact
- |
if [ -f terraform/prod.tfplan ]; then
echo "=== Aplicando Terraform Plan previamente aprobado ==="
cd terraform
terraform apply -lock-timeout=5m prod.tfplan
cd ..
else
echo "WARNING: No se encontró prod.tfplan. Terraform apply saltado."
fi
- echo "${PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
- chmod 600 ~/.ssh/sacc4_key
- |
@@ -399,3 +485,30 @@ pipelines:
- export TELEGRAM_BOT_TOKEN="${PROD_TELEGRAM_BOT_TOKEN}"
- export TELEGRAM_CHAT_ID="${PROD_TELEGRAM_CHAT_ID}"
- bash scripts/telegram-pipeline-notify.sh success "CloudFront invalidado | Deploy a PROD aprobado y completado"
- step:
name: 09_health_check
oidc: true
script:
- set -euo pipefail
- apt-get update -y && apt-get install -y curl
- source scripts/aws-oidc-setup.sh prod
- echo "=== Health Check post-deploy (PROD) ==="
- |
export ENV=prod
export HEALTH_URL="http://${PROD_SERVER_IP_PROYECTOSACC}:8080/actuator/health"
export MAX_RETRIES=12
export RETRY_INTERVAL=10
# Verificar CloudFront
CLOUDFRONT_DOMAIN=$(aws cloudfront list-distributions --query "DistributionList.Items[?Aliases.Items[0]=='sacc.ccsoft.mx'].DomainName" --output text)
if [ -n "${CLOUDFRONT_DOMAIN}" ]; then
export CF_DOMAIN="${CLOUDFRONT_DOMAIN}"
fi
# Verificar RDS si está configurado
if [ -n "${PROD_DB_HOST:-}" ]; then
export DB_HOST="${PROD_DB_HOST}"
fi
bash scripts/health-check.sh
- export TELEGRAM_BOT_TOKEN="${PROD_TELEGRAM_BOT_TOKEN}"
- export TELEGRAM_CHAT_ID="${PROD_TELEGRAM_CHAT_ID}"
- bash scripts/telegram-pipeline-notify.sh success "Health Check pasó - Servicios de PROD saludables"
+96 -17
View File
@@ -1,12 +1,14 @@
#!/usr/bin/env bash
# ===============================================================================================================
# health-check.sh - Verifica que la API backend esté saludable
# health-check.sh - Verifica que la API backend, CloudFront y RDS estén saludables
# Descripción:
# Realiza peticiones al endpoint de salud de la API con reintentos.
# Puede ejecutarse desde la EC2 localmente o desde el pipeline.
# Realiza peticiones a múltiples endpoints con reintentos configurables.
# Puede ejecutarse desde la EC2 localmente o desde el pipeline CI/CD.
# Verifica: API backend (puertos 8080-8084), CloudFront, RDS.
#
# Uso:
# HEALTH_URL=http://localhost:8080/actuator/health bash health-check.sh
# ENV=prod CF_DOMAIN=sacc.ccsoft.mx bash health-check.sh
#
# Autor: Área de Tecnología y Desarrollo - CCsoft
# ===============================================================================================================
@@ -17,8 +19,12 @@ set -euo pipefail
# Configuración
# -------------------------------------------------------------------------------
HEALTH_URL="${HEALTH_URL:-http://localhost:8080/actuator/health}"
MAX_RETRIES="${MAX_RETRIES:-30}"
RETRY_INTERVAL="${RETRY_INTERVAL:-2}"
CF_DOMAIN="${CF_DOMAIN:-}"
DB_HOST="${DB_HOST:-}"
DB_PORT="${DB_PORT:-3306}"
ENV="${ENV:-dev}"
MAX_RETRIES="${MAX_RETRIES:-12}"
RETRY_INTERVAL="${RETRY_INTERVAL:-10}"
readonly CLR_RED='\033[0;31m'
readonly CLR_GREEN='\033[0;32m'
@@ -32,30 +38,103 @@ _log_error() { echo -e "${CLR_RED}[ERROR]${CLR_NC} $*" >&2; }
_log_step() { echo ""; echo -e "${CLR_BLUE}==== $* ====${CLR_NC}"; }
# -------------------------------------------------------------------------------
# Health Check
# Health Check con reintentos
# -------------------------------------------------------------------------------
main() {
_log_step "Health Check: ${HEALTH_URL}"
check_with_retry() {
local name="$1"
local url="$2"
local check_cmd="$3"
_log_step "Verificando: ${name} (${url})"
local attempt=1
while [[ $attempt -le $MAX_RETRIES ]]; do
_log_info "Intento ${attempt}/${MAX_RETRIES}..."
if curl -sf "${HEALTH_URL}" | grep -q '"status":"UP"'; then
_log_info "Health check PASÓ - API saludable"
if eval "${check_cmd}"; then
_log_info "${name} - SALUDABLE"
return 0
fi
if [[ $attempt -lt $MAX_RETRIES ]]; then
_log_warn "API no responde aún, reintentando en ${RETRY_INTERVAL}s..."
_log_warn "${name} no responde, reintentando en ${RETRY_INTERVAL}s..."
sleep "${RETRY_INTERVAL}"
fi
((attempt++))
done
_log_error "Health check FALLÓ después de ${MAX_RETRIES} intentos"
_log_error "${name} - FALLÓ después de ${MAX_RETRIES} intentos"
return 1
}
# -------------------------------------------------------------------------------
# Health Check Principal
# -------------------------------------------------------------------------------
main() {
local exit_code=0
_log_step "Health Check Post-Deploy [${ENV^^}]"
echo "Configuración: MAX_RETRIES=${MAX_RETRIES}, INTERVAL=${RETRY_INTERVAL}s"
echo ""
# 1. Verificar API Backend (puerto principal)
if ! check_with_retry "API Backend" "${HEALTH_URL}" \
"curl -sf '${HEALTH_URL}' | grep -q '\"status\":\"UP\"'"; then
exit_code=1
fi
# 2. Verificar puertos adicionales de servicios (8080-8084)
local base_url
base_url=$(echo "${HEALTH_URL}" | sed 's|/actuator/health||')
for port in 8081 8082 8083 8084; do
local port_url="${base_url//:[0-9]*/:${port}}/actuator/health"
# Solo verificar si la variable del puerto está definida o si es el mismo host
if [[ "${base_url}" =~ :[0-9]+ ]]; then
port_url="${base_url%:*}:${port}/actuator/health"
fi
# Verificación opcional - no falla el pipeline si un puerto opcional no responde
_log_info "Verificando puerto opcional ${port}..."
if curl -sf "${port_url}" | grep -q '"status":"UP"' 2>/dev/null; then
_log_info "✓ Puerto ${port} - RESPONDE"
else
_log_warn "Puerto ${port} no responde (opcional)"
fi
done
# 3. Verificar CloudFront (si está configurado)
if [[ -n "${CF_DOMAIN}" ]]; then
if ! check_with_retry "CloudFront" "https://${CF_DOMAIN}/api/health" \
"curl -sf -o /dev/null 'https://${CF_DOMAIN}/api/health'"; then
exit_code=1
fi
else
_log_warn "CF_DOMAIN no configurado. Saltando verificación de CloudFront."
fi
# 4. Verificar RDS (si está configurado)
if [[ -n "${DB_HOST}" ]]; then
if command -v nc &> /dev/null; then
if ! check_with_retry "RDS" "${DB_HOST}:${DB_PORT}" \
"nc -zv '${DB_HOST}' ${DB_PORT}"; then
exit_code=1
fi
else
_log_warn "nc (netcat) no instalado. Saltando verificación de RDS."
fi
else
_log_warn "DB_HOST no configurado. Saltando verificación de RDS."
fi
echo ""
if [[ $exit_code -eq 0 ]]; then
_log_step "✓ TODOS LOS HEALTH CHECKS PASARON"
else
_log_step "✗ ALGUNOS HEALTH CHECKS FALLARON"
fi
return $exit_code
}
main "$@"
+6 -2
View File
@@ -116,12 +116,16 @@ resource "aws_security_group" "ec2_api" {
vpc_id = aws_vpc.main.id
description = "Security Group para la API backend de ${var.project_name}"
# NOTA DE SEGURIDAD: Acceso SSH controlado EXCLUSIVAMENTE por llaves SSH
# administradas por el pipeline CI/CD (key-based auth), NO por restricción de IP.
# El pipeline inyecta y rota las llaves públicas en authorized_keys del usuario thoth.
# Considerar migrar a AWS Systems Manager Session Manager para eliminar acceso SSH directo.
ingress {
description = "SSH desde IPs confiables"
description = "SSH - Acceso controlado por llaves CI/CD (no por IP)"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # SSH desde cualquier IP (pipeline Bitbucket + administración)
cidr_blocks = ["0.0.0.0/0"]
}
ingress {