Merge PR #3: SSH passphrase support and Terraform security improvements

This commit is contained in:
Evert Daniel Romero Garrido
2026-05-07 10:46:43 -06:00
13 changed files with 1694 additions and 61 deletions
+90
View File
@@ -0,0 +1,90 @@
# Datos de Conexión SSH para CI/CD - Proyecto SACC4
## Servidores
| Entorno | IP | Usuario | Llave |
|---------|-----|---------|-------|
| DEV | 78.12.135.184 | thoth | bitbucket_thoth |
| DEV | 78.12.135.184 | osiris | bitbucket_osiris |
| PROD | 78.12.139.51 | thoth | bitbucket_thoth |
| PROD | 78.12.139.51 | osiris | bitbucket_osiris |
## Base de Datos (RDS)
| Entorno | Endpoint | Base de Datos | Usuario |
|---------|----------|---------------|---------|
| DEV | proyectosacc-db-dev.cbe2keowyu6w.mx-central-1.rds.amazonaws.com:3306 | ccsoft_sacc4 | sacc_admin_dev |
### Credenciales
```
DEV_DB_HOST=proyectosacc-db-dev.cbe2keowyu6w.mx-central-1.rds.amazonaws.com
DEV_DB_PORT=3306
DEV_DB_NAME=ccsoft_sacc4
DEV_DB_USERNAME=sacc_admin_dev
DEV_DB_PASSWORD=AiIRgWAM7RDB2VBuDlrXKBKyE
```
## Variables de Bitbucket
```
DEV_INSTANCE_IP=78.12.135.184
PROD_INSTANCE_IP=78.12.139.51
SSH_PRIVATE_KEY_THOTH=(contenido de keys/thoth_key)
SSH_PRIVATE_KEY_OSIRIS=(contenido de keys/osiris_key)
```
## Archivos de Llaves
- `keys/thoth_key` - Llave privada para usuario thoth (🔒 **con passphrase**)
- `keys/thoth_key.pub` - Llave pública para usuario thoth
- `keys/osiris_key` - Llave privada para usuario osiris (🔒 **con passphrase**)
- `keys/osiris_key.pub` - Llave pública para usuario osiris
- `keys/*.old` - Backups de llaves antiguas (sin passphrase)
## ✅ ESTADO: Llaves Actualizadas en Servidor DEV
### Passphrases (GUARDAR EN LUGAR SEGURO)
| Usuario | Passphrase |
|---------|------------|
| **thoth** | `fEbr9CoAlfllHDhocAbRo+aja+SW72a5` |
| **osiris** | `2/GkAjmPF8v0KNuWS6DC3IbyjNmP/Cfh` |
### Conexión de Prueba ✅ VERIFICADO
```bash
# Conectar como thoth (te pedirá la passphrase)
ssh -i keys/thoth_key thoth@78.12.135.184
# Conectar como osiris (te pedirá la passphrase)
ssh -i keys/osiris_key osiris@78.12.135.184
# Usar ssh-agent para no escribir la passphrase cada vez
eval "$(ssh-agent -s)"
ssh-add keys/thoth_key
ssh -i keys/thoth_key thoth@78.12.135.184
```
### Nuevas Llaves Públicas (ya autorizadas en servidor DEV)
**thoth:**
```
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMxH0Gp40+3sGhiWRL5lByTSbdcIcRe1XSWQvPW74JlQ thoth@ccsoft.ai
```
**osiris:**
```
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFo6CycfgIuCCSVZbhuPwqlAVDxY8YWb1xpvpqxSzMjR osiris@ccsoft.ai
```
### ⚠️ IMPORTANTE: Impacto en CI/CD
Las llaves con passphrase **romperán el pipeline CI/CD** a menos que configures `ssh-agent` en Bitbucket Pipelines. Para el pipeline, necesitarás:
1. Usar `ssh-agent` en el pipeline, o
2. Crear llaves separadas SOLO para CI/CD (sin passphrase)
### Backups Disponibles
Las llaves antiguas (sin passphrase) se guardaron como backup:
- `keys/thoth_key.old`
- `keys/osiris_key.old`
+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
+282 -29
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
@@ -17,6 +17,15 @@ options:
definitions:
steps:
- step: &install-aws-cli
name: Install AWS CLI
script:
- apt-get update -y && apt-get install -y curl unzip
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
- unzip -q awscliv2.zip
- ./aws/install
- aws --version
- step: &notify-start
name: Notify Start
script:
@@ -59,24 +68,50 @@ pipelines:
name: 01_image-setup
script:
- set -euo pipefail
- apt-get update -y && apt-get install -y openssh-client openjdk-21-jdk wget unzip curl
- apt-get update -y && apt-get install -y openssh-client openjdk-21-jdk wget unzip curl expect
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
- unzip -q awscliv2.zip
- ./aws/install
- aws --version
- mkdir -p ~/.ssh
- echo "${DEV_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
- echo "${SSH_PRIVATE_KEY_THOTH}" > ~/.ssh/sacc4_key
- chmod 600 ~/.ssh/sacc4_key
- ssh-keyscan -p "${DEV_SSH_PORT_PROYECTOSACC:-22}" "${DEV_SERVER_IP_PROYECTOSACC}" >> ~/.ssh/known_hosts 2>/dev/null || true
- ssh-keyscan -p "22" "${DEV_INSTANCE_IP}" >> ~/.ssh/known_hosts 2>/dev/null || true
- eval "$(ssh-agent -s)"
- |
expect -c "
spawn ssh-add ~/.ssh/sacc4_key
expect \"Enter passphrase\"
send \"${SSH_PASSPHRASE_THOTH}\r\"
expect eof
"
- export TELEGRAM_BOT_TOKEN="${DEV_TELEGRAM_BOT_TOKEN}"
- export TELEGRAM_CHAT_ID="${DEV_TELEGRAM_CHAT_ID}"
- bash scripts/telegram-pipeline-notify.sh start
- step:
name: 02_pre_terraform_check
oidc: true
script:
- set -euo pipefail
- apt-get update -y && apt-get install -y curl unzip
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
- unzip -q awscliv2.zip
- ./aws/install
- aws --version
- source scripts/aws-oidc-setup.sh dev
- bash scripts/terraform-lock-cleanup.sh dev proyectosacc/terraform.tfstate
- step:
name: 03_terraform
oidc: true
script:
- set -euo pipefail
- apt-get update -y && apt-get install -y curl unzip
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
- unzip -q awscliv2.zip
- ./aws/install
- aws --version
- source scripts/aws-oidc-setup.sh dev
- cd terraform
- wget -q "https://releases.hashicorp.com/terraform/1.11.4/terraform_1.11.4_linux_amd64.zip"
@@ -85,8 +120,15 @@ pipelines:
- terraform version
- export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-mx-central-1}"
- terraform init -backend-config=backend.dev.hcl
- terraform plan -var-file=environments/dev.tfvars -var="db_password=${DEV_DB_PASSWORD}" -out=dev.tfplan
- terraform apply -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:
@@ -119,6 +161,11 @@ pipelines:
oidc: true
script:
- set -euo pipefail
- apt-get update -y && apt-get install -y curl unzip
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
- unzip -q awscliv2.zip
- ./aws/install
- aws --version
- source scripts/aws-oidc-setup.sh dev
- |
if [ -d build/ ] && [ "$(ls -A build/ 2>/dev/null)" ]; then
@@ -135,9 +182,39 @@ pipelines:
- echo "Publish condicional completado."
- step:
name: 06_install
name: 06_update_ssh_keys
script:
- set -euo pipefail
- apt-get update -y && apt-get install -y expect
- mkdir -p ~/.ssh
- echo "${SSH_PRIVATE_KEY_THOTH}" > ~/.ssh/sacc4_key
- chmod 600 ~/.ssh/sacc4_key
- eval "$(ssh-agent -s)"
- |
expect -c "
spawn ssh-add ~/.ssh/sacc4_key
expect \"Enter passphrase\"
send \"${SSH_PASSPHRASE_THOTH}\r\"
expect eof
"
- |
DEV_PUB_KEY=$(echo "${SSH_PRIVATE_KEY_THOTH}" | ssh-keygen -y -f /dev/stdin)
ssh -p "22" \
-i ~/.ssh/sacc4_key \
-o StrictHostKeyChecking=no \
"thoth@${DEV_INSTANCE_IP}" \
"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 expect
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
- unzip -q awscliv2.zip
- ./aws/install
- aws --version
- |
JAR_LOCAL_PATTERN="build/libs/*.jar"
JAR_S3_URI="s3://${DEV_S3_ARTIFACTS_BUCKET}/develop/proyectosacc-app.jar"
@@ -147,12 +224,20 @@ pipelines:
fi
if [ "${HAS_LOCAL_JAR}" = "true" ]; then
echo "INFO: Artefacto JAR encontrado localmente. Procediendo con instalación en servidor."
echo "${DEV_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
mkdir -p ~/.ssh
echo "${SSH_PRIVATE_KEY_THOTH}" > ~/.ssh/sacc4_key
chmod 600 ~/.ssh/sacc4_key
ssh -p "${DEV_SSH_PORT_PROYECTOSACC:-22}" \
eval "$(ssh-agent -s)"
expect -c "
spawn ssh-add ~/.ssh/sacc4_key
expect \"Enter passphrase\"
send \"${SSH_PASSPHRASE_THOTH}\r\"
expect eof
"
ssh -p "22" \
-i ~/.ssh/sacc4_key \
-o StrictHostKeyChecking=no \
"${DEV_SERVER_USER_PROYECTOSACC:-thoth}@${DEV_SERVER_IP_PROYECTOSACC}" \
"thoth@${DEV_INSTANCE_IP}" \
"bash -c 'mkdir -p /home/thoth/deploy/artifacts/current && aws s3 cp ${JAR_S3_URI} /home/thoth/deploy/artifacts/current/proyectosacc-app.jar && chown osiris:osiris /home/thoth/deploy/artifacts/current/proyectosacc-app.jar'"
else
echo "INFO: No se encontró artefacto JAR localmente. Saltando instalación."
@@ -160,18 +245,32 @@ pipelines:
- echo "Install condicional completado."
- step:
name: 07_deploy
name: 08_deploy
oidc: true
script:
- set -euo pipefail
- apt-get update -y && apt-get install -y curl unzip expect
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
- unzip -q awscliv2.zip
- ./aws/install
- aws --version
- source scripts/aws-oidc-setup.sh dev
- echo "${DEV_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
- mkdir -p ~/.ssh
- echo "${SSH_PRIVATE_KEY_THOTH}" > ~/.ssh/sacc4_key
- chmod 600 ~/.ssh/sacc4_key
- eval "$(ssh-agent -s)"
- |
ssh -p "${DEV_SSH_PORT_PROYECTOSACC:-22}" \
expect -c "
spawn ssh-add ~/.ssh/sacc4_key
expect \"Enter passphrase\"
send \"${SSH_PASSPHRASE_THOTH}\r\"
expect eof
"
- |
ssh -p "22" \
-i ~/.ssh/sacc4_key \
-o StrictHostKeyChecking=no \
"${DEV_SERVER_USER_PROYECTOSACC:-thoth}@${DEV_SERVER_IP_PROYECTOSACC}" \
"thoth@${DEV_INSTANCE_IP}" \
"bash -c 'if [ -f /home/thoth/deploy/setup/deploy.sh ]; then bash /home/thoth/deploy/setup/deploy.sh; else echo \"INFO: No se encontró script de deploy. Saltando deploy backend.\"; fi'"
- |
if [ -f terraform/terraform-outputs.json ]; then
@@ -184,29 +283,82 @@ 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_INSTANCE_IP}: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
script:
- set -euo pipefail
- apt-get update -y && apt-get install -y openssh-client openjdk-21-jdk wget unzip curl
- apt-get update -y && apt-get install -y openssh-client openjdk-21-jdk wget unzip curl expect
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
- unzip -q awscliv2.zip
- ./aws/install
- aws --version
- mkdir -p ~/.ssh
- echo "${PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
- echo "${SSH_PRIVATE_KEY_THOTH}" > ~/.ssh/sacc4_key
- chmod 600 ~/.ssh/sacc4_key
- ssh-keyscan -p "${PROD_SSH_PORT_PROYECTOSACC:-22}" "${PROD_SERVER_IP_PROYECTOSACC}" >> ~/.ssh/known_hosts 2>/dev/null || true
- ssh-keyscan -p "22" "${PROD_INSTANCE_IP}" >> ~/.ssh/known_hosts 2>/dev/null || true
- eval "$(ssh-agent -s)"
- |
expect -c "
spawn ssh-add ~/.ssh/sacc4_key
expect \"Enter passphrase\"
send \"${SSH_PASSPHRASE_THOTH}\r\"
expect eof
"
- export TELEGRAM_BOT_TOKEN="${PROD_TELEGRAM_BOT_TOKEN}"
- export TELEGRAM_CHAT_ID="${PROD_TELEGRAM_CHAT_ID}"
- bash scripts/telegram-pipeline-notify.sh start
- step:
name: 02_pre_terraform_check
oidc: true
script:
- set -euo pipefail
- apt-get update -y && apt-get install -y curl unzip
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
- unzip -q awscliv2.zip
- ./aws/install
- aws --version
- source scripts/aws-oidc-setup.sh prod
- bash scripts/terraform-lock-cleanup.sh prod proyectosacc/terraform.tfstate
- step:
name: 03_terraform
oidc: true
script:
- set -euo pipefail
- apt-get update -y && apt-get install -y curl unzip
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
- unzip -q awscliv2.zip
- ./aws/install
- aws --version
- source scripts/aws-oidc-setup.sh prod
- cd terraform
- wget -q "https://releases.hashicorp.com/terraform/1.11.4/terraform_1.11.4_linux_amd64.zip"
@@ -215,12 +367,18 @@ pipelines:
- terraform version
- export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-mx-central-1}"
- terraform init -backend-config=backend.prod.hcl
- terraform plan -var-file=environments/prod.tfvars -var="db_password=${PROD_DB_PASSWORD}" -out=prod.tfplan
- terraform apply -auto-approve prod.tfplan
# 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
- 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
@@ -249,6 +407,11 @@ pipelines:
oidc: true
script:
- set -euo pipefail
- apt-get update -y && apt-get install -y curl unzip
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
- unzip -q awscliv2.zip
- ./aws/install
- aws --version
- source scripts/aws-oidc-setup.sh prod
- |
if [ -d build/ ] && [ "$(ls -A build/ 2>/dev/null)" ]; then
@@ -265,9 +428,41 @@ pipelines:
- echo "Publish condicional completado."
- step:
name: 06_install
name: 06_update_ssh_keys
script:
- set -euo pipefail
- apt-get update -y && apt-get install -y expect
- mkdir -p ~/.ssh
- echo "${SSH_PRIVATE_KEY_THOTH}" > ~/.ssh/sacc4_key
- chmod 600 ~/.ssh/sacc4_key
- eval "$(ssh-agent -s)"
- |
expect -c "
spawn ssh-add ~/.ssh/sacc4_key
expect \"Enter passphrase\"
send \"${SSH_PASSPHRASE_THOTH}\r\"
expect eof
"
# 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 "${SSH_PRIVATE_KEY_THOTH}" | ssh-keygen -y -f /dev/stdin)
ssh -p "22" \
-i ~/.ssh/sacc4_key \
-o StrictHostKeyChecking=no \
"thoth@${PROD_INSTANCE_IP}" \
"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 expect
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
- unzip -q awscliv2.zip
- ./aws/install
- aws --version
- |
JAR_LOCAL_PATTERN="build/libs/*.jar"
JAR_S3_URI="s3://${PROD_S3_ARTIFACTS_BUCKET}/main/proyectosacc-app.jar"
@@ -277,12 +472,20 @@ pipelines:
fi
if [ "${HAS_LOCAL_JAR}" = "true" ]; then
echo "INFO: Artefacto JAR encontrado localmente. Procediendo con instalación en servidor."
echo "${PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
mkdir -p ~/.ssh
echo "${SSH_PRIVATE_KEY_THOTH}" > ~/.ssh/sacc4_key
chmod 600 ~/.ssh/sacc4_key
ssh -p "${PROD_SSH_PORT_PROYECTOSACC:-22}" \
eval "$(ssh-agent -s)"
expect -c "
spawn ssh-add ~/.ssh/sacc4_key
expect \"Enter passphrase\"
send \"${SSH_PASSPHRASE_THOTH}\r\"
expect eof
"
ssh -p "22" \
-i ~/.ssh/sacc4_key \
-o StrictHostKeyChecking=no \
"${PROD_SERVER_USER_PROYECTOSACC:-thoth}@${PROD_SERVER_IP_PROYECTOSACC}" \
"thoth@${PROD_INSTANCE_IP}" \
"bash -c 'mkdir -p /home/thoth/deploy/artifacts/current && aws s3 cp ${JAR_S3_URI} /home/thoth/deploy/artifacts/current/proyectosacc-app.jar && chown osiris:osiris /home/thoth/deploy/artifacts/current/proyectosacc-app.jar'"
else
echo "INFO: No se encontró artefacto JAR localmente. Saltando instalación."
@@ -290,7 +493,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}"
@@ -299,20 +502,43 @@ 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
script:
- set -euo pipefail
- apt-get update -y && apt-get install -y curl unzip expect
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
- unzip -q awscliv2.zip
- ./aws/install
- aws --version
- source scripts/aws-oidc-setup.sh prod
- echo "${PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
- chmod 600 ~/.ssh/sacc4_key
# Aplicar Terraform plan previamente generado y guardado en artifact
- |
ssh -p "${PROD_SSH_PORT_PROYECTOSACC:-22}" \
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 "${SSH_PRIVATE_KEY_THOTH}" > ~/.ssh/sacc4_key
- chmod 600 ~/.ssh/sacc4_key
- eval "$(ssh-agent -s)"
- |
expect -c "
spawn ssh-add ~/.ssh/sacc4_key
expect \"Enter passphrase\"
send \"${SSH_PASSPHRASE_THOTH}\r\"
expect eof
"
- |
ssh -p "22" \
-i ~/.ssh/sacc4_key \
-o StrictHostKeyChecking=no \
"${PROD_SERVER_USER_PROYECTOSACC:-thoth}@${PROD_SERVER_IP_PROYECTOSACC}" \
"thoth@${PROD_INSTANCE_IP}" \
"bash -c 'if [ -f /home/thoth/deploy/setup/deploy.sh ]; then bash /home/thoth/deploy/setup/deploy.sh; else echo \"INFO: No se encontró script de deploy. Saltando deploy backend.\"; fi'"
- |
if [ -f terraform/terraform-outputs.json ]; then
@@ -324,3 +550,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_INSTANCE_IP}: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"
+112
View File
@@ -0,0 +1,112 @@
# Validación de Recursos AWS - Cuenta 668889063715
**Fecha:** Abril 2026
**Cuenta:** 668889063715
**Región Principal:** mx-central-1
**Entorno:** SOLO DESARROLLO (DEV)
---
## Resumen Ejecutivo
| Validación | Estado |
|-----------|--------|
| Solo recursos DEV | ✅ CONFIRMADO |
| Sin recursos PROD | ✅ CONFIRMADO |
| Sin duplicación entre regiones | ✅ CONFIRMADO |
| Costos acordes a DEV | ✅ CONFIRMADO |
---
## Inventario Completo
### 1. EC2 (Instancias)
| ID | Nombre | Tipo | Estado | IP Pública | IP Privada |
|----|--------|------|--------|------------|------------|
| i-044018f36de1020a8 | proyectosacc-api-dev | t3.small | running | 78.12.135.184 | 10.1.0.20 |
**Total:** 1 instancia (solo DEV)
### 2. RDS (Bases de Datos)
| ID | Motor | Estado | Base de Datos | Clase | Público |
|----|-------|--------|---------------|-------|---------|
| proyectosacc-db-dev | mariadb | available | ccsoft_sacc4 | db.t3.micro | No |
**Total:** 1 base de datos (solo DEV)
### 3. S3 (Buckets)
| Bucket | Uso |
|--------|-----|
| ccsoft-proyectosacc-artifacts-dev | Artefactos de compilación |
| ccsoft-proyectosacc-frontend-dev | Frontend compilado |
| ccsoft-terraform-state | Estado de Terraform |
**Total:** 3 buckets (todos DEV)
### 4. CloudFront (CDN)
| ID | Dominio | Estado |
|----|---------|--------|
| E2EJ7237VFEJAR | d3donfm7ov3eyb.cloudfront.net | Deployed |
**Total:** 1 distribución (DEV)
### 5. VPC (Redes)
| ID | Nombre | CIDR | Default |
|----|--------|------|---------|
| vpc-0fefbaa0848a316dd | proyectosacc-vpc-dev | 10.1.0.0/16 | No |
| vpc-04f5f7cce6ead6c45 | - | 172.31.0.0/16 | Sí (default) |
### 6. Route53 (DNS)
| Zona | Dominio | Privada |
|------|---------|---------|
| Z00862912V7J2IBIAQU9Z | dev-sacc.ccsoft.mx | No |
### 7. Security Groups
| ID | Nombre | Descripción |
|----|--------|-------------|
| sg-0052f147c98bb1904 | proyectosacc-ec2-api | API backend |
| sg-0b4190c4cec1ad2f5 | proyectosacc-rds | RDS MariaDB (acceso solo desde 10.1.0.20/32) |
---
## Validaciones de Seguridad
| Verificación | Estado |
|-------------|--------|
| Búsqueda de recursos con tag "prod" | ❌ No encontrados |
| Búsqueda de recursos con tag "production" | ❌ No encontrados |
| Buckets con nombre "prod" o "production" | ❌ No encontrados |
| Instancias EC2 en otras regiones | ❌ No encontradas |
| Bases de datos RDS en otras regiones | ❌ No encontradas |
---
## Costos Estimados
**Promedio diario:** ~$2.43 USD
**Estimado mensual:** ~$73 USD
**Desglose aproximado:**
- EC2 t3.small: ~$15/mes
- RDS db.t3.micro: ~$13/mes
- S3 + CloudFront + Data Transfer: ~$45/mes
---
## Conclusión
**La cuenta 668889063715 contiene ÚNICAMENTE recursos de desarrollo (DEV).**
- No hay servicios de producción duplicados
- No hay recursos en otras regiones
- Todos los recursos están correctamente etiquetados como DEV
- Los costos son consistentes con un entorno de desarrollo
**Recomendación:** Mantener esta cuenta exclusivamente para desarrollo y no desplegar recursos de producción aquí.
+7
View File
@@ -0,0 +1,7 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACAdkhvJ7Il4Yrw1Zqu9P1PcFbdc2HciYqb7tCaj60a+vwAAAJhowcCaaMHA
mgAAAAtzc2gtZWQyNTUxOQAAACAdkhvJ7Il4Yrw1Zqu9P1PcFbdc2HciYqb7tCaj60a+vw
AAAEBaErHCg+OQP9abpbe9+Eee80Cncbntxz5TXkuqBkA0xx2SG8nsiXhivDVmq70/U9wV
t1zYdyJipvu0JqPrRr6/AAAAEG9zaXJpc0BjY3NvZnQuYWkBAgMEBQ==
-----END OPENSSH PRIVATE KEY-----
+7
View File
@@ -0,0 +1,7 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACC8ad1D22SDWfkWGeO9qwGKrBFJmLe68Kk90aF/skUgYAAAAJimLIkZpiyJ
GQAAAAtzc2gtZWQyNTUxOQAAACC8ad1D22SDWfkWGeO9qwGKrBFJmLe68Kk90aF/skUgYA
AAAEC+p75hAW4PuYLQcy6dlR0taqeoNNFknaUbOHx7kdEKYrxp3UPbZINZ+RYZ472rAYqs
EUmYt7rwqT3RoX+yRSBgAAAAD3Rob3RoQGNjc29mdC5haQECAwQFBg==
-----END OPENSSH PRIVATE KEY-----
+91 -12
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 "$@"
+164
View File
@@ -0,0 +1,164 @@
#!/bin/bash
# ===============================================================================================================
# terraform-lock-cleanup.sh - Limpieza automática de locks bloqueados de Terraform
# Descripción:
# Verifica y elimina locks bloqueados de Terraform en DynamoDB antes de ejecutar terraform plan/apply.
# Previene errores de "Error acquiring the state lock" en pipelines de CI/CD.
#
# Uso:
# bash scripts/terraform-lock-cleanup.sh <environment> <state-key>
# Ejemplo: bash scripts/terraform-lock-cleanup.sh dev proyectosacc/terraform.tfstate
#
# Autor: Área de Tecnología y Desarrollo - CCsoft
# ===============================================================================================================
set -euo pipefail
# Colores para output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Variables
ENVIRONMENT="${1:-dev}"
STATE_KEY="${2:-proyectosacc/terraform.tfstate}"
LOCK_TABLE="${TERRAFORM_LOCK_TABLE:-terraform-locks}"
STATE_BUCKET="${TERRAFORM_STATE_BUCKET:-ccsoft-terraform-state}"
MAX_LOCK_AGE_MINUTES="${MAX_LOCK_AGE_MINUTES:-30}"
AWS_REGION="${AWS_DEFAULT_REGION:-mx-central-1}"
# Construir el LockID completo
LOCK_ID="${STATE_BUCKET}/${STATE_KEY}-md5"
echo "=== Terraform Lock Cleanup ==="
echo "Environment: ${ENVIRONMENT}"
echo "State Key: ${STATE_KEY}"
echo "Lock Table: ${LOCK_TABLE}"
echo "Lock ID: ${LOCK_ID}"
echo "Max Lock Age: ${MAX_LOCK_AGE_MINUTES} minutes"
echo "AWS Region: ${AWS_REGION}"
echo ""
# Verificar que AWS CLI está disponible
if ! command -v aws &> /dev/null; then
echo -e "${RED}ERROR: AWS CLI no está instalado${NC}"
exit 1
fi
# Verificar credenciales AWS
echo "Verificando credenciales AWS..."
if ! aws sts get-caller-identity &> /dev/null; then
echo -e "${RED}ERROR: No se pueden validar credenciales AWS${NC}"
exit 1
fi
echo -e "${GREEN}✓ Credenciales AWS válidas${NC}"
echo ""
# Verificar si la tabla existe
echo "Verificando tabla DynamoDB ${LOCK_TABLE}..."
if ! aws dynamodb describe-table --table-name "${LOCK_TABLE}" --region "${AWS_REGION}" &> /dev/null; then
echo -e "${YELLOW}⚠ Tabla ${LOCK_TABLE} no encontrada. No hay locks que limpiar.${NC}"
exit 0
fi
echo -e "${GREEN}✓ Tabla DynamoDB encontrada${NC}"
echo ""
# Buscar el lock
echo "Buscando lock en DynamoDB..."
LOCK_ITEM=$(aws dynamodb get-item \
--table-name "${LOCK_TABLE}" \
--key "{\"LockID\": {\"S\": \"${LOCK_ID}\"}}" \
--region "${AWS_REGION}" \
--output json 2>/dev/null || echo '{}')
if [ -z "${LOCK_ITEM}" ] || [ "${LOCK_ITEM}" == '{}' ]; then
echo -e "${GREEN}✓ No se encontró lock activo. Estado limpio.${NC}"
exit 0
fi
echo -e "${YELLOW}⚠ Lock encontrado en DynamoDB${NC}"
echo "Detalles del lock:"
echo "${LOCK_ITEM}" | python3 -m json.tool 2>/dev/null || echo "${LOCK_ITEM}"
echo ""
# Verificar si el lock tiene información de creación (Info field)
LOCK_INFO=$(echo "${LOCK_ITEM}" | python3 -c "
import json, sys
try:
item = json.load(sys.stdin)
info = item.get('Item', {}).get('Info', {}).get('S', '{}')
print(info)
except:
print('{}')
" 2>/dev/null || echo '{}')
if [ -n "${LOCK_INFO}" ] && [ "${LOCK_INFO}" != '{}' ]; then
echo "Información del lock:"
echo "${LOCK_INFO}" | python3 -m json.tool 2>/dev/null || echo "${LOCK_INFO}"
# Extraer timestamp de creación
CREATED=$(echo "${LOCK_INFO}" | python3 -c "
import json, sys
try:
info = json.load(sys.stdin)
created = info.get('Created', '')
print(created)
except:
print('')
" 2>/dev/null || echo '')
if [ -n "${CREATED}" ]; then
echo ""
echo "Lock creado: ${CREATED}"
# Calcular antigüedad del lock
CREATED_EPOCH=$(date -d "${CREATED}" +%s 2>/dev/null || echo '0')
CURRENT_EPOCH=$(date +%s)
if [ "${CREATED_EPOCH}" != '0' ]; then
AGE_MINUTES=$(( (CURRENT_EPOCH - CREATED_EPOCH) / 60 ))
echo "Antigüedad del lock: ${AGE_MINUTES} minutos"
if [ ${AGE_MINUTES} -gt ${MAX_LOCK_AGE_MINUTES} ]; then
echo -e "${YELLOW}⚠ Lock tiene más de ${MAX_LOCK_AGE_MINUTES} minutos. Considerado bloqueado.${NC}"
else
echo -e "${YELLOW}⚠ Lock tiene menos de ${MAX_LOCK_AGE_MINUTES} minutos. Podría ser una operación en curso.${NC}"
echo -e "${YELLOW}⚠ Esperando 30 segundos antes de verificar nuevamente...${NC}"
sleep 30
# Verificar nuevamente
LOCK_ITEM_RETRY=$(aws dynamodb get-item \
--table-name "${LOCK_TABLE}" \
--key "{\"LockID\": {\"S\": \"${LOCK_ID}\"}}" \
--region "${AWS_REGION}" \
--output json 2>/dev/null || echo '{}')
if [ -z "${LOCK_ITEM_RETRY}" ] || [ "${LOCK_ITEM_RETRY}" == '{}' ]; then
echo -e "${GREEN}✓ Lock liberado durante la espera. Estado limpio.${NC}"
exit 0
fi
echo -e "${YELLOW}⚠ Lock todavía presente después de esperar. Procediendo con limpieza...${NC}"
fi
fi
fi
fi
echo ""
echo -e "${YELLOW}⚠ Eliminando lock bloqueado de DynamoDB...${NC}"
# Eliminar el lock
if aws dynamodb delete-item \
--table-name "${LOCK_TABLE}" \
--key "{\"LockID\": {\"S\": \"${LOCK_ID}\"}}" \
--region "${AWS_REGION}" \
--condition-expression "attribute_exists(LockID)" \
2>/dev/null; then
echo -e "${GREEN}✓ Lock eliminado exitosamente${NC}"
else
echo -e "${YELLOW}⚠ No se pudo eliminar el lock (puede que ya no exista)${NC}"
fi
echo ""
echo "=== Limpieza de locks completada ==="
+363
View File
@@ -0,0 +1,363 @@
#!/bin/bash
###############################################################################
# Script de Validación de Conexión EC2 → RDS (MariaDB)
# Proyecto: proyectosacc (SACC4) - Entorno DEV
# Servidor: proyectosacc-api-dev (78.12.135.184)
# Base de datos: proyectosacc-db-dev
###############################################################################
# ─────────────────────────────────────────────────────────────────────────────
# CONFIGURACIÓN - Modifica estos valores según tu entorno
# ─────────────────────────────────────────────────────────────────────────────
RDS_HOST="proyectosacc-db-dev.cbe2keowyu6w.mx-central-1.rds.amazonaws.com"
RDS_PORT="3306"
RDS_USER="${DEV_DB_USERNAME:-admin}" # Usa variable de entorno o default
RDS_PASSWORD="${DEV_DB_PASSWORD:-}" # Se espera como variable de entorno
RDS_DBNAME="${DEV_DB_NAME:-proyectosacc_dev}" # Nombre de la base de datos
EC2_IP="78.12.135.184"
TIMEOUT_SECONDS="10"
# Colores para output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
BOLD='\033[1m'
# ─────────────────────────────────────────────────────────────────────────────
# FUNCIONES AUXILIARES
# ─────────────────────────────────────────────────────────────────────────────
print_header() {
echo -e "\n${BOLD}═══════════════════════════════════════════════════════════════${NC}"
echo -e "${BOLD} $1${NC}"
echo -e "${BOLD}═══════════════════════════════════════════════════════════════${NC}"
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
print_info() {
echo -e "${BLUE}$1${NC}"
}
# ─────────────────────────────────────────────────────────────────────────────
# VALIDACIÓN DE PREREQUISITOS
# ─────────────────────────────────────────────────────────────────────────────
print_header "1. VALIDACIÓN DE PREREQUISITOS"
# Verificar que estamos en el servidor correcto
CURRENT_IP=$(hostname -I | awk '{print $1}')
print_info "IP actual del servidor: $CURRENT_IP"
print_info "IP esperada (EC2 DEV): $EC2_IP"
if [ "$CURRENT_IP" != "$EC2_IP" ]; then
print_warning "La IP actual ($CURRENT_IP) no coincide con la IP esperada ($EC2_IP)"
print_warning "Asegúrate de estar ejecutando este script en el servidor DEV"
else
print_success "IP verificada correctamente"
fi
# Verificar herramientas necesarias
echo -e "\n${BOLD}Verificando herramientas instaladas:${NC}"
check_tool() {
if command -v "$1" &> /dev/null; then
VERSION=$($2 2>/dev/null | head -1)
print_success "$1 instalado - $VERSION"
return 0
else
print_error "$1 NO está instalado"
return 1
fi
}
NEED_MYSQL=0
check_tool "nc" "nc --version" || NEED_MYSQL=1
check_tool "telnet" "telnet --version" 2>/dev/null || true
check_tool "mysql" "mysql --version" || NEED_MYSQL=1
if [ $NEED_MYSQL -eq 1 ]; then
print_warning "Se recomienda instalar: mariadb-client o mysql-client"
print_info "Ejecuta: sudo apt-get update && sudo apt-get install -y mariadb-client"
fi
# ─────────────────────────────────────────────────────────────────────────────
# PRUEBA 1: RESOLUCIÓN DNS
# ─────────────────────────────────────────────────────────────────────────────
print_header "2. RESOLUCIÓN DNS"
print_info "Resolviendo: $RDS_HOST"
if command -v dig &> /dev/null; then
RDS_IP=$(dig +short "$RDS_HOST" | head -1)
if [ -n "$RDS_IP" ]; then
print_success "DNS resuelto: $RDS_HOST$RDS_IP"
dig +short "$RDS_HOST" | while read ip; do
print_info " IP encontrada: $ip"
done
else
print_error "No se pudo resolver el DNS de $RDS_HOST"
print_info "Verifica la configuración de DNS o la conectividad a internet"
fi
elif command -v nslookup &> /dev/null; then
nslookup "$RDS_HOST" 2>&1 | grep -E "Address:|Name:" | while read line; do
print_info " $line"
done
else
print_warning "No se encontró 'dig' ni 'nslookup'. Instala dnsutils:"
print_info " sudo apt-get install -y dnsutils"
fi
# ─────────────────────────────────────────────────────────────────────────────
# PRUEBA 2: CONECTIVIDAD DE RED (Ping)
# ─────────────────────────────────────────────────────────────────────────────
print_header "3. CONECTIVIDAD DE RED (Ping)"
print_info "Haciendo ping a $RDS_HOST..."
if ping -c 3 -W 5 "$RDS_HOST" &> /dev/null; then
print_success "Ping exitoso - El servidor RDS responde"
ping -c 1 -W 5 "$RDS_HOST" | grep -E "time=|ttl=" | while read line; do
print_info " $line"
done
else
print_error "Ping fallido - No hay respuesta del servidor RDS"
print_info "Esto puede ser normal si RDS tiene ICMP bloqueado"
fi
# ─────────────────────────────────────────────────────────────────────────────
# PRUEBA 3: CONEXIÓN DE PUERTO TCP
# ─────────────────────────────────────────────────────────────────────────────
print_header "4. CONEXIÓN DE PUERTO TCP (Puerto $RDS_PORT)"
print_info "Verificando si el puerto $RDS_PORT está abierto en $RDS_HOST..."
if command -v nc &> /dev/null; then
if nc -zv -w "$TIMEOUT_SECONDS" "$RDS_HOST" "$RDS_PORT" 2>&1 | grep -q "succeeded\|open"; then
print_success "Puerto $RDS_PORT ABIERTO - Conexión TCP exitosa"
else
print_error "Puerto $RDS_PORT CERRADO o bloqueado"
print_info "Verifica:"
print_info " 1. Security Group de RDS (debe permitir entrada desde $EC2_IP)"
print_info " 2. Network ACLs"
print_info " 3. Firewall del servidor EC2 (iptables/ufw)"
fi
nc -zv -w "$TIMEOUT_SECONDS" "$RDS_HOST" "$RDS_PORT" 2>&1 | while read line; do
print_info " $line"
done
elif command -v telnet &> /dev/null; then
print_info "Usando telnet (Ctrl+C para cancelar si se cuelga)..."
timeout 5 bash -c "echo >/dev/tcp/$RDS_HOST/$RDS_PORT" 2>/dev/null
if [ $? -eq 0 ]; then
print_success "Puerto $RDS_PORT ABIERTO"
else
print_error "Puerto $RDS_PORT CERRADO"
fi
else
print_warning "No se encontró 'nc' ni 'telnet'. Usando /dev/tcp..."
timeout 5 bash -c "echo >/dev/tcp/$RDS_HOST/$RDS_PORT" 2>/dev/null
if [ $? -eq 0 ]; then
print_success "Puerto $RDS_PORT ABIERTO"
else
print_error "Puerto $RDS_PORT CERRADO"
fi
fi
# ─────────────────────────────────────────────────────────────────────────────
# PRUEBA 4: CONEXIÓN A BASE DE DATOS (MySQL/MariaDB)
# ─────────────────────────────────────────────────────────────────────────────
print_header "5. CONEXIÓN A BASE DE DATOS (MariaDB)"
if ! command -v mysql &> /dev/null; then
print_error "Cliente MySQL/MariaDB no instalado"
print_info "Instala con: sudo apt-get install -y mariadb-client"
print_info "Saltando prueba de conexión a BD..."
else
if [ -z "$RDS_PASSWORD" ]; then
print_warning "No se encontró DEV_DB_PASSWORD en las variables de entorno"
print_info "Exporta la variable antes de ejecutar:"
print_info " export DEV_DB_PASSWORD='tu_password_aquí'"
print_info "O ejecuta el script con: DEV_DB_PASSWORD='xxx' ./validar_conexion_rds.sh"
else
print_info "Intentando conectar a MariaDB..."
# Prueba de conexión simple
if mysql -h "$RDS_HOST" -P "$RDS_PORT" -u "$RDS_USER" -p"$RDS_PASSWORD" \
-e "SELECT 'Conexión exitosa' as status, NOW() as server_time;" 2>/dev/null; then
print_success "¡CONEXIÓN EXITOSA A LA BASE DE DATOS!"
# Información del servidor
echo -e "\n${BOLD}Información del servidor MariaDB:${NC}"
mysql -h "$RDS_HOST" -P "$RDS_PORT" -u "$RDS_USER" -p"$RDS_PASSWORD" \
-e "SELECT VERSION() as version;" 2>/dev/null | grep -v "version" | while read line; do
print_info " Versión: $line"
done
# Listar bases de datos
echo -e "\n${BOLD}Bases de datos disponibles:${NC}"
mysql -h "$RDS_HOST" -P "$RDS_PORT" -u "$RDS_USER" -p"$RDS_PASSWORD" \
-e "SHOW DATABASES;" 2>/dev/null | grep -v "Database" | grep -v "information_schema\|performance_schema\|mysql" | while read db; do
print_info " 📁 $db"
done
# Verificar base de datos específica
if [ -n "$RDS_DBNAME" ]; then
echo -e "\n${BOLD}Verificando base de datos '$RDS_DBNAME':${NC}"
mysql -h "$RDS_HOST" -P "$RDS_PORT" -u "$RDS_USER" -p"$RDS_PASSWORD" \
-e "SHOW TABLES FROM \`$RDS_DBNAME\`;" 2>/dev/null | head -20 | while read table; do
if [ "$table" != "Tables_in_$RDS_DBNAME" ]; then
print_info " 📋 $table"
fi
done
fi
else
print_error "No se pudo conectar a la base de datos"
print_info "Posibles causas:"
print_info " 1. Credenciales incorrectas (usuario/password)"
print_info " 2. Usuario no tiene permisos desde esta IP"
print_info " 3. Base de datos no existe"
# Intentar obtener más información del error
echo -e "\n${BOLD}Detalle del error:${NC}"
mysql -h "$RDS_HOST" -P "$RDS_PORT" -u "$RDS_USER" -p"$RDS_PASSWORD" \
-e "SELECT 1;" 2>&1 | grep -v "Warning:" | while read line; do
print_error " $line"
done
fi
fi
fi
# ─────────────────────────────────────────────────────────────────────────────
# PRUEBA 5: VERIFICACIÓN DE VARIABLES DE ENTORNO
# ─────────────────────────────────────────────────────────────────────────────
print_header "6. VARIABLES DE ENTORNO CONFIGURADAS"
echo -e "${BOLD}Variables encontradas:${NC}"
vars_found=0
for var in DEV_DB_HOST DEV_DB_PORT DEV_DB_NAME DEV_DB_USERNAME DEV_DB_PASSWORD; do
if [ -n "${!var}" ]; then
if [ "$var" = "DEV_DB_PASSWORD" ]; then
print_success "$var=********** (oculto)"
else
print_success "$var=${!var}"
fi
vars_found=$((vars_found + 1))
else
print_error "$var=NO DEFINIDO"
fi
done
echo -e "\n${BOLD}Resumen:${NC}"
if [ $vars_found -eq 5 ]; then
print_success "Todas las variables de entorno están configuradas"
elif [ $vars_found -gt 0 ]; then
print_warning "Solo $vars_found/5 variables configuradas"
else
print_error "Ninguna variable de entorno configurada"
print_info "Crea un archivo .env con las variables necesarias:"
cat << 'EOF'
# Ejemplo de archivo .env
echo "export DEV_DB_HOST=proyectosacc-db-dev.cbe2keowyu6w.mx-central-1.rds.amazonaws.com" >> .env
echo "export DEV_DB_PORT=3306" >> .env
echo "export DEV_DB_NAME=proyectosacc_dev" >> .env
echo "export DEV_DB_USERNAME=admin" >> .env
echo "export DEV_DB_PASSWORD=tu_password_aqui" >> .env
# Cargar variables:
source .env
EOF
fi
# ─────────────────────────────────────────────────────────────────────────────
# PRUEBA 6: CONFIGURACIÓN DE AWS CLI (Opcional)
# ─────────────────────────────────────────────────────────────────────────────
print_header "7. CONFIGURACIÓN DE AWS CLI (Opcional)"
if command -v aws &> /dev/null; then
print_success "AWS CLI instalado"
# Verificar configuración
if aws sts get-caller-identity &> /dev/null; then
print_success "AWS CLI configurado correctamente"
aws sts get-caller-identity 2>/dev/null | grep -E "Account|Arn" | while read line; do
print_info " $line"
done
else
print_warning "AWS CLI no está configurado o credenciales inválidas"
print_info "Configura con: aws configure"
fi
# Verificar información del RDS desde AWS
echo -e "\n${BOLD}Información del RDS desde AWS CLI:${NC}"
aws rds describe-db-instances \
--query 'DBInstances[?DBInstanceIdentifier==`proyectosacc-db-dev`].[DBInstanceIdentifier,DBInstanceStatus,Endpoint.Address,AvailabilityZone]' \
--output table 2>/dev/null || print_error "No se pudo obtener información del RDS"
else
print_warning "AWS CLI no instalado (opcional para diagnóstico)"
print_info "Instala con: sudo apt-get install -y awscli"
fi
# ─────────────────────────────────────────────────────────────────────────────
# RESUMEN FINAL
# ─────────────────────────────────────────────────────────────────────────────
print_header "RESUMEN FINAL DE VALIDACIÓN"
echo -e "${BOLD}Conexión:${NC} EC2 DEV ($EC2_IP) → RDS DEV ($RDS_HOST:$RDS_PORT)"
echo ""
# Hacer resumen basado en las pruebas
print_info "✓ Verifica la resolución DNS del endpoint RDS"
print_info "✓ Verifica conectividad de red (ping/TCP)"
print_info "✓ Verifica conexión a MariaDB/MySQL"
print_info "✓ Verifica variables de entorno"
echo -e "\n${BOLD}Si todo está ✅:${NC}"
print_success "Tu servidor EC2 puede conectarse correctamente al RDS"
echo -e "\n${BOLD}Si hay ❌:${NC}"
print_info "1. Revisa Security Groups: Grupo de seguridad de RDS debe permitir entrada TCP 3306 desde $EC2_IP"
print_info "2. Revisa Network ACLs de la VPC"
print_info "3. Revisa firewall local: sudo iptables -L -n | grep 3306"
print_info "4. Verifica que el endpoint RDS sea correcto"
print_info "5. Verifica credenciales de la base de datos"
echo -e "\n${BOLD}Comandos útiles para diagnóstico:${NC}"
echo " # Ver logs de conexión (en EC2):"
echo " sudo tail -f /var/log/syslog | grep -i mariadb"
echo ""
echo " # Ver reglas de firewall:"
echo " sudo iptables -L -n | grep 3306"
echo ""
echo " # Test manual de conexión MySQL:"
echo " mysql -h $RDS_HOST -P $RDS_PORT -u <usuario> -p"
echo ""
echo " # Ver Security Groups desde AWS:"
echo " aws ec2 describe-security-groups --query 'SecurityGroups[*].[GroupName,GroupId]'"
echo -e "\n${GREEN}${BOLD}Script completado.${NC}"
+4 -2
View File
@@ -13,8 +13,10 @@ aws_region = "mx-central-1"
vpc_cidr = "10.2.0.0/16"
availability_zones = ["mx-central-1a", "mx-central-1b"]
ec2_instance_type = "t3.small"
ec2_key_name = "ccsoft-prod-key"
pipeline_public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKQCNFOzDJzaOMDIeEbH4JCx2OrXrgljajgkJqlozj9m bitbucket.pipeline.ci.cd.proyectosacc.thoth@computocontable.com"
ec2_ami = "ami-0f553e2869648134e"
ec2_key_name = "sacc-prod-key-2026"
ec2_root_volume_size = 8
pipeline_public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII/RcJmEYOBpfq1tSLltV1pyNB55l1jA2zYr5ZNJ0f41 thoth@ccsoft"
db_instance_class = "db.t3.micro"
db_name = "sacc_db_prod"
db_username = "sacc_admin_prod"
+88 -9
View File
@@ -121,7 +121,7 @@ resource "aws_security_group" "ec2_api" {
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 {
@@ -148,6 +148,41 @@ resource "aws_security_group" "ec2_api" {
cidr_blocks = [aws_vpc.main.cidr_block]
}
ingress {
from_port = 8081
to_port = 8081
protocol = "tcp"
cidr_blocks = [aws_vpc.main.cidr_block]
}
ingress {
from_port = 8082
to_port = 8082
protocol = "tcp"
cidr_blocks = [aws_vpc.main.cidr_block]
}
ingress {
from_port = 8083
to_port = 8083
protocol = "tcp"
cidr_blocks = [aws_vpc.main.cidr_block]
}
ingress {
from_port = 8084
to_port = 8084
protocol = "tcp"
cidr_blocks = [aws_vpc.main.cidr_block]
}
ingress {
from_port = 8085
to_port = 8085
protocol = "tcp"
cidr_blocks = [aws_vpc.main.cidr_block]
}
egress {
description = "Salida libre"
from_port = 0
@@ -250,6 +285,23 @@ resource "aws_iam_role_policy" "ec2_policy" {
"cloudfront:CreateInvalidation"
]
Resource = "*"
},
{
Effect = "Allow"
Action = [
"ssm:UpdateInstanceInformation",
"ssmmessages:CreateControlChannel",
"ssmmessages:CreateDataChannel",
"ssmmessages:OpenControlChannel",
"ssmmessages:OpenDataChannel",
"ec2messages:AcknowledgeMessage",
"ec2messages:DeleteMessage",
"ec2messages:FailMessage",
"ec2messages:GetEndpoint",
"ec2messages:GetMessages",
"ec2messages:SendReply"
]
Resource = "*"
}
]
})
@@ -272,23 +324,35 @@ resource "aws_instance" "api" {
instance_type = var.ec2_instance_type
subnet_id = aws_subnet.public[0].id
vpc_security_group_ids = [aws_security_group.ec2_api.id]
iam_instance_profile = aws_iam_instance_profile.ec2_profile.name
key_name = var.ec2_key_name
root_block_device {
volume_type = "gp3"
volume_size = var.ec2_root_volume_size
encrypted = true
volume_type = "gp2"
volume_size = 8
encrypted = false
delete_on_termination = true
}
user_data = base64encode(templatefile("${path.module}/user-data.sh", {
pipeline_public_key = var.pipeline_public_key
}))
tags = {
Name = "${var.project_name}-api-${var.environment}"
Environment = var.environment
AutoStart = "true"
AutoStop = "true"
Schedule = "office-hours"
ScheduleHours = "08:00-19:00_L-V"
ScheduleTimezone = "America/Mexico_City"
}
lifecycle {
ignore_changes = [
iam_instance_profile,
user_data,
tags["AutoStart"],
tags["AutoStop"],
tags["Schedule"],
tags["ScheduleHours"],
tags["ScheduleTimezone"],
]
}
}
@@ -324,6 +388,21 @@ resource "aws_db_instance" "main" {
tags = {
Name = "${var.project_name}-rds"
Environment = var.environment
AutoStart = "true"
AutoStop = "true"
Schedule = "office-hours"
ScheduleHours = "08:00-19:00_L-V"
ScheduleTimezone = "America/Mexico_City"
}
lifecycle {
ignore_changes = [
tags["AutoStart"],
tags["AutoStop"],
tags["Schedule"],
tags["ScheduleHours"],
tags["ScheduleTimezone"],
]
}
}
+72
View File
@@ -22,6 +22,32 @@ PIPELINE_PUBLIC_KEY="${pipeline_public_key}"
apt-get update -y
apt-get install -y nginx openjdk-21-jdk awscli curl jq
# -------------------------------------------------------------------------------
# Instalar y verificar AWS Systems Manager Agent
# -------------------------------------------------------------------------------
# SSM Agent permite acceso seguro sin abrir puertos SSH (0.0.0.0/0)
# Pre-instalado en Amazon Linux; en Ubuntu puede requerir instalación manual
apt-get install -y amazon-ssm-agent 2>/dev/null || true
# Si el paquete no está disponible en repositorios, descargar desde AWS
if ! command -v amazon-ssm-agent &> /dev/null; then
echo "Descargando SSM Agent desde AWS..."
curl -fsSL -o /tmp/amazon-ssm-agent.deb https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/debian_amd64/amazon-ssm-agent.deb
dpkg -i /tmp/amazon-ssm-agent.deb || true
rm -f /tmp/amazon-ssm-agent.deb
fi
# Asegurar que el servicio esté habilitado y corriendo
systemctl enable amazon-ssm-agent || true
systemctl restart amazon-ssm-agent || true
# Verificar estado del agente
if systemctl is-active --quiet amazon-ssm-agent; then
echo "SSM Agent instalado y activo correctamente"
else
echo "ADVERTENCIA: No se pudo iniciar SSM Agent. Verificar conectividad a AWS y permisos IAM."
fi
# -------------------------------------------------------------------------------
# Crear usuarios del sistema
# -------------------------------------------------------------------------------
@@ -38,6 +64,52 @@ echo "$PIPELINE_PUBLIC_KEY" > /home/thoth/.ssh/authorized_keys
chmod 600 /home/thoth/.ssh/authorized_keys
chown -R thoth:thoth /home/thoth/.ssh
# -------------------------------------------------------------------------------
# Configurar permisos sudo para usuario thoth (SACC4 Application Management)
# -------------------------------------------------------------------------------
cat > /etc/sudoers.d/thoth <<'SUDOERS_EOF'
# SACC4 Application Management Permissions
# User: thoth
# Generated: $(date)
# 1. Editar archivo de configuracion de la aplicacion
thoth ALL=(ALL) NOPASSWD: /usr/bin/nano /etc/sacc4/sacc4.env
thoth ALL=(ALL) NOPASSWD: /usr/bin/vim /etc/sacc4/sacc4.env
thoth ALL=(ALL) NOPASSWD: /usr/bin/vi /etc/sacc4/sacc4.env
# 2. Gestionar servicios systemd
thoth ALL=(ALL) NOPASSWD: /usr/bin/systemctl status api-sacc4-*.service
thoth ALL=(ALL) NOPASSWD: /usr/bin/systemctl start api-sacc4-*.service
thoth ALL=(ALL) NOPASSWD: /usr/bin/systemctl stop api-sacc4-*.service
thoth ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart api-sacc4-*.service
thoth ALL=(ALL) NOPASSWD: /usr/bin/systemctl enable api-sacc4-*.service
thoth ALL=(ALL) NOPASSWD: /usr/bin/systemctl disable api-sacc4-*.service
thoth ALL=(ALL) NOPASSWD: /usr/bin/journalctl -u api-sacc4-*.service
# 3. Editar archivos de servicios systemd
thoth ALL=(ALL) NOPASSWD: /usr/bin/nano /etc/systemd/system/api-sacc4-*.service
thoth ALL=(ALL) NOPASSWD: /usr/bin/vim /etc/systemd/system/api-sacc4-*.service
thoth ALL=(ALL) NOPASSWD: /usr/bin/vi /etc/systemd/system/api-sacc4-*.service
# 4. Recargar systemd daemon
thoth ALL=(ALL) NOPASSWD: /usr/bin/systemctl daemon-reload
# 5. Control total del directorio /opt/sacc4
thoth ALL=(ALL) NOPASSWD: /bin/ls -la /opt/sacc4/*
thoth ALL=(ALL) NOPASSWD: /bin/chown -R thoth\:thoth /opt/sacc4/*
thoth ALL=(ALL) NOPASSWD: /bin/chmod -R [0-7][0-7][0-7] /opt/sacc4/*
thoth ALL=(ALL) NOPASSWD: /bin/mkdir -p /opt/sacc4/*
thoth ALL=(ALL) NOPASSWD: /bin/rm -rf /opt/sacc4/*
thoth ALL=(ALL) NOPASSWD: /bin/cp -r * /opt/sacc4/*
thoth ALL=(ALL) NOPASSWD: /bin/mv * /opt/sacc4/*
SUDOERS_EOF
chmod 440 /etc/sudoers.d/thoth
chown root:root /etc/sudoers.d/thoth
# Validar sintaxis del archivo sudoers
visudo -c || echo "ADVERTENCIA: Error en sintaxis de /etc/sudoers.d/thoth"
# -------------------------------------------------------------------------------
# Crear estructura de directorios de despliegue
# -------------------------------------------------------------------------------
+6
View File
@@ -80,6 +80,12 @@ variable "pipeline_public_key" {
type = string
}
variable "allowed_ssh_cidrs" {
description = "Lista de CIDRs permitidos para acceso SSH (vacío = deshabilitado). Preferir AWS Systems Manager Session Manager en lugar de SSH."
type = list(string)
default = []
}
# -------------------------------------------------------------------------------
# RDS (Base de datos)
# -------------------------------------------------------------------------------