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:
@@ -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
@@ -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"
|
||||
|
||||
+91
-12
@@ -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
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user