diff --git a/DATOS_CONEXION.md b/DATOS_CONEXION.md new file mode 100644 index 0000000..a86a9b2 --- /dev/null +++ b/DATOS_CONEXION.md @@ -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` diff --git a/GUIA_DESARROLLO.md b/GUIA_DESARROLLO.md new file mode 100644 index 0000000..b6d8371 --- /dev/null +++ b/GUIA_DESARROLLO.md @@ -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 diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 9c9eb67..f99aae7 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -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: ¬ify-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" diff --git a/docs/VALIDACION_CUENTA_AWS.md b/docs/VALIDACION_CUENTA_AWS.md new file mode 100644 index 0000000..2bd6264 --- /dev/null +++ b/docs/VALIDACION_CUENTA_AWS.md @@ -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Γ­. diff --git a/keys/osiris_key b/keys/osiris_key new file mode 100644 index 0000000..be74df0 --- /dev/null +++ b/keys/osiris_key @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACAdkhvJ7Il4Yrw1Zqu9P1PcFbdc2HciYqb7tCaj60a+vwAAAJhowcCaaMHA +mgAAAAtzc2gtZWQyNTUxOQAAACAdkhvJ7Il4Yrw1Zqu9P1PcFbdc2HciYqb7tCaj60a+vw +AAAEBaErHCg+OQP9abpbe9+Eee80Cncbntxz5TXkuqBkA0xx2SG8nsiXhivDVmq70/U9wV +t1zYdyJipvu0JqPrRr6/AAAAEG9zaXJpc0BjY3NvZnQuYWkBAgMEBQ== +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/thoth_key b/keys/thoth_key new file mode 100644 index 0000000..59739dc --- /dev/null +++ b/keys/thoth_key @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACC8ad1D22SDWfkWGeO9qwGKrBFJmLe68Kk90aF/skUgYAAAAJimLIkZpiyJ +GQAAAAtzc2gtZWQyNTUxOQAAACC8ad1D22SDWfkWGeO9qwGKrBFJmLe68Kk90aF/skUgYA +AAAEC+p75hAW4PuYLQcy6dlR0taqeoNNFknaUbOHx7kdEKYrxp3UPbZINZ+RYZ472rAYqs +EUmYt7rwqT3RoX+yRSBgAAAAD3Rob3RoQGNjc29mdC5haQECAwQFBg== +-----END OPENSSH PRIVATE KEY----- diff --git a/scripts/health-check.sh b/scripts/health-check.sh index 5b8345b..54c2522 100755 --- a/scripts/health-check.sh +++ b/scripts/health-check.sh @@ -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 "$@" diff --git a/scripts/terraform-lock-cleanup.sh b/scripts/terraform-lock-cleanup.sh new file mode 100644 index 0000000..d26eb9c --- /dev/null +++ b/scripts/terraform-lock-cleanup.sh @@ -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 +# 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 ===" diff --git a/scripts/validar_conexion_rds.sh b/scripts/validar_conexion_rds.sh new file mode 100644 index 0000000..2f101ff --- /dev/null +++ b/scripts/validar_conexion_rds.sh @@ -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 -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}" diff --git a/terraform/environments/prod.tfvars b/terraform/environments/prod.tfvars index 9e66608..05748ff 100644 --- a/terraform/environments/prod.tfvars +++ b/terraform/environments/prod.tfvars @@ -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" diff --git a/terraform/main.tf b/terraform/main.tf index 4ba021b..80e3f74 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -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 + 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"], + ] } } @@ -322,8 +386,23 @@ resource "aws_db_instance" "main" { backup_retention_period = 7 tags = { - Name = "${var.project_name}-rds" - Environment = var.environment + 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"], + ] } } diff --git a/terraform/user-data.sh b/terraform/user-data.sh index eb3dc71..63445ea 100755 --- a/terraform/user-data.sh +++ b/terraform/user-data.sh @@ -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 # ------------------------------------------------------------------------------- diff --git a/terraform/variables.tf b/terraform/variables.tf index 20c2022..fbaf240 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -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) # -------------------------------------------------------------------------------