Merge PR #3: SSH passphrase support and Terraform security improvements
This commit is contained in:
@@ -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`
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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í.
|
||||
@@ -0,0 +1,7 @@
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
QyNTUxOQAAACAdkhvJ7Il4Yrw1Zqu9P1PcFbdc2HciYqb7tCaj60a+vwAAAJhowcCaaMHA
|
||||
mgAAAAtzc2gtZWQyNTUxOQAAACAdkhvJ7Il4Yrw1Zqu9P1PcFbdc2HciYqb7tCaj60a+vw
|
||||
AAAEBaErHCg+OQP9abpbe9+Eee80Cncbntxz5TXkuqBkA0xx2SG8nsiXhivDVmq70/U9wV
|
||||
t1zYdyJipvu0JqPrRr6/AAAAEG9zaXJpc0BjY3NvZnQuYWkBAgMEBQ==
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
@@ -0,0 +1,7 @@
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
QyNTUxOQAAACC8ad1D22SDWfkWGeO9qwGKrBFJmLe68Kk90aF/skUgYAAAAJimLIkZpiyJ
|
||||
GQAAAAtzc2gtZWQyNTUxOQAAACC8ad1D22SDWfkWGeO9qwGKrBFJmLe68Kk90aF/skUgYA
|
||||
AAAEC+p75hAW4PuYLQcy6dlR0taqeoNNFknaUbOHx7kdEKYrxp3UPbZINZ+RYZ472rAYqs
|
||||
EUmYt7rwqT3RoX+yRSBgAAAAD3Rob3RoQGNjc29mdC5haQECAwQFBg==
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
+91
-12
@@ -1,12 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
# ===============================================================================================================
|
||||
# health-check.sh - Verifica que la API backend esté saludable
|
||||
# health-check.sh - Verifica que la API backend, CloudFront y RDS estén saludables
|
||||
# Descripción:
|
||||
# Realiza peticiones al endpoint de salud de la API con reintentos.
|
||||
# Puede ejecutarse desde la EC2 localmente o desde el pipeline.
|
||||
# Realiza peticiones a múltiples endpoints con reintentos configurables.
|
||||
# Puede ejecutarse desde la EC2 localmente o desde el pipeline CI/CD.
|
||||
# Verifica: API backend (puertos 8080-8084), CloudFront, RDS.
|
||||
#
|
||||
# Uso:
|
||||
# HEALTH_URL=http://localhost:8080/actuator/health bash health-check.sh
|
||||
# ENV=prod CF_DOMAIN=sacc.ccsoft.mx bash health-check.sh
|
||||
#
|
||||
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
||||
# ===============================================================================================================
|
||||
@@ -17,8 +19,12 @@ set -euo pipefail
|
||||
# Configuración
|
||||
# -------------------------------------------------------------------------------
|
||||
HEALTH_URL="${HEALTH_URL:-http://localhost:8080/actuator/health}"
|
||||
MAX_RETRIES="${MAX_RETRIES:-30}"
|
||||
RETRY_INTERVAL="${RETRY_INTERVAL:-2}"
|
||||
CF_DOMAIN="${CF_DOMAIN:-}"
|
||||
DB_HOST="${DB_HOST:-}"
|
||||
DB_PORT="${DB_PORT:-3306}"
|
||||
ENV="${ENV:-dev}"
|
||||
MAX_RETRIES="${MAX_RETRIES:-12}"
|
||||
RETRY_INTERVAL="${RETRY_INTERVAL:-10}"
|
||||
|
||||
readonly CLR_RED='\033[0;31m'
|
||||
readonly CLR_GREEN='\033[0;32m'
|
||||
@@ -32,30 +38,103 @@ _log_error() { echo -e "${CLR_RED}[ERROR]${CLR_NC} $*" >&2; }
|
||||
_log_step() { echo ""; echo -e "${CLR_BLUE}==== $* ====${CLR_NC}"; }
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
# Health Check
|
||||
# Health Check con reintentos
|
||||
# -------------------------------------------------------------------------------
|
||||
main() {
|
||||
_log_step "Health Check: ${HEALTH_URL}"
|
||||
check_with_retry() {
|
||||
local name="$1"
|
||||
local url="$2"
|
||||
local check_cmd="$3"
|
||||
|
||||
_log_step "Verificando: ${name} (${url})"
|
||||
|
||||
local attempt=1
|
||||
while [[ $attempt -le $MAX_RETRIES ]]; do
|
||||
_log_info "Intento ${attempt}/${MAX_RETRIES}..."
|
||||
|
||||
if curl -sf "${HEALTH_URL}" | grep -q '"status":"UP"'; then
|
||||
_log_info "Health check PASÓ - API saludable"
|
||||
if eval "${check_cmd}"; then
|
||||
_log_info "✓ ${name} - SALUDABLE"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ $attempt -lt $MAX_RETRIES ]]; then
|
||||
_log_warn "API no responde aún, reintentando en ${RETRY_INTERVAL}s..."
|
||||
_log_warn "${name} no responde, reintentando en ${RETRY_INTERVAL}s..."
|
||||
sleep "${RETRY_INTERVAL}"
|
||||
fi
|
||||
|
||||
((attempt++))
|
||||
done
|
||||
|
||||
_log_error "Health check FALLÓ después de ${MAX_RETRIES} intentos"
|
||||
_log_error "✗ ${name} - FALLÓ después de ${MAX_RETRIES} intentos"
|
||||
return 1
|
||||
}
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
# Health Check Principal
|
||||
# -------------------------------------------------------------------------------
|
||||
main() {
|
||||
local exit_code=0
|
||||
|
||||
_log_step "Health Check Post-Deploy [${ENV^^}]"
|
||||
echo "Configuración: MAX_RETRIES=${MAX_RETRIES}, INTERVAL=${RETRY_INTERVAL}s"
|
||||
echo ""
|
||||
|
||||
# 1. Verificar API Backend (puerto principal)
|
||||
if ! check_with_retry "API Backend" "${HEALTH_URL}" \
|
||||
"curl -sf '${HEALTH_URL}' | grep -q '\"status\":\"UP\"'"; then
|
||||
exit_code=1
|
||||
fi
|
||||
|
||||
# 2. Verificar puertos adicionales de servicios (8080-8084)
|
||||
local base_url
|
||||
base_url=$(echo "${HEALTH_URL}" | sed 's|/actuator/health||')
|
||||
for port in 8081 8082 8083 8084; do
|
||||
local port_url="${base_url//:[0-9]*/:${port}}/actuator/health"
|
||||
# Solo verificar si la variable del puerto está definida o si es el mismo host
|
||||
if [[ "${base_url}" =~ :[0-9]+ ]]; then
|
||||
port_url="${base_url%:*}:${port}/actuator/health"
|
||||
fi
|
||||
|
||||
# Verificación opcional - no falla el pipeline si un puerto opcional no responde
|
||||
_log_info "Verificando puerto opcional ${port}..."
|
||||
if curl -sf "${port_url}" | grep -q '"status":"UP"' 2>/dev/null; then
|
||||
_log_info "✓ Puerto ${port} - RESPONDE"
|
||||
else
|
||||
_log_warn "Puerto ${port} no responde (opcional)"
|
||||
fi
|
||||
done
|
||||
|
||||
# 3. Verificar CloudFront (si está configurado)
|
||||
if [[ -n "${CF_DOMAIN}" ]]; then
|
||||
if ! check_with_retry "CloudFront" "https://${CF_DOMAIN}/api/health" \
|
||||
"curl -sf -o /dev/null 'https://${CF_DOMAIN}/api/health'"; then
|
||||
exit_code=1
|
||||
fi
|
||||
else
|
||||
_log_warn "CF_DOMAIN no configurado. Saltando verificación de CloudFront."
|
||||
fi
|
||||
|
||||
# 4. Verificar RDS (si está configurado)
|
||||
if [[ -n "${DB_HOST}" ]]; then
|
||||
if command -v nc &> /dev/null; then
|
||||
if ! check_with_retry "RDS" "${DB_HOST}:${DB_PORT}" \
|
||||
"nc -zv '${DB_HOST}' ${DB_PORT}"; then
|
||||
exit_code=1
|
||||
fi
|
||||
else
|
||||
_log_warn "nc (netcat) no instalado. Saltando verificación de RDS."
|
||||
fi
|
||||
else
|
||||
_log_warn "DB_HOST no configurado. Saltando verificación de RDS."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
_log_step "✓ TODOS LOS HEALTH CHECKS PASARON"
|
||||
else
|
||||
_log_step "✗ ALGUNOS HEALTH CHECKS FALLARON"
|
||||
fi
|
||||
|
||||
return $exit_code
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
@@ -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 ==="
|
||||
@@ -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}"
|
||||
@@ -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"
|
||||
|
||||
+92
-13
@@ -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"],
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
# -------------------------------------------------------------------------------
|
||||
|
||||
@@ -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)
|
||||
# -------------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user