Compare commits
10 Commits
b1c0be4ea6
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 720aac1e0d | |||
| aaa2c06c30 | |||
| 557feb02e0 | |||
| 41b2347a33 | |||
| d73d636177 | |||
| 7e0c764f3f | |||
| 2e3627fb66 | |||
| 15e499d970 | |||
| defce6933d | |||
| f32b58fc46 |
@@ -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,245 @@
|
|||||||
|
# 📋 Documentación Completa de Cambios - Proyectosacc PROD
|
||||||
|
|
||||||
|
> Fecha: Mayo 2026
|
||||||
|
> Ambiente: PRODUCCIÓN (AWS Account: 523761210517)
|
||||||
|
> Región: mx-central-1 (AWS México)
|
||||||
|
> Responsable: Área de Tecnología y Desarrollo - CCsoft
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📌 Resumen Ejecutivo
|
||||||
|
|
||||||
|
Este documento consolida todos los cambios realizados a la infraestructura de proyectosacc en producción, incluyendo mejoras de seguridad, correcciones de Terraform, configuración de accesos, y solución de problemas de scheduling automatizado.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Alcance de los Cambios
|
||||||
|
|
||||||
|
### 1. 🔐 Seguridad SSH y Acceso
|
||||||
|
|
||||||
|
#### 1.1 Generación de Nueva Llave SSH
|
||||||
|
- **Archivo**: `keys/thoth_prod_key`
|
||||||
|
- **Tipo**: ED25519
|
||||||
|
- **Passphrase**: `fEbr9CoAlfllHDhocAbRo+aja+SW72a5`
|
||||||
|
- **Usuario**: `thoth`
|
||||||
|
- **Estado**: ✅ Instalada en EC2 PROD (78.13.201.205)
|
||||||
|
|
||||||
|
#### 1.2 Restricción de Acceso SSH
|
||||||
|
- **Antes**: Acceso abierto (`0.0.0.0/0`)
|
||||||
|
- **Después**: Acceso restringido (deshabilitado por defecto)
|
||||||
|
- **Variable**: `allowed_ssh_cidrs` configurable
|
||||||
|
- **Acceso alternativo**: AWS Systems Manager Session Manager
|
||||||
|
|
||||||
|
#### 1.3 AWS Systems Manager (Session Manager)
|
||||||
|
- **Estado**: ✅ Instalado y configurado en EC2
|
||||||
|
- **IAM Permissions**: ✅ Agregados a `proyectosacc-ec2-policy-prod`
|
||||||
|
- **Ventaja**: Acceso seguro sin abrir puertos SSH
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. 🏗️ Infraestructura Terraform
|
||||||
|
|
||||||
|
#### 2.1 Corrección de Backend S3
|
||||||
|
- **Problema**: Bucket incorrecto (`ccsoft-terraform-state`)
|
||||||
|
- **Solución**: Bucket correcto (`ccsoft-proyectosacc-terraform-state-prod`)
|
||||||
|
- **Lockfile**: Cambiado de `dynamodb_table` (deprecado) a `use_lockfile`
|
||||||
|
- **Región**: `mx-central-1` requiere Terraform 1.15.2+
|
||||||
|
|
||||||
|
#### 2.2 Importación de EC2 al Estado
|
||||||
|
- **Instancia**: `i-02428e733083ea877`
|
||||||
|
- **Estado**: Importada correctamente a Terraform state
|
||||||
|
- **AMI**: `ami-00665bcc521d597f1` (Ubuntu Server 22.04 LTS)
|
||||||
|
|
||||||
|
#### 2.3 Lifecycle Rules - Protección Anti-Destrucción
|
||||||
|
|
||||||
|
| Recurso | Protección | Detalle |
|
||||||
|
|---------|-----------|---------|
|
||||||
|
| VPC | prevent_destroy | ✅ No se puede destruir |
|
||||||
|
| EC2 | prevent_destroy + ignore_changes | ✅ AMI, user_data, tags scheduling |
|
||||||
|
| RDS | prevent_destroy + ignore_changes | ✅ Tags scheduling |
|
||||||
|
| S3 Frontend | prevent_destroy | ✅ No se puede destruir |
|
||||||
|
| S3 Artifacts | prevent_destroy | ✅ No se puede destruir |
|
||||||
|
| CloudFront | prevent_destroy | ✅ No se puede destruir |
|
||||||
|
| NAT Gateway | prevent_destroy | ✅ No se puede destruir |
|
||||||
|
|
||||||
|
#### 2.4 Variables Corregidas
|
||||||
|
- **Key Name**: `sacc-prod-key-2026` (antes `ccsoft-prod-key`)
|
||||||
|
- **AMI**: Actualizada para coincidir con instancia existente
|
||||||
|
- **Tags Scheduling**: Preservados vía `ignore_changes`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. 👤 Permisos de Usuario Thoth
|
||||||
|
|
||||||
|
#### 3.1 Configuración Sudo (NOPASSWD)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Archivo: /etc/sudoers.d/thoth
|
||||||
|
# Permisos concedidos:
|
||||||
|
```
|
||||||
|
|
||||||
|
| Comando | Descripción |
|
||||||
|
|---------|-------------|
|
||||||
|
| `/bin/nano /etc/sacc4/sacc4.env` | Editar variables de entorno |
|
||||||
|
| `/bin/systemctl * api-sacc4-*.service` | Control de servicios API |
|
||||||
|
| `/bin/systemctl daemon-reload` | Recargar systemd |
|
||||||
|
| `/opt/sacc4/` | Control total del directorio |
|
||||||
|
| `/bin/chmod`, `/bin/chown` | Permisos de archivos |
|
||||||
|
|
||||||
|
#### 3.2 Acceso SSH
|
||||||
|
- **Llave**: `thoth_prod_key` (ED25519 con passphrase)
|
||||||
|
- **Comando con passphrase**:
|
||||||
|
```bash
|
||||||
|
eval "$(ssh-agent -s)"
|
||||||
|
ssh-add ~/.ssh/thoth_prod_key
|
||||||
|
ssh -i ~/.ssh/thoth_prod_key thoth@78.13.201.205
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. ⏰ Scheduling Automatizado (Lambda START/STOP)
|
||||||
|
|
||||||
|
#### 4.1 Problema Original
|
||||||
|
- **Fecha**: 2026-05-06 19:00:42 UTC
|
||||||
|
- **Error**: `InvalidDBInstanceState` - RDS no estaba detenida
|
||||||
|
- **Causa**: Lambda intentó iniciar RDS que ya estaba corriendo
|
||||||
|
|
||||||
|
#### 4.2 Solución Implementada
|
||||||
|
|
||||||
|
**Lambda START** (`sacc4-start-instances`):
|
||||||
|
- ✅ Valida estado EC2 antes de iniciar (debe estar `stopped`)
|
||||||
|
- ✅ Valida estado RDS antes de iniciar (debe estar `stopped`)
|
||||||
|
- ✅ Logs claros: indica estado actual y acción tomada
|
||||||
|
- ✅ Campos de resultado: `ec2_skipped`, `rds_skipped`
|
||||||
|
|
||||||
|
**Lambda STOP** (`sacc4-stop-instances`):
|
||||||
|
- ✅ Valida estado EC2 antes de detener (debe estar `running`)
|
||||||
|
- ✅ Valida estado RDS antes de detener (debe estar `available`)
|
||||||
|
- ✅ Logs claros: indica estado actual y acción tomada
|
||||||
|
|
||||||
|
#### 4.3 Horarios Configurados
|
||||||
|
|
||||||
|
| Acción | Hora CDMX | Hora UTC | Días | Estado |
|
||||||
|
|--------|-----------|----------|------|--------|
|
||||||
|
| START | 08:00 AM | 13:00 | Lunes-Viernes | ✅ ENABLED |
|
||||||
|
| STOP | 07:00 PM | 00:00 | Martes-Sábado | ✅ ENABLED |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. 🔄 Pipeline Bitbucket (CI/CD)
|
||||||
|
|
||||||
|
#### 5.1 Cambios en `bitbucket-pipelines.yml`
|
||||||
|
|
||||||
|
- **OIDC**: ✅ Configurado para autenticación AWS sin credenciales manuales
|
||||||
|
- **Script**: `aws-oidc-setup.sh` gestiona credenciales temporales automáticamente
|
||||||
|
- **Variables**: Agregado soporte para passphrase SSH
|
||||||
|
- **Steps**: Expandido de 7 a 9 pasos con validaciones
|
||||||
|
|
||||||
|
#### 5.2 PR #3 Fusionado
|
||||||
|
- **Branch**: `feature/ssh-passphrase-dev` → `developer`
|
||||||
|
- **Status**: ✅ MERGED
|
||||||
|
- **Archivos**: 13 archivos, +1,693 líneas
|
||||||
|
- **Cambios clave**:
|
||||||
|
- Soporte passphrase SSH
|
||||||
|
- Terraform lifecycle rules
|
||||||
|
- Sudo configuration para thoth
|
||||||
|
- Session Manager support
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6. ☁️ Estado Actual de Infraestructura AWS
|
||||||
|
|
||||||
|
#### 6.1 Recursos Activos
|
||||||
|
|
||||||
|
| Servicio | ID/Endpoint | Estado | IP/URL |
|
||||||
|
|----------|-------------|--------|--------|
|
||||||
|
| **EC2** | i-02428e733083ea877 | running | 78.13.201.205 |
|
||||||
|
| **RDS** | proyectosacc-db-prod | available | proyectosacc-db-prod.c3uysq6uyyx0.mx-central-1.rds.amazonaws.com |
|
||||||
|
| **NAT Gateway** | nat-0d010c53e7583d761 | available | 78.13.177.201 |
|
||||||
|
| **S3 Frontend** | ccsoft-proyectosacc-frontend-prod | ✅ | — |
|
||||||
|
| **S3 Artifacts** | ccsoft-proyectosacc-artifacts-prod | ✅ | — |
|
||||||
|
| **CloudFront** | E35SPB389PFV1J | Deployed | d46pni5e2nvua.cloudfront.net |
|
||||||
|
| **Lambda START** | sacc4-start-instances | ✅ | Versión 2 |
|
||||||
|
| **Lambda STOP** | sacc4-stop-instances | ✅ | Versión 2 |
|
||||||
|
| **VPC** | vpc-0ed9acf33a45527ad | available | 10.2.0.0/16 |
|
||||||
|
|
||||||
|
#### 6.2 Validación de Duplicados
|
||||||
|
|
||||||
|
| Recurso | Cantidad | ¿Duplicado? |
|
||||||
|
|---------|----------|-------------|
|
||||||
|
| EC2 Instances | 1 | ✅ No |
|
||||||
|
| RDS Instances | 1 | ✅ No |
|
||||||
|
| NAT Gateways | 1 | ✅ No |
|
||||||
|
| Elastic IPs | 2 (1 NAT, 1 EC2) | ✅ No |
|
||||||
|
| Load Balancers | 0 | ✅ No |
|
||||||
|
| Lambda Functions | 2 | ✅ No |
|
||||||
|
| EventBridge Rules | 2 | ✅ No |
|
||||||
|
| CloudFront Distributions | 1 | ✅ No |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 7. 🔑 Gestión de Credenciales AWS
|
||||||
|
|
||||||
|
#### 7.1 SSO Configurado (Recomendado)
|
||||||
|
```bash
|
||||||
|
# Perfil: proyectosacc-sso
|
||||||
|
aws sso login --profile proyectosacc-sso
|
||||||
|
```
|
||||||
|
- **URL**: https://d-9067a6e1d5.awsapps.com/start
|
||||||
|
- **Rol**: PER-AWS-Admins-Infra-Ops
|
||||||
|
- **Cuenta**: 523761210517
|
||||||
|
|
||||||
|
#### 7.2 Pipeline Bitbucket (Automático)
|
||||||
|
- **Método**: OIDC (OpenID Connect)
|
||||||
|
- **Script**: `scripts/aws-oidc-setup.sh`
|
||||||
|
- **Duración**: 3600 segundos (1 hora)
|
||||||
|
- **NO requiere credenciales manuales**
|
||||||
|
|
||||||
|
#### 7.3 Credenciales Temporales (Legacy)
|
||||||
|
- **Perfil**: `proyectosacc-temp` en `~/.aws/credentials`
|
||||||
|
- **Nota**: Caducan rápidamente, usar SSO como alternativa
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 8. 📁 Archivos Clave Modificados
|
||||||
|
|
||||||
|
| Archivo | Cambio | Ruta |
|
||||||
|
|---------|--------|------|
|
||||||
|
| `backend.tf` | Bucket S3 corregido | `terraform/backend.tf` |
|
||||||
|
| `main.tf` | Lifecycle rules + recursos | `terraform/main.tf` |
|
||||||
|
| `variables.tf` | Variables nuevas | `terraform/variables.tf` |
|
||||||
|
| `user-data.sh` | Sudo config para thoth | `terraform/user-data.sh` |
|
||||||
|
| `bitbucket-pipelines.yml` | OIDC + passphrase | raíz |
|
||||||
|
| `aws-oidc-setup.sh` | Setup de credenciales | `scripts/aws-oidc-setup.sh` |
|
||||||
|
| `lambda-scheduler` | Validación de estado | `terraform-sacc4/modules/lambda-scheduler/` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 9. ✅ Checklist de Verificación
|
||||||
|
|
||||||
|
- [x] Llave SSH con passphrase generada e instalada
|
||||||
|
- [x] Terraform backend corregido y funcional
|
||||||
|
- [x] EC2 importada al estado de Terraform
|
||||||
|
- [x] Lifecycle prevent_destroy agregado
|
||||||
|
- [x] Permisos sudo configurados para thoth
|
||||||
|
- [x] Session Manager habilitado
|
||||||
|
- [x] Lambdas START/STOP corregidas y desplegadas
|
||||||
|
- [x] EventBridge rules configuradas
|
||||||
|
- [x] Pipeline Bitbucket con OIDC
|
||||||
|
- [x] No hay recursos duplicados
|
||||||
|
- [x] AWS SSO configurado localmente
|
||||||
|
- [x] Terraform apply exitoso (2 creados, 5 modificados, 0 destruidos)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 10. 🚨 Notas Importantes
|
||||||
|
|
||||||
|
1. **NO ejecutar `terraform destroy`** — los recursos críticos tienen `prevent_destroy`
|
||||||
|
2. **Las credenciales temporales caducan** — usar `aws sso login` para renovar
|
||||||
|
3. **Las Lambdas ahora validan estado** — no fallarán por estados inválidos
|
||||||
|
4. **El scheduling es L-V** — fines de semana no hay acciones automáticas
|
||||||
|
5. **Backup de estado Terraform** — se mantiene en S3 con versionado
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Documento generado automáticamente - Cómputo Contable Soft SA de CV*
|
||||||
@@ -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
|
||||||
+237
-33
@@ -1,7 +1,7 @@
|
|||||||
# ===============================================================================================================
|
# ===============================================================================================================
|
||||||
# bitbucket-pipelines.yml - Pipeline CI/CD para proyectosacc
|
# bitbucket-pipelines.yml - Pipeline CI/CD para proyectosacc
|
||||||
# Descripción:
|
# 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.
|
# frontend React (S3+CloudFront) y API backend (EC2) de SACC.
|
||||||
#
|
#
|
||||||
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
||||||
@@ -68,19 +68,40 @@ pipelines:
|
|||||||
name: 01_image-setup
|
name: 01_image-setup
|
||||||
script:
|
script:
|
||||||
- set -euo pipefail
|
- 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"
|
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
|
||||||
- unzip -q awscliv2.zip
|
- unzip -q awscliv2.zip
|
||||||
- ./aws/install
|
- ./aws/install
|
||||||
- aws --version
|
- aws --version
|
||||||
- mkdir -p ~/.ssh
|
- 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
|
- 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_BOT_TOKEN="${DEV_TELEGRAM_BOT_TOKEN}"
|
||||||
- export TELEGRAM_CHAT_ID="${DEV_TELEGRAM_CHAT_ID}"
|
- export TELEGRAM_CHAT_ID="${DEV_TELEGRAM_CHAT_ID}"
|
||||||
- bash scripts/telegram-pipeline-notify.sh start
|
- 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:
|
- step:
|
||||||
name: 03_terraform
|
name: 03_terraform
|
||||||
oidc: true
|
oidc: true
|
||||||
@@ -99,8 +120,15 @@ pipelines:
|
|||||||
- terraform version
|
- terraform version
|
||||||
- export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-mx-central-1}"
|
- export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-mx-central-1}"
|
||||||
- terraform init -backend-config=backend.dev.hcl
|
- terraform init -backend-config=backend.dev.hcl
|
||||||
- terraform plan -var-file=environments/dev.tfvars -var="db_password=${DEV_DB_PASSWORD}" -out=dev.tfplan
|
- echo "=== Ejecutando Terraform Plan (solo verificación) ==="
|
||||||
- terraform apply -auto-approve dev.tfplan
|
- 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
|
- terraform output -json > terraform-outputs.json
|
||||||
- cat terraform-outputs.json
|
- cat terraform-outputs.json
|
||||||
artifacts:
|
artifacts:
|
||||||
@@ -154,10 +182,35 @@ pipelines:
|
|||||||
- echo "Publish condicional completado."
|
- echo "Publish condicional completado."
|
||||||
|
|
||||||
- step:
|
- step:
|
||||||
name: 06_install
|
name: 06_update_ssh_keys
|
||||||
script:
|
script:
|
||||||
- set -euo pipefail
|
- set -euo pipefail
|
||||||
- apt-get update -y && apt-get install -y curl unzip
|
- 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"
|
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
|
||||||
- unzip -q awscliv2.zip
|
- unzip -q awscliv2.zip
|
||||||
- ./aws/install
|
- ./aws/install
|
||||||
@@ -171,12 +224,20 @@ pipelines:
|
|||||||
fi
|
fi
|
||||||
if [ "${HAS_LOCAL_JAR}" = "true" ]; then
|
if [ "${HAS_LOCAL_JAR}" = "true" ]; then
|
||||||
echo "INFO: Artefacto JAR encontrado localmente. Procediendo con instalación en servidor."
|
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
|
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 \
|
-i ~/.ssh/sacc4_key \
|
||||||
-o StrictHostKeyChecking=no \
|
-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'"
|
"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
|
else
|
||||||
echo "INFO: No se encontró artefacto JAR localmente. Saltando instalación."
|
echo "INFO: No se encontró artefacto JAR localmente. Saltando instalación."
|
||||||
@@ -184,23 +245,32 @@ pipelines:
|
|||||||
- echo "Install condicional completado."
|
- echo "Install condicional completado."
|
||||||
|
|
||||||
- step:
|
- step:
|
||||||
name: 07_deploy
|
name: 08_deploy
|
||||||
oidc: true
|
oidc: true
|
||||||
script:
|
script:
|
||||||
- set -euo pipefail
|
- set -euo pipefail
|
||||||
- apt-get update -y && apt-get install -y curl unzip
|
- 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"
|
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
|
||||||
- unzip -q awscliv2.zip
|
- unzip -q awscliv2.zip
|
||||||
- ./aws/install
|
- ./aws/install
|
||||||
- aws --version
|
- aws --version
|
||||||
- source scripts/aws-oidc-setup.sh dev
|
- 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
|
- 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 \
|
-i ~/.ssh/sacc4_key \
|
||||||
-o StrictHostKeyChecking=no \
|
-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'"
|
"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
|
if [ -f terraform/terraform-outputs.json ]; then
|
||||||
@@ -213,24 +283,72 @@ pipelines:
|
|||||||
- export TELEGRAM_CHAT_ID="${DEV_TELEGRAM_CHAT_ID}"
|
- export TELEGRAM_CHAT_ID="${DEV_TELEGRAM_CHAT_ID}"
|
||||||
- bash scripts/telegram-pipeline-notify.sh success "Deploy condicional completado"
|
- 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:
|
master:
|
||||||
- step:
|
- step:
|
||||||
name: 01_image-setup
|
name: 01_image-setup
|
||||||
script:
|
script:
|
||||||
- set -euo pipefail
|
- 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"
|
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
|
||||||
- unzip -q awscliv2.zip
|
- unzip -q awscliv2.zip
|
||||||
- ./aws/install
|
- ./aws/install
|
||||||
- aws --version
|
- aws --version
|
||||||
- mkdir -p ~/.ssh
|
- 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
|
- 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_BOT_TOKEN="${PROD_TELEGRAM_BOT_TOKEN}"
|
||||||
- export TELEGRAM_CHAT_ID="${PROD_TELEGRAM_CHAT_ID}"
|
- export TELEGRAM_CHAT_ID="${PROD_TELEGRAM_CHAT_ID}"
|
||||||
- bash scripts/telegram-pipeline-notify.sh start
|
- 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:
|
- step:
|
||||||
name: 03_terraform
|
name: 03_terraform
|
||||||
oidc: true
|
oidc: true
|
||||||
@@ -249,12 +367,18 @@ pipelines:
|
|||||||
- terraform version
|
- terraform version
|
||||||
- export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-mx-central-1}"
|
- export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-mx-central-1}"
|
||||||
- terraform init -backend-config=backend.prod.hcl
|
- terraform init -backend-config=backend.prod.hcl
|
||||||
- terraform plan -var-file=environments/prod.tfvars -var="db_password=${PROD_DB_PASSWORD}" -out=prod.tfplan
|
# NOTA DE SEGURIDAD: En PROD nunca usar -auto-approve sin revisión manual.
|
||||||
- terraform apply -auto-approve prod.tfplan
|
# 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
|
- terraform output -json > terraform-outputs.json
|
||||||
- cat terraform-outputs.json
|
- cat terraform-outputs.json
|
||||||
artifacts:
|
artifacts:
|
||||||
- terraform/terraform-outputs.json
|
- terraform/terraform-outputs.json
|
||||||
|
- terraform/prod.tfplan
|
||||||
|
|
||||||
- step:
|
- step:
|
||||||
name: 04_build
|
name: 04_build
|
||||||
@@ -304,10 +428,37 @@ pipelines:
|
|||||||
- echo "Publish condicional completado."
|
- echo "Publish condicional completado."
|
||||||
|
|
||||||
- step:
|
- step:
|
||||||
name: 06_install
|
name: 06_update_ssh_keys
|
||||||
script:
|
script:
|
||||||
- set -euo pipefail
|
- set -euo pipefail
|
||||||
- apt-get update -y && apt-get install -y curl unzip
|
- 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"
|
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
|
||||||
- unzip -q awscliv2.zip
|
- unzip -q awscliv2.zip
|
||||||
- ./aws/install
|
- ./aws/install
|
||||||
@@ -321,12 +472,20 @@ pipelines:
|
|||||||
fi
|
fi
|
||||||
if [ "${HAS_LOCAL_JAR}" = "true" ]; then
|
if [ "${HAS_LOCAL_JAR}" = "true" ]; then
|
||||||
echo "INFO: Artefacto JAR encontrado localmente. Procediendo con instalación en servidor."
|
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
|
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 \
|
-i ~/.ssh/sacc4_key \
|
||||||
-o StrictHostKeyChecking=no \
|
-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'"
|
"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
|
else
|
||||||
echo "INFO: No se encontró artefacto JAR localmente. Saltando instalación."
|
echo "INFO: No se encontró artefacto JAR localmente. Saltando instalación."
|
||||||
@@ -334,7 +493,7 @@ pipelines:
|
|||||||
- echo "Install condicional completado."
|
- echo "Install condicional completado."
|
||||||
|
|
||||||
- step:
|
- step:
|
||||||
name: 06b_notify_approval
|
name: 07b_notify_approval
|
||||||
script:
|
script:
|
||||||
- set -euo pipefail
|
- set -euo pipefail
|
||||||
- export TELEGRAM_BOT_TOKEN="${PROD_TELEGRAM_BOT_TOKEN}"
|
- export TELEGRAM_BOT_TOKEN="${PROD_TELEGRAM_BOT_TOKEN}"
|
||||||
@@ -343,25 +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."
|
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:
|
- step:
|
||||||
name: 07_deploy
|
name: 08_deploy
|
||||||
oidc: true
|
oidc: true
|
||||||
deployment: production
|
deployment: production
|
||||||
trigger: manual
|
trigger: manual
|
||||||
script:
|
script:
|
||||||
- set -euo pipefail
|
- set -euo pipefail
|
||||||
- apt-get update -y && apt-get install -y curl unzip
|
- 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"
|
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
|
||||||
- unzip -q awscliv2.zip
|
- unzip -q awscliv2.zip
|
||||||
- ./aws/install
|
- ./aws/install
|
||||||
- aws --version
|
- aws --version
|
||||||
- source scripts/aws-oidc-setup.sh prod
|
- source scripts/aws-oidc-setup.sh prod
|
||||||
- echo "${PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
|
# Aplicar Terraform plan previamente generado y guardado en artifact
|
||||||
- chmod 600 ~/.ssh/sacc4_key
|
|
||||||
- |
|
- |
|
||||||
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 \
|
-i ~/.ssh/sacc4_key \
|
||||||
-o StrictHostKeyChecking=no \
|
-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'"
|
"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
|
if [ -f terraform/terraform-outputs.json ]; then
|
||||||
@@ -373,3 +550,30 @@ pipelines:
|
|||||||
- export TELEGRAM_BOT_TOKEN="${PROD_TELEGRAM_BOT_TOKEN}"
|
- export TELEGRAM_BOT_TOKEN="${PROD_TELEGRAM_BOT_TOKEN}"
|
||||||
- export TELEGRAM_CHAT_ID="${PROD_TELEGRAM_CHAT_ID}"
|
- export TELEGRAM_CHAT_ID="${PROD_TELEGRAM_CHAT_ID}"
|
||||||
- bash scripts/telegram-pipeline-notify.sh success "CloudFront invalidado | Deploy a PROD aprobado y completado"
|
- 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,394 @@
|
|||||||
|
{
|
||||||
|
"type": "excalidraw",
|
||||||
|
"version": 2,
|
||||||
|
"source": "https://excalidraw.com",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"id": "title",
|
||||||
|
"type": "text",
|
||||||
|
"x": 400,
|
||||||
|
"y": 30,
|
||||||
|
"width": 500,
|
||||||
|
"height": 40,
|
||||||
|
"text": "Arquitectura AWS - Proyectosacc PROD",
|
||||||
|
"fontSize": 28,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "users",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 50,
|
||||||
|
"y": 150,
|
||||||
|
"width": 150,
|
||||||
|
"height": 80,
|
||||||
|
"backgroundColor": "#e7f5ff",
|
||||||
|
"strokeColor": "#1971c2",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "users_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 60,
|
||||||
|
"y": 170,
|
||||||
|
"width": 130,
|
||||||
|
"height": 40,
|
||||||
|
"text": "Usuarios\\n(Internet)",
|
||||||
|
"fontSize": 16,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "route53",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 300,
|
||||||
|
"y": 150,
|
||||||
|
"width": 150,
|
||||||
|
"height": 80,
|
||||||
|
"backgroundColor": "#fff3bf",
|
||||||
|
"strokeColor": "#f08c00",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "route53_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 310,
|
||||||
|
"y": 165,
|
||||||
|
"width": 130,
|
||||||
|
"height": 50,
|
||||||
|
"text": "Route 53\\nprod-sacc.ccsoft.mx",
|
||||||
|
"fontSize": 14,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cloudfront",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 550,
|
||||||
|
"y": 150,
|
||||||
|
"width": 180,
|
||||||
|
"height": 80,
|
||||||
|
"backgroundColor": "#e7f5ff",
|
||||||
|
"strokeColor": "#1971c2",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cloudfront_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 560,
|
||||||
|
"y": 165,
|
||||||
|
"width": 160,
|
||||||
|
"height": 50,
|
||||||
|
"text": "CloudFront CDN\\nE35SPB389PFV1J",
|
||||||
|
"fontSize": 14,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "s3_frontend",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 550,
|
||||||
|
"y": 300,
|
||||||
|
"width": 180,
|
||||||
|
"height": 80,
|
||||||
|
"backgroundColor": "#d3f9d8",
|
||||||
|
"strokeColor": "#2b8a3e",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "s3_frontend_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 560,
|
||||||
|
"y": 310,
|
||||||
|
"width": 160,
|
||||||
|
"height": 60,
|
||||||
|
"text": "S3 Frontend\\nccsoft-proyectosacc\\n-frontend-prod",
|
||||||
|
"fontSize": 12,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ec2",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 800,
|
||||||
|
"y": 150,
|
||||||
|
"width": 200,
|
||||||
|
"height": 100,
|
||||||
|
"backgroundColor": "#ffe0e0",
|
||||||
|
"strokeColor": "#c92a2a",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ec2_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 810,
|
||||||
|
"y": 160,
|
||||||
|
"width": 180,
|
||||||
|
"height": 80,
|
||||||
|
"text": "EC2 API Backend\\ni-02428e733083ea877\\n78.13.201.205\\nt3.small",
|
||||||
|
"fontSize": 12,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "rds",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 800,
|
||||||
|
"y": 300,
|
||||||
|
"width": 200,
|
||||||
|
"height": 80,
|
||||||
|
"backgroundColor": "#e7f5ff",
|
||||||
|
"strokeColor": "#1971c2",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "rds_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 810,
|
||||||
|
"y": 310,
|
||||||
|
"width": 180,
|
||||||
|
"height": 60,
|
||||||
|
"text": "RDS MariaDB\\nproyectosacc-db-prod\\ndb.t3.micro",
|
||||||
|
"fontSize": 12,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "s3_artifacts",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 550,
|
||||||
|
"y": 420,
|
||||||
|
"width": 180,
|
||||||
|
"height": 80,
|
||||||
|
"backgroundColor": "#d3f9d8",
|
||||||
|
"strokeColor": "#2b8a3e",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "s3_artifacts_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 560,
|
||||||
|
"y": 430,
|
||||||
|
"width": 160,
|
||||||
|
"height": 60,
|
||||||
|
"text": "S3 Artifacts\\nccsoft-proyectosacc\\n-artifacts-prod",
|
||||||
|
"fontSize": 12,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "lambda_start",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 300,
|
||||||
|
"y": 300,
|
||||||
|
"width": 150,
|
||||||
|
"height": 60,
|
||||||
|
"backgroundColor": "#ffd43b",
|
||||||
|
"strokeColor": "#e67700",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "lambda_start_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 310,
|
||||||
|
"y": 310,
|
||||||
|
"width": 130,
|
||||||
|
"height": 40,
|
||||||
|
"text": "Lambda START\\n8:00 AM L-V",
|
||||||
|
"fontSize": 12,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "lambda_stop",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 300,
|
||||||
|
"y": 380,
|
||||||
|
"width": 150,
|
||||||
|
"height": 60,
|
||||||
|
"backgroundColor": "#ffd43b",
|
||||||
|
"strokeColor": "#e67700",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "lambda_stop_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 310,
|
||||||
|
"y": 390,
|
||||||
|
"width": 130,
|
||||||
|
"height": 40,
|
||||||
|
"text": "Lambda STOP\\n7:00 PM L-V",
|
||||||
|
"fontSize": 12,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "eventbridge",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 50,
|
||||||
|
"y": 330,
|
||||||
|
"width": 150,
|
||||||
|
"height": 80,
|
||||||
|
"backgroundColor": "#f3d9fa",
|
||||||
|
"strokeColor": "#862e9c",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "eventbridge_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 60,
|
||||||
|
"y": 340,
|
||||||
|
"width": 130,
|
||||||
|
"height": 60,
|
||||||
|
"text": "EventBridge\\nScheduler\\ncron L-V",
|
||||||
|
"fontSize": 12,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow1",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 200,
|
||||||
|
"y": 190,
|
||||||
|
"points": [[0,0], [100,0]],
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"strokeWidth": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow2",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 450,
|
||||||
|
"y": 190,
|
||||||
|
"points": [[0,0], [100,0]],
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"strokeWidth": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow3",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 730,
|
||||||
|
"y": 190,
|
||||||
|
"points": [[0,0], [70,0]],
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"strokeWidth": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow4",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 900,
|
||||||
|
"y": 250,
|
||||||
|
"points": [[0,0], [0,50]],
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"strokeWidth": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow5",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 640,
|
||||||
|
"y": 230,
|
||||||
|
"points": [[0,0], [0,70]],
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"strokeWidth": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow6",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 640,
|
||||||
|
"y": 380,
|
||||||
|
"points": [[0,0], [0,40]],
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"strokeWidth": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow7",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 200,
|
||||||
|
"y": 370,
|
||||||
|
"points": [[0,0], [100,0]],
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"strokeWidth": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow8",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 450,
|
||||||
|
"y": 330,
|
||||||
|
"points": [[0,0], [100,0]],
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"strokeWidth": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow9",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 450,
|
||||||
|
"y": 410,
|
||||||
|
"points": [[0,0], [100,0]],
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"strokeWidth": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "vpc_box",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 750,
|
||||||
|
"y": 120,
|
||||||
|
"width": 300,
|
||||||
|
"height": 300,
|
||||||
|
"backgroundColor": "transparent",
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"roughness": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "vpc_label",
|
||||||
|
"type": "text",
|
||||||
|
"x": 850,
|
||||||
|
"y": 125,
|
||||||
|
"width": 100,
|
||||||
|
"height": 20,
|
||||||
|
"text": "VPC 10.2.0.0/16",
|
||||||
|
"fontSize": 12,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"appState": {
|
||||||
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"gridSize": 20
|
||||||
|
},
|
||||||
|
"files": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,431 @@
|
|||||||
|
{
|
||||||
|
"type": "excalidraw",
|
||||||
|
"version": 2,
|
||||||
|
"source": "https://excalidraw.com",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"id": "title",
|
||||||
|
"type": "text",
|
||||||
|
"x": 300,
|
||||||
|
"y": 20,
|
||||||
|
"width": 600,
|
||||||
|
"height": 40,
|
||||||
|
"text": "Pipeline CI/CD - Bitbucket to AWS PROD",
|
||||||
|
"fontSize": 28,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dev_branch",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 50,
|
||||||
|
"y": 120,
|
||||||
|
"width": 140,
|
||||||
|
"height": 60,
|
||||||
|
"backgroundColor": "#a5d8ff",
|
||||||
|
"strokeColor": "#1971c2",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dev_branch_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 60,
|
||||||
|
"y": 130,
|
||||||
|
"width": 120,
|
||||||
|
"height": 40,
|
||||||
|
"text": "Branch\\ndeveloper",
|
||||||
|
"fontSize": 14,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "step1",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 250,
|
||||||
|
"y": 120,
|
||||||
|
"width": 140,
|
||||||
|
"height": 60,
|
||||||
|
"backgroundColor": "#d3f9d8",
|
||||||
|
"strokeColor": "#2b8a3e",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "step1_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 260,
|
||||||
|
"y": 135,
|
||||||
|
"width": 120,
|
||||||
|
"height": 30,
|
||||||
|
"text": "01_image-setup",
|
||||||
|
"fontSize": 14,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "step2",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 450,
|
||||||
|
"y": 120,
|
||||||
|
"width": 140,
|
||||||
|
"height": 60,
|
||||||
|
"backgroundColor": "#d3f9d8",
|
||||||
|
"strokeColor": "#2b8a3e",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "step2_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 460,
|
||||||
|
"y": 135,
|
||||||
|
"width": 120,
|
||||||
|
"height": 30,
|
||||||
|
"text": "02_repo-config",
|
||||||
|
"fontSize": 14,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "step3",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 650,
|
||||||
|
"y": 120,
|
||||||
|
"width": 140,
|
||||||
|
"height": 60,
|
||||||
|
"backgroundColor": "#d3f9d8",
|
||||||
|
"strokeColor": "#2b8a3e",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "step3_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 660,
|
||||||
|
"y": 135,
|
||||||
|
"width": 120,
|
||||||
|
"height": 30,
|
||||||
|
"text": "03_deps",
|
||||||
|
"fontSize": 14,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "step4",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 850,
|
||||||
|
"y": 120,
|
||||||
|
"width": 140,
|
||||||
|
"height": 60,
|
||||||
|
"backgroundColor": "#d3f9d8",
|
||||||
|
"strokeColor": "#2b8a3e",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "step4_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 860,
|
||||||
|
"y": 135,
|
||||||
|
"width": 120,
|
||||||
|
"height": 30,
|
||||||
|
"text": "04_build",
|
||||||
|
"fontSize": 14,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "step5",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 250,
|
||||||
|
"y": 220,
|
||||||
|
"width": 140,
|
||||||
|
"height": 60,
|
||||||
|
"backgroundColor": "#fff3bf",
|
||||||
|
"strokeColor": "#f08c00",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "step5_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 260,
|
||||||
|
"y": 235,
|
||||||
|
"width": 120,
|
||||||
|
"height": 30,
|
||||||
|
"text": "05_publish",
|
||||||
|
"fontSize": 14,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "step6",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 450,
|
||||||
|
"y": 220,
|
||||||
|
"width": 140,
|
||||||
|
"height": 60,
|
||||||
|
"backgroundColor": "#fff3bf",
|
||||||
|
"strokeColor": "#f08c00",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "step6_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 460,
|
||||||
|
"y": 235,
|
||||||
|
"width": 120,
|
||||||
|
"height": 30,
|
||||||
|
"text": "06_install",
|
||||||
|
"fontSize": 14,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "step7",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 650,
|
||||||
|
"y": 220,
|
||||||
|
"width": 140,
|
||||||
|
"height": 60,
|
||||||
|
"backgroundColor": "#ffd43b",
|
||||||
|
"strokeColor": "#e67700",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "step7_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 660,
|
||||||
|
"y": 235,
|
||||||
|
"width": 120,
|
||||||
|
"height": 30,
|
||||||
|
"text": "07_deploy",
|
||||||
|
"fontSize": 14,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "oidc",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 850,
|
||||||
|
"y": 220,
|
||||||
|
"width": 180,
|
||||||
|
"height": 80,
|
||||||
|
"backgroundColor": "#e7f5ff",
|
||||||
|
"strokeColor": "#1971c2",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "oidc_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 860,
|
||||||
|
"y": 235,
|
||||||
|
"width": 160,
|
||||||
|
"height": 50,
|
||||||
|
"text": "AWS OIDC\\nAuth automática",
|
||||||
|
"fontSize": 14,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "aws_prod",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 650,
|
||||||
|
"y": 340,
|
||||||
|
"width": 200,
|
||||||
|
"height": 100,
|
||||||
|
"backgroundColor": "#ffe0e0",
|
||||||
|
"strokeColor": "#c92a2a",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "aws_prod_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 660,
|
||||||
|
"y": 350,
|
||||||
|
"width": 180,
|
||||||
|
"height": 80,
|
||||||
|
"text": "AWS PROD\\nmx-central-1\\nAccount: 523761210517",
|
||||||
|
"fontSize": 14,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "master_branch",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 50,
|
||||||
|
"y": 340,
|
||||||
|
"width": 140,
|
||||||
|
"height": 60,
|
||||||
|
"backgroundColor": "#ffc9c9",
|
||||||
|
"strokeColor": "#c92a2a",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "master_branch_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 60,
|
||||||
|
"y": 350,
|
||||||
|
"width": 120,
|
||||||
|
"height": 40,
|
||||||
|
"text": "Branch\\nmaster (PROD)",
|
||||||
|
"fontSize": 14,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "merge_arrow",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 120,
|
||||||
|
"y": 180,
|
||||||
|
"points": [[0,0], [0,160]],
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"strokeWidth": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "merge_label",
|
||||||
|
"type": "text",
|
||||||
|
"x": 130,
|
||||||
|
"y": 240,
|
||||||
|
"width": 100,
|
||||||
|
"height": 20,
|
||||||
|
"text": "Merge PR",
|
||||||
|
"fontSize": 12,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "left",
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow_dev_step1",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 190,
|
||||||
|
"y": 150,
|
||||||
|
"points": [[0,0], [60,0]],
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"strokeWidth": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow_s1_s2",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 390,
|
||||||
|
"y": 150,
|
||||||
|
"points": [[0,0], [60,0]],
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"strokeWidth": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow_s2_s3",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 590,
|
||||||
|
"y": 150,
|
||||||
|
"points": [[0,0], [60,0]],
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"strokeWidth": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow_s3_s4",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 790,
|
||||||
|
"y": 150,
|
||||||
|
"points": [[0,0], [60,0]],
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"strokeWidth": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow_s4_s5",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 920,
|
||||||
|
"y": 180,
|
||||||
|
"points": [[0,0], [0,40], [-570,0]],
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"strokeWidth": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow_s5_s6",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 390,
|
||||||
|
"y": 250,
|
||||||
|
"points": [[0,0], [60,0]],
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"strokeWidth": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow_s6_s7",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 590,
|
||||||
|
"y": 250,
|
||||||
|
"points": [[0,0], [60,0]],
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"strokeWidth": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow_s7_aws",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 750,
|
||||||
|
"y": 280,
|
||||||
|
"points": [[0,0], [0,60]],
|
||||||
|
"strokeColor": "#c92a2a",
|
||||||
|
"strokeWidth": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow_oidc_aws",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 940,
|
||||||
|
"y": 300,
|
||||||
|
"points": [[0,0], [0,40], [-90,0]],
|
||||||
|
"strokeColor": "#1971c2",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"strokeStyle": "dashed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "legend",
|
||||||
|
"type": "text",
|
||||||
|
"x": 50,
|
||||||
|
"y": 450,
|
||||||
|
"width": 400,
|
||||||
|
"height": 120,
|
||||||
|
"text": "Leyenda:\\n🟦 Azul: Código/branch\\n🟩 Verde: Build steps\\n🟨 Amarillo: Deploy steps\\n🟥 Rojo: Producción/AWS",
|
||||||
|
"fontSize": 14,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "left",
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"appState": {
|
||||||
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"gridSize": 20
|
||||||
|
},
|
||||||
|
"files": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,394 @@
|
|||||||
|
{
|
||||||
|
"type": "excalidraw",
|
||||||
|
"version": 2,
|
||||||
|
"source": "https://excalidraw.com",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"id": "title",
|
||||||
|
"type": "text",
|
||||||
|
"x": 300,
|
||||||
|
"y": 20,
|
||||||
|
"width": 600,
|
||||||
|
"height": 40,
|
||||||
|
"text": "Seguridad y Acceso - Proyectosacc PROD",
|
||||||
|
"fontSize": 28,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "internet",
|
||||||
|
"type": "ellipse",
|
||||||
|
"x": 50,
|
||||||
|
"y": 120,
|
||||||
|
"width": 120,
|
||||||
|
"height": 80,
|
||||||
|
"backgroundColor": "#e7f5ff",
|
||||||
|
"strokeColor": "#1971c2",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "internet_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 60,
|
||||||
|
"y": 140,
|
||||||
|
"width": 100,
|
||||||
|
"height": 40,
|
||||||
|
"text": "Internet",
|
||||||
|
"fontSize": 16,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cloudfront_box",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 250,
|
||||||
|
"y": 120,
|
||||||
|
"width": 160,
|
||||||
|
"height": 80,
|
||||||
|
"backgroundColor": "#e7f5ff",
|
||||||
|
"strokeColor": "#1971c2",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cloudfront_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 260,
|
||||||
|
"y": 135,
|
||||||
|
"width": 140,
|
||||||
|
"height": 50,
|
||||||
|
"text": "CloudFront\\nCDN + WAF",
|
||||||
|
"fontSize": 14,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ssh_restricted",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 250,
|
||||||
|
"y": 250,
|
||||||
|
"width": 160,
|
||||||
|
"height": 80,
|
||||||
|
"backgroundColor": "#ffc9c9",
|
||||||
|
"strokeColor": "#c92a2a",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ssh_restricted_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 260,
|
||||||
|
"y": 260,
|
||||||
|
"width": 140,
|
||||||
|
"height": 60,
|
||||||
|
"text": "SSH Restringido\\nSolo México IPs\\nPassphrase req.",
|
||||||
|
"fontSize": 12,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#c92a2a",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "session_manager",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 450,
|
||||||
|
"y": 250,
|
||||||
|
"width": 160,
|
||||||
|
"height": 80,
|
||||||
|
"backgroundColor": "#d3f9d8",
|
||||||
|
"strokeColor": "#2b8a3e",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "session_manager_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 460,
|
||||||
|
"y": 260,
|
||||||
|
"width": 140,
|
||||||
|
"height": 60,
|
||||||
|
"text": "Session Manager\\nAWS Systems\\nManager",
|
||||||
|
"fontSize": 12,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#2b8a3e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ec2_server",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 680,
|
||||||
|
"y": 120,
|
||||||
|
"width": 200,
|
||||||
|
"height": 120,
|
||||||
|
"backgroundColor": "#fff3bf",
|
||||||
|
"strokeColor": "#f08c00",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ec2_server_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 690,
|
||||||
|
"y": 130,
|
||||||
|
"width": 180,
|
||||||
|
"height": 100,
|
||||||
|
"text": "EC2 PROD\\ni-02428e733083ea877\\n78.13.201.205\\nUsuario: thoth\\nSudo: NOPASSWD",
|
||||||
|
"fontSize": 12,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "rds_db",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 680,
|
||||||
|
"y": 300,
|
||||||
|
"width": 200,
|
||||||
|
"height": 80,
|
||||||
|
"backgroundColor": "#e7f5ff",
|
||||||
|
"strokeColor": "#1971c2",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "rds_db_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 690,
|
||||||
|
"y": 310,
|
||||||
|
"width": 180,
|
||||||
|
"height": 60,
|
||||||
|
"text": "RDS MariaDB\\nproyectosacc-db-prod\\nEncrypted + Backup",
|
||||||
|
"fontSize": 12,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "security_group",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 930,
|
||||||
|
"y": 120,
|
||||||
|
"width": 180,
|
||||||
|
"height": 100,
|
||||||
|
"backgroundColor": "#f3d9fa",
|
||||||
|
"strokeColor": "#862e9c",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "security_group_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 940,
|
||||||
|
"y": 130,
|
||||||
|
"width": 160,
|
||||||
|
"height": 80,
|
||||||
|
"text": "Security Groups\\n✓ Puerto 80/443\\n✓ Puerto 8080-8085\\n✗ SSH 0.0.0.0/0",
|
||||||
|
"fontSize": 11,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "left",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "iam_role",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 930,
|
||||||
|
"y": 250,
|
||||||
|
"width": 180,
|
||||||
|
"height": 80,
|
||||||
|
"backgroundColor": "#ffd43b",
|
||||||
|
"strokeColor": "#e67700",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "iam_role_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 940,
|
||||||
|
"y": 260,
|
||||||
|
"width": 160,
|
||||||
|
"height": 60,
|
||||||
|
"text": "IAM Roles\\nEC2 Role\\nSSM Permissions\\nS3 Access",
|
||||||
|
"fontSize": 11,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "left",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "bitbucket_pipeline",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 50,
|
||||||
|
"y": 400,
|
||||||
|
"width": 180,
|
||||||
|
"height": 80,
|
||||||
|
"backgroundColor": "#a5d8ff",
|
||||||
|
"strokeColor": "#1971c2",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "bitbucket_pipeline_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 60,
|
||||||
|
"y": 410,
|
||||||
|
"width": 160,
|
||||||
|
"height": 60,
|
||||||
|
"text": "Bitbucket Pipeline\\nOIDC Auth\\nAuto-deploy",
|
||||||
|
"fontSize": 12,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "oidc_auth",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 300,
|
||||||
|
"y": 400,
|
||||||
|
"width": 160,
|
||||||
|
"height": 80,
|
||||||
|
"backgroundColor": "#d3f9d8",
|
||||||
|
"strokeColor": "#2b8a3e",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"fillStyle": "solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "oidc_auth_text",
|
||||||
|
"type": "text",
|
||||||
|
"x": 310,
|
||||||
|
"y": 410,
|
||||||
|
"width": 140,
|
||||||
|
"height": 60,
|
||||||
|
"text": "AWS SSO/OIDC\\nNo credentials\\nAutomatic",
|
||||||
|
"fontSize": 12,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#2b8a3e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow_internet_cf",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 170,
|
||||||
|
"y": 160,
|
||||||
|
"points": [[0,0], [80,0]],
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"strokeWidth": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow_cf_ec2",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 410,
|
||||||
|
"y": 160,
|
||||||
|
"points": [[0,0], [70,0]],
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"strokeWidth": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow_ssh_ec2",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 410,
|
||||||
|
"y": 290,
|
||||||
|
"points": [[0,0], [270,0]],
|
||||||
|
"strokeColor": "#c92a2a",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"strokeStyle": "dashed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow_ssm_ec2",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 610,
|
||||||
|
"y": 290,
|
||||||
|
"points": [[0,0], [70,0]],
|
||||||
|
"strokeColor": "#2b8a3e",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"strokeStyle": "dashed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow_ec2_rds",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 780,
|
||||||
|
"y": 240,
|
||||||
|
"points": [[0,0], [0,60]],
|
||||||
|
"strokeColor": "#495057",
|
||||||
|
"strokeWidth": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow_bb_oidc",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 230,
|
||||||
|
"y": 440,
|
||||||
|
"points": [[0,0], [70,0]],
|
||||||
|
"strokeColor": "#2b8a3e",
|
||||||
|
"strokeWidth": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "arrow_oidc_aws",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 460,
|
||||||
|
"y": 440,
|
||||||
|
"points": [[0,0], [220,0]],
|
||||||
|
"strokeColor": "#2b8a3e",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"strokeStyle": "dashed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "prevent_destroy_label",
|
||||||
|
"type": "text",
|
||||||
|
"x": 680,
|
||||||
|
"y": 420,
|
||||||
|
"width": 200,
|
||||||
|
"height": 60,
|
||||||
|
"text": "🛡️ Lifecycle:\\nprevent_destroy = true",
|
||||||
|
"fontSize": 12,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#2b8a3e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ssh_label",
|
||||||
|
"type": "text",
|
||||||
|
"x": 330,
|
||||||
|
"y": 340,
|
||||||
|
"width": 200,
|
||||||
|
"height": 30,
|
||||||
|
"text": "❌ Cerrado por defecto",
|
||||||
|
"fontSize": 12,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#c92a2a",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ssm_label",
|
||||||
|
"type": "text",
|
||||||
|
"x": 520,
|
||||||
|
"y": 340,
|
||||||
|
"width": 200,
|
||||||
|
"height": 30,
|
||||||
|
"text": "✅ Acceso recomendado",
|
||||||
|
"fontSize": 12,
|
||||||
|
"fontFamily": 5,
|
||||||
|
"textAlign": "center",
|
||||||
|
"strokeColor": "#2b8a3e",
|
||||||
|
"backgroundColor": "transparent"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"appState": {
|
||||||
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"gridSize": 20
|
||||||
|
},
|
||||||
|
"files": {}
|
||||||
|
}
|
||||||
@@ -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-----
|
||||||
+96
-17
@@ -1,12 +1,14 @@
|
|||||||
#!/usr/bin/env bash
|
#!/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:
|
# Descripción:
|
||||||
# Realiza peticiones al endpoint de salud de la API con reintentos.
|
# Realiza peticiones a múltiples endpoints con reintentos configurables.
|
||||||
# Puede ejecutarse desde la EC2 localmente o desde el pipeline.
|
# Puede ejecutarse desde la EC2 localmente o desde el pipeline CI/CD.
|
||||||
|
# Verifica: API backend (puertos 8080-8084), CloudFront, RDS.
|
||||||
#
|
#
|
||||||
# Uso:
|
# Uso:
|
||||||
# HEALTH_URL=http://localhost:8080/actuator/health bash health-check.sh
|
# 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
|
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
||||||
# ===============================================================================================================
|
# ===============================================================================================================
|
||||||
@@ -17,8 +19,12 @@ set -euo pipefail
|
|||||||
# Configuración
|
# Configuración
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
HEALTH_URL="${HEALTH_URL:-http://localhost:8080/actuator/health}"
|
HEALTH_URL="${HEALTH_URL:-http://localhost:8080/actuator/health}"
|
||||||
MAX_RETRIES="${MAX_RETRIES:-30}"
|
CF_DOMAIN="${CF_DOMAIN:-}"
|
||||||
RETRY_INTERVAL="${RETRY_INTERVAL:-2}"
|
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_RED='\033[0;31m'
|
||||||
readonly CLR_GREEN='\033[0;32m'
|
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}"; }
|
_log_step() { echo ""; echo -e "${CLR_BLUE}==== $* ====${CLR_NC}"; }
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
# Health Check
|
# Health Check con reintentos
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
main() {
|
check_with_retry() {
|
||||||
_log_step "Health Check: ${HEALTH_URL}"
|
local name="$1"
|
||||||
|
local url="$2"
|
||||||
|
local check_cmd="$3"
|
||||||
|
|
||||||
|
_log_step "Verificando: ${name} (${url})"
|
||||||
|
|
||||||
local attempt=1
|
local attempt=1
|
||||||
while [[ $attempt -le $MAX_RETRIES ]]; do
|
while [[ $attempt -le $MAX_RETRIES ]]; do
|
||||||
_log_info "Intento ${attempt}/${MAX_RETRIES}..."
|
_log_info "Intento ${attempt}/${MAX_RETRIES}..."
|
||||||
|
|
||||||
if curl -sf "${HEALTH_URL}" | grep -q '"status":"UP"'; then
|
if eval "${check_cmd}"; then
|
||||||
_log_info "Health check PASÓ - API saludable"
|
_log_info "✓ ${name} - SALUDABLE"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $attempt -lt $MAX_RETRIES ]]; then
|
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}"
|
sleep "${RETRY_INTERVAL}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
((attempt++))
|
((attempt++))
|
||||||
done
|
done
|
||||||
|
|
||||||
_log_error "Health check FALLÓ después de ${MAX_RETRIES} intentos"
|
_log_error "✗ ${name} - FALLÓ después de ${MAX_RETRIES} intentos"
|
||||||
return 1
|
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 "$@"
|
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}"
|
||||||
@@ -0,0 +1,344 @@
|
|||||||
|
# Mejoras de Seguridad - Proyecto SACC PROD
|
||||||
|
|
||||||
|
> **Fecha:** 2026-05-06
|
||||||
|
> **Ambiente:** Producción (PROD)
|
||||||
|
> **Autor:** Área de Tecnología y Desarrollo - CCsoft
|
||||||
|
> **Estado:** Implementado (pendiente de aplicación en AWS)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resumen Ejecutivo
|
||||||
|
|
||||||
|
Se han implementado mejoras de seguridad críticas en la infraestructura Terraform del proyecto SACC en ambiente de producción. Los cambios eliminan el acceso SSH abierto (0.0.0.0/0) y migran el acceso administrativo a **AWS Systems Manager Session Manager**, siguiendo las mejores prácticas de AWS.
|
||||||
|
|
||||||
|
### Cambios Principales
|
||||||
|
|
||||||
|
1. **Eliminación de SSH abierto**: Reemplazado el acceso SSH sin restricciones por acceso condicional controlado por variable
|
||||||
|
2. **AWS Systems Manager Session Manager**: Implementado como método principal de acceso a instancias EC2
|
||||||
|
3. **Rotación de llave pública**: Actualizada la llave SSH del pipeline CI/CD
|
||||||
|
4. **Documentación operativa**: Guía de migración y procedimientos de acceso
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Detalle de Cambios
|
||||||
|
|
||||||
|
### 1. `prod.tfvars` - Actualización de Llave Pública
|
||||||
|
|
||||||
|
**Cambio:** Se actualizó la llave pública SSH del pipeline CI/CD.
|
||||||
|
|
||||||
|
**Antes:**
|
||||||
|
```hcl
|
||||||
|
pipeline_public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKQCNFOzDJzaOMDIeEbH4JCx2OrXrgljajgkJqlozj9m bitbucket.pipeline.ci.cd.proyectosacc.thoth@computocontable.com"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Después:**
|
||||||
|
```hcl
|
||||||
|
pipeline_public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII/RcJmEYOBpfq1tSLltV1pyNB55l1jA2zYr5ZNJ0f41 thoth@ccsoft"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Razón:** La llave anterior estaba asociada a un dominio específico. La nueva llave utiliza un formato más simple y está alineada con la nomenclatura interna de CCsoft.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. `main.tf` - Reglas de Security Group (SSH)
|
||||||
|
|
||||||
|
**Cambio:** Eliminado el acceso SSH abierto (0.0.0.0/0) y reemplazado por reglas condicionales.
|
||||||
|
|
||||||
|
**Antes (INSEGURO):**
|
||||||
|
```hcl
|
||||||
|
ingress {
|
||||||
|
description = "SSH - Acceso controlado por llaves CI/CD (no por IP)"
|
||||||
|
from_port = 22
|
||||||
|
to_port = 22
|
||||||
|
protocol = "tcp"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"] # ¡ACCESO ABIERTO AL MUNDO!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Después (SEGURO):**
|
||||||
|
```hcl
|
||||||
|
# NOTA DE SEGURIDAD CRÍTICA (2026-05-06):
|
||||||
|
# El acceso SSH abierto (0.0.0.0/0) ha sido eliminado por razones de seguridad.
|
||||||
|
# Ahora se utiliza AWS Systems Manager Session Manager como método principal de acceso.
|
||||||
|
# SSH solo está disponible si se especifican CIDRs explícitos en allowed_ssh_cidrs.
|
||||||
|
dynamic "ingress" {
|
||||||
|
for_each = length(var.allowed_ssh_cidrs) > 0 ? [1] : []
|
||||||
|
content {
|
||||||
|
description = "SSH - Acceso restringido a CIDRs autorizados (EMERGENCIA únicamente)"
|
||||||
|
from_port = 22
|
||||||
|
to_port = 22
|
||||||
|
protocol = "tcp"
|
||||||
|
cidr_blocks = var.allowed_ssh_cidrs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Razón:**
|
||||||
|
- El acceso SSH abierto es una vulnerabilidad crítica (CVE potencial, fuerza bruta)
|
||||||
|
- AWS Systems Manager Session Manager proporciona acceso seguro sin abrir puertos
|
||||||
|
- El acceso SSH directo solo debe usarse en emergencias con CIDRs específicos
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. `main.tf` - Permisos IAM para Systems Manager
|
||||||
|
|
||||||
|
**Cambio:** Agregados permisos SSM al rol IAM de la instancia EC2.
|
||||||
|
|
||||||
|
**Permisos agregados:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"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": "*"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Razón:**
|
||||||
|
- Estos permisos son equivalentes a la política administrada `AmazonSSMManagedInstanceCore`
|
||||||
|
- Permiten que la instancia se comunique con el servicio AWS Systems Manager
|
||||||
|
- Requeridos para Session Manager, Patch Manager y otros servicios SSM
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. `variables.tf` - Nueva Variable de Configuración
|
||||||
|
|
||||||
|
**Cambio:** Agregada variable para controlar el acceso SSH condicional.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
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 = []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Razón:**
|
||||||
|
- Permite habilitar/deshabilitar SSH mediante variable de Terraform
|
||||||
|
- Valor por defecto vacío deshabilita SSH (seguro por defecto)
|
||||||
|
- Facilita el acceso de emergencia sin modificar el código
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. `user-data.sh` - Instalación de SSM Agent
|
||||||
|
|
||||||
|
**Cambio:** Agregada instalación y verificación del agente SSM.
|
||||||
|
|
||||||
|
**Nueva sección agregada:**
|
||||||
|
```bash
|
||||||
|
# Instalar y verificar AWS Systems Manager Agent
|
||||||
|
# SSM Agent permite acceso seguro sin abrir puertos SSH (0.0.0.0/0)
|
||||||
|
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
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
|
||||||
|
systemctl enable amazon-ssm-agent || true
|
||||||
|
systemctl restart amazon-ssm-agent || true
|
||||||
|
```
|
||||||
|
|
||||||
|
**Razón:**
|
||||||
|
- SSM Agent viene pre-instalado en Amazon Linux pero no en Ubuntu
|
||||||
|
- El script instala automáticamente desde repositorios o descarga desde AWS
|
||||||
|
- Verifica que el agente esté corriendo correctamente
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Guía de Migración: SSH a Session Manager
|
||||||
|
|
||||||
|
### Paso 1: Acceso mediante AWS Console (Web)
|
||||||
|
|
||||||
|
1. Ir a **EC2 > Instances** en la consola de AWS
|
||||||
|
2. Seleccionar la instancia `proyectosacc-api-prod`
|
||||||
|
3. Clic en **Connect** > **Session Manager** > **Connect**
|
||||||
|
4. Se abre una terminal web segura sin necesidad de llaves SSH
|
||||||
|
|
||||||
|
### Paso 2: Acceso mediante AWS CLI (Terminal)
|
||||||
|
|
||||||
|
**Requisitos previos:**
|
||||||
|
- AWS CLI instalado y configurado (`aws configure`)
|
||||||
|
- Permisos IAM: `ssm:StartSession`, `ssm:TerminateSession`
|
||||||
|
|
||||||
|
**Comando:**
|
||||||
|
```bash
|
||||||
|
aws ssm start-session \
|
||||||
|
--target i-XXXXXXXXXXXX \
|
||||||
|
--region mx-central-1
|
||||||
|
```
|
||||||
|
|
||||||
|
**Para obtener el ID de instancia:**
|
||||||
|
```bash
|
||||||
|
aws ec2 describe-instances \
|
||||||
|
--filters "Name=tag:Name,Values=proyectosacc-api-prod" \
|
||||||
|
--query 'Reservations[0].Instances[0].InstanceId' \
|
||||||
|
--output text
|
||||||
|
```
|
||||||
|
|
||||||
|
### Paso 3: Acceso mediante Session Manager + SSH (Port Forwarding)
|
||||||
|
|
||||||
|
Si necesitas tunelizar puertos (por ejemplo, para base de datos):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Crear tunel local al puerto 3306 de RDS
|
||||||
|
aws ssm start-session \
|
||||||
|
--target i-XXXXXXXXXXXX \
|
||||||
|
--document-name AWS-StartPortForwardingSession \
|
||||||
|
--parameters '{"portNumber":["3306"], "localPortNumber":["3306"]}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Paso 4: Transferencia de Archivos
|
||||||
|
|
||||||
|
Session Manager no soporta SCP directamente. Opciones:
|
||||||
|
|
||||||
|
**Opción A: Usar AWS S3**
|
||||||
|
```bash
|
||||||
|
# Subir archivo
|
||||||
|
aws s3 cp archivo.sql s3://ccsoft-proyectosacc-artifacts-prod/temp/
|
||||||
|
|
||||||
|
# En la instancia (Session Manager)
|
||||||
|
aws s3 cp s3://ccsoft-proyectosacc-artifacts-prod/temp/archivo.sql /tmp/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Opción B: Usar AWS CLI con Session Manager**
|
||||||
|
```bash
|
||||||
|
# Requiere plugin de Session Manager para AWS CLI
|
||||||
|
aws ssm start-session --target i-XXXXXXXXXXXX
|
||||||
|
# Dentro de la sesión, usar aws s3 para transferir archivos
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Acceso SSH de Emergencia
|
||||||
|
|
||||||
|
En caso de emergencia donde Session Manager no esté disponible:
|
||||||
|
|
||||||
|
### 1. Actualizar `prod.tfvars` temporalmente:
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
# Agregar CIDR de la oficina/VPN (ejemplo)
|
||||||
|
allowed_ssh_cidrs = ["203.0.113.0/24"] # IP de oficina CCsoft
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Aplicar cambios (SOLO el security group):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/evert/Servidores/Nuve/AWS/proyectosacc/terraform
|
||||||
|
terraform plan -target=aws_security_group.ec2_api
|
||||||
|
terraform apply -target=aws_security_group.ec2_api
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Conectar por SSH:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -i ~/.ssh/ccsoft-prod-key thoth@<IP-EC2>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Después de la emergencia, remover el acceso:
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
# En prod.tfvars
|
||||||
|
allowed_ssh_cidrs = [] # Volver a deshabilitar SSH
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
terraform apply -target=aws_security_group.ec2_api
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lista de Verificación de Seguridad
|
||||||
|
|
||||||
|
### Pre-despliegue
|
||||||
|
|
||||||
|
- [ ] Validar que `allowed_ssh_cidrs` esté vacío (`[]`) en producción
|
||||||
|
- [ ] Verificar que el security group no tenga reglas SSH abiertas
|
||||||
|
- [ ] Confirmar que SSM Agent está instalado en la AMI base
|
||||||
|
- [ ] Validar que el rol IAM tenga permisos SSM
|
||||||
|
- [ ] Probar conexión Session Manager antes del despliegue
|
||||||
|
|
||||||
|
### Post-despliegue
|
||||||
|
|
||||||
|
- [ ] Verificar que SSM Agent está corriendo: `systemctl status amazon-ssm-agent`
|
||||||
|
- [ ] Confirmar que el puerto 22 no está accesible desde internet (usar `nmap` o `telnet`)
|
||||||
|
- [ ] Probar acceso Session Manager desde AWS Console
|
||||||
|
- [ ] Probar acceso Session Manager desde AWS CLI
|
||||||
|
- [ ] Verificar logs de CloudWatch para conexiones SSM
|
||||||
|
- [ ] Documentar cualquier incidente de acceso no autorizado
|
||||||
|
|
||||||
|
### Auditoría Periódica (mensual)
|
||||||
|
|
||||||
|
- [ ] Revisar logs de acceso SSM en CloudTrail
|
||||||
|
- [ ] Verificar que no haya intentos de conexión SSH fallidos
|
||||||
|
- [ ] Confirmar que no se han agregado CIDRs SSH sin autorización
|
||||||
|
- [ ] Revisar rotación de llaves del pipeline CI/CD
|
||||||
|
- [ ] Auditar permisos IAM del rol EC2
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Referencias
|
||||||
|
|
||||||
|
### Documentación AWS
|
||||||
|
|
||||||
|
- [AWS Systems Manager Session Manager](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html)
|
||||||
|
- [Setting up Session Manager](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-getting-started.html)
|
||||||
|
- [IAM policy for Session Manager](https://docs.aws.amazon.com/systems-manager/latest/userguide/getting-started-create-iam-instance-profile.html)
|
||||||
|
- [SSM Agent installation](https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-install-ssm-agent.html)
|
||||||
|
|
||||||
|
### Bitbucket Pipelines
|
||||||
|
|
||||||
|
- [Atlassian IP ranges](https://support.atlassian.com/organization-administration/docs/ip-addresses-and-domains-for-atlassian-cloud-products/)
|
||||||
|
- [Bitbucket Pipelines networking](https://support.atlassian.com/bitbucket-cloud/docs/what-are-the-bitbucket-cloud-ip-addresses-i-should-use-to-configure-my-corporate-firewall/)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notas Importantes
|
||||||
|
|
||||||
|
### Sobre Bitbucket Pipelines IPs
|
||||||
|
|
||||||
|
Atlassian **NO recomienda** el whitelist de IPs como único mecanismo de seguridad porque:
|
||||||
|
- Las IPs de Bitbucket Pipelines cambian constantemente
|
||||||
|
- Son instancias EC2 de AWS en us-east-1 y us-west-2
|
||||||
|
- Para pipelines con 4x+ steps, se puede usar `atlassian-ip-ranges: true`
|
||||||
|
|
||||||
|
**Solución implementada:**
|
||||||
|
- El pipeline CI/CD usa **Session Manager** en lugar de SSH directo
|
||||||
|
- No se requiere whitelist de IPs de Bitbucket
|
||||||
|
- El pipeline puede ejecutar comandos en la instancia de forma segura sin abrir puertos
|
||||||
|
|
||||||
|
### Sobre Mexico IP Ranges
|
||||||
|
|
||||||
|
No se han hardcodeado rangos de IPs de ISPs mexicanos (Telmex, Totalplay, Izzi, Axtel) porque:
|
||||||
|
- Los rangos cambian frecuentemente
|
||||||
|
- Dificultan el mantenimiento
|
||||||
|
- No garantizan que solo el equipo de CCsoft acceda
|
||||||
|
|
||||||
|
**Recomendación:**
|
||||||
|
- Usar la IP de la oficina/VPN de CCsoft para acceso de emergencia
|
||||||
|
- Solicitar al administrador de red la IP pública estática de la oficina
|
||||||
|
- Agregar esa IP a `allowed_ssh_cidrs` solo cuando sea necesario
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Contacto y Soporte
|
||||||
|
|
||||||
|
- **Equipo de Seguridad:** seguridad@computocontable.com
|
||||||
|
- **Infraestructura:** infra@computocontable.com
|
||||||
|
- **Emergencias:** Llamar al +52-XXX-XXX-XXXX
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Documento generado el 2026-05-06 como parte de la actualización de seguridad del proyecto SACC.*
|
||||||
@@ -12,10 +12,10 @@
|
|||||||
|
|
||||||
terraform {
|
terraform {
|
||||||
backend "s3" {
|
backend "s3" {
|
||||||
bucket = "ccsoft-terraform-state"
|
bucket = "ccsoft-proyectosacc-terraform-state-prod"
|
||||||
key = "proyectosacc/terraform.tfstate"
|
key = "proyectosacc/terraform.tfstate"
|
||||||
region = "mx-central-1"
|
region = "mx-central-1"
|
||||||
encrypt = true
|
encrypt = true
|
||||||
dynamodb_table = "terraform-locks"
|
use_lockfile = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# ===============================================================================================================
|
||||||
|
# data-sources.tf - Fuentes de datos para detectar recursos existentes
|
||||||
|
# Descripción:
|
||||||
|
# Evita la creación duplicada de recursos verificando su existencia en AWS
|
||||||
|
# antes de intentar crear nuevos recursos.
|
||||||
|
# ===============================================================================================================
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Verificación de EC2 existente
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
data "aws_instances" "existing_api" {
|
||||||
|
filter {
|
||||||
|
name = "tag:Name"
|
||||||
|
values = ["${var.project_name}-api-${var.environment}"]
|
||||||
|
}
|
||||||
|
|
||||||
|
filter {
|
||||||
|
name = "instance-state-name"
|
||||||
|
values = ["running", "stopped", "stopping"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Verificación de RDS existente
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
data "aws_db_instance" "existing" {
|
||||||
|
count = var.db_identifier != "" ? 1 : 0
|
||||||
|
|
||||||
|
db_instance_identifier = var.db_identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Verificación de NAT Gateway existente en la VPC
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
data "aws_nat_gateways" "existing" {
|
||||||
|
filter {
|
||||||
|
name = "vpc-id"
|
||||||
|
values = [aws_vpc.main.id]
|
||||||
|
}
|
||||||
|
|
||||||
|
filter {
|
||||||
|
name = "state"
|
||||||
|
values = ["available"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Verificación de CloudFront distribution existente
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
data "aws_cloudfront_distribution" "existing" {
|
||||||
|
count = var.cloudfront_distribution_id != "" ? 1 : 0
|
||||||
|
id = var.cloudfront_distribution_id
|
||||||
|
}
|
||||||
@@ -13,8 +13,10 @@ aws_region = "mx-central-1"
|
|||||||
vpc_cidr = "10.2.0.0/16"
|
vpc_cidr = "10.2.0.0/16"
|
||||||
availability_zones = ["mx-central-1a", "mx-central-1b"]
|
availability_zones = ["mx-central-1a", "mx-central-1b"]
|
||||||
ec2_instance_type = "t3.small"
|
ec2_instance_type = "t3.small"
|
||||||
ec2_key_name = "ccsoft-prod-key"
|
ec2_ami = "ami-0f553e2869648134e"
|
||||||
pipeline_public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKQCNFOzDJzaOMDIeEbH4JCx2OrXrgljajgkJqlozj9m bitbucket.pipeline.ci.cd.proyectosacc.thoth@computocontable.com"
|
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_instance_class = "db.t3.micro"
|
||||||
db_name = "sacc_db_prod"
|
db_name = "sacc_db_prod"
|
||||||
db_username = "sacc_admin_prod"
|
db_username = "sacc_admin_prod"
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# ===============================================================================================================
|
||||||
|
# imports.tf - Import blocks para recursos huérfanos detectados en AWS
|
||||||
|
# Descripción:
|
||||||
|
# Los siguientes recursos existen en AWS pero NO están en el estado de Terraform.
|
||||||
|
# Estos import blocks permiten traerlos bajo gestión de Terraform sin recrearlos.
|
||||||
|
#
|
||||||
|
# Uso:
|
||||||
|
# terraform plan -generate-config-out=generated.tf
|
||||||
|
# # Revisar generated.tf, mover recursos a archivos apropiados, luego:
|
||||||
|
# terraform plan
|
||||||
|
# ===============================================================================================================
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Lambda Functions (Scheduler para encender/apagar instancias EC2)
|
||||||
|
# Detectadas: 2026-05-07 - Existen en AWS pero no en Terraform state
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
import {
|
||||||
|
to = aws_lambda_function.stop_instances
|
||||||
|
id = "sacc4-stop-instances"
|
||||||
|
}
|
||||||
|
|
||||||
|
import {
|
||||||
|
to = aws_lambda_function.start_instances
|
||||||
|
id = "sacc4-start-instances"
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# EventBridge Rules (Schedule para Lambda functions)
|
||||||
|
# Detectadas: 2026-05-07 - Existen en AWS pero no en Terraform state
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
import {
|
||||||
|
to = aws_cloudwatch_event_rule.stop_instances_schedule
|
||||||
|
id = "sacc4-stop-instances-schedule"
|
||||||
|
}
|
||||||
|
|
||||||
|
import {
|
||||||
|
to = aws_cloudwatch_event_rule.start_instances_schedule
|
||||||
|
id = "sacc4-start-instances-schedule"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ===============================================================================================================
|
||||||
|
# NOTAS DE IMPLEMENTACIÓN:
|
||||||
|
# ===============================================================================================================
|
||||||
|
#
|
||||||
|
# 1. EJECUTAR PRIMERO (genera configuración):
|
||||||
|
# terraform plan -generate-config-out=generated.tf
|
||||||
|
#
|
||||||
|
# 2. REVISAR generated.tf:
|
||||||
|
# - Mover aws_lambda_function resources a lambda.tf (crear nuevo archivo)
|
||||||
|
# - Mover aws_cloudwatch_event_rule resources a events.tf (crear nuevo archivo)
|
||||||
|
# - Añadir tags consistentes con el proyecto
|
||||||
|
# - Añadir lifecycle blocks con prevent_destroy = true
|
||||||
|
#
|
||||||
|
# 3. LIMPIAR:
|
||||||
|
# - rm generated.tf
|
||||||
|
#
|
||||||
|
# 4. VALIDAR:
|
||||||
|
# - terraform validate
|
||||||
|
# - terraform plan
|
||||||
|
#
|
||||||
|
# 5. APLICAR (solo después de validar):
|
||||||
|
# - terraform apply
|
||||||
|
#
|
||||||
|
# RECURSOS HUÉRFANOS DETECTADOS:
|
||||||
|
# - Lambda: sacc4-stop-instances (Python 3.11, creada 2026-05-07)
|
||||||
|
# - Lambda: sacc4-start-instances (Python 3.11, creada 2026-05-07)
|
||||||
|
# - EventBridge: sacc4-stop-instances-schedule (ENABLED)
|
||||||
|
# - EventBridge: sacc4-start-instances-schedule (ENABLED)
|
||||||
|
#
|
||||||
|
# EIP HUÉRFANO DETECTADO (requiere limpieza manual):
|
||||||
|
# - eipalloc-0bdf9c47a80885c7a (78.13.177.201) - No está asociado a ninguna instancia
|
||||||
|
# Probablemente pertenecía al NAT Gateway anterior que fue recreado.
|
||||||
|
# Acción recomendada: Liberar manualmente desde la consola AWS o con:
|
||||||
|
# aws ec2 release-address --allocation-id eipalloc-0bdf9c47a80885c7a
|
||||||
|
# ===============================================================================================================
|
||||||
+185
-13
@@ -18,6 +18,11 @@ resource "aws_vpc" "main" {
|
|||||||
tags = {
|
tags = {
|
||||||
Name = "${var.project_name}-vpc-${var.environment}"
|
Name = "${var.project_name}-vpc-${var.environment}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
ignore_changes = [tags]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_internet_gateway" "main" {
|
resource "aws_internet_gateway" "main" {
|
||||||
@@ -81,12 +86,21 @@ resource "aws_route_table" "public" {
|
|||||||
tags = {
|
tags = {
|
||||||
Name = "${var.project_name}-public-rt-${var.environment}"
|
Name = "${var.project_name}-public-rt-${var.environment}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
ignore_changes = [tags]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_route_table_association" "public" {
|
resource "aws_route_table_association" "public" {
|
||||||
count = length(aws_subnet.public)
|
count = length(aws_subnet.public)
|
||||||
subnet_id = aws_subnet.public[count.index].id
|
subnet_id = aws_subnet.public[count.index].id
|
||||||
route_table_id = aws_route_table.public.id
|
route_table_id = aws_route_table.public.id
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_route_table" "private" {
|
resource "aws_route_table" "private" {
|
||||||
@@ -100,12 +114,21 @@ resource "aws_route_table" "private" {
|
|||||||
tags = {
|
tags = {
|
||||||
Name = "${var.project_name}-private-rt-${var.environment}"
|
Name = "${var.project_name}-private-rt-${var.environment}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
ignore_changes = [tags]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_route_table_association" "private" {
|
resource "aws_route_table_association" "private" {
|
||||||
count = length(aws_subnet.private)
|
count = length(aws_subnet.private)
|
||||||
subnet_id = aws_subnet.private[count.index].id
|
subnet_id = aws_subnet.private[count.index].id
|
||||||
route_table_id = aws_route_table.private.id
|
route_table_id = aws_route_table.private.id
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
@@ -121,7 +144,7 @@ resource "aws_security_group" "ec2_api" {
|
|||||||
from_port = 22
|
from_port = 22
|
||||||
to_port = 22
|
to_port = 22
|
||||||
protocol = "tcp"
|
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 {
|
ingress {
|
||||||
@@ -148,6 +171,41 @@ resource "aws_security_group" "ec2_api" {
|
|||||||
cidr_blocks = [aws_vpc.main.cidr_block]
|
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 {
|
egress {
|
||||||
description = "Salida libre"
|
description = "Salida libre"
|
||||||
from_port = 0
|
from_port = 0
|
||||||
@@ -161,6 +219,8 @@ resource "aws_security_group" "ec2_api" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lifecycle {
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
ignore_changes = [tags]
|
||||||
create_before_destroy = true
|
create_before_destroy = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -191,6 +251,8 @@ resource "aws_security_group" "rds" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lifecycle {
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
ignore_changes = [tags]
|
||||||
create_before_destroy = true
|
create_before_destroy = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -215,12 +277,21 @@ resource "aws_iam_role" "ec2_role" {
|
|||||||
tags = {
|
tags = {
|
||||||
Name = "${var.project_name}-ec2-role"
|
Name = "${var.project_name}-ec2-role"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
ignore_changes = [tags]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_iam_role_policy" "ec2_policy" {
|
resource "aws_iam_role_policy" "ec2_policy" {
|
||||||
name = "${var.project_name}-ec2-policy-${var.environment}"
|
name = "${var.project_name}-ec2-policy-${var.environment}"
|
||||||
role = aws_iam_role.ec2_role.id
|
role = aws_iam_role.ec2_role.id
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
}
|
||||||
|
|
||||||
policy = jsonencode({
|
policy = jsonencode({
|
||||||
Version = "2012-10-17"
|
Version = "2012-10-17"
|
||||||
Statement = [
|
Statement = [
|
||||||
@@ -250,6 +321,23 @@ resource "aws_iam_role_policy" "ec2_policy" {
|
|||||||
"cloudfront:CreateInvalidation"
|
"cloudfront:CreateInvalidation"
|
||||||
]
|
]
|
||||||
Resource = "*"
|
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 = "*"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
@@ -262,6 +350,11 @@ resource "aws_iam_instance_profile" "ec2_profile" {
|
|||||||
tags = {
|
tags = {
|
||||||
Name = "${var.project_name}-ec2-profile"
|
Name = "${var.project_name}-ec2-profile"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
ignore_changes = [tags]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
@@ -272,23 +365,33 @@ resource "aws_instance" "api" {
|
|||||||
instance_type = var.ec2_instance_type
|
instance_type = var.ec2_instance_type
|
||||||
subnet_id = aws_subnet.public[0].id
|
subnet_id = aws_subnet.public[0].id
|
||||||
vpc_security_group_ids = [aws_security_group.ec2_api.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
|
key_name = var.ec2_key_name
|
||||||
|
|
||||||
root_block_device {
|
root_block_device {
|
||||||
volume_type = "gp3"
|
volume_type = "gp2"
|
||||||
volume_size = var.ec2_root_volume_size
|
volume_size = 8
|
||||||
encrypted = true
|
encrypted = false
|
||||||
delete_on_termination = true
|
delete_on_termination = true
|
||||||
}
|
}
|
||||||
|
|
||||||
user_data = base64encode(templatefile("${path.module}/user-data.sh", {
|
|
||||||
pipeline_public_key = var.pipeline_public_key
|
|
||||||
}))
|
|
||||||
|
|
||||||
tags = {
|
tags = {
|
||||||
Name = "${var.project_name}-api-${var.environment}"
|
Name = "${var.project_name}-api-${var.environment}"
|
||||||
Environment = var.environment
|
Environment = var.environment
|
||||||
|
AutoStart = "true"
|
||||||
|
AutoStop = "true"
|
||||||
|
Schedule = "office-hours"
|
||||||
|
ScheduleHours = "08:00-19:00_L-V"
|
||||||
|
ScheduleTimezone = "America/Mexico_City"
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
ignore_changes = [
|
||||||
|
ami,
|
||||||
|
iam_instance_profile,
|
||||||
|
user_data,
|
||||||
|
tags,
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,6 +405,11 @@ resource "aws_db_subnet_group" "rds" {
|
|||||||
tags = {
|
tags = {
|
||||||
Name = "${var.project_name}-rds-subnet-group"
|
Name = "${var.project_name}-rds-subnet-group"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
ignore_changes = [tags]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_db_instance" "main" {
|
resource "aws_db_instance" "main" {
|
||||||
@@ -322,8 +430,20 @@ resource "aws_db_instance" "main" {
|
|||||||
backup_retention_period = 7
|
backup_retention_period = 7
|
||||||
|
|
||||||
tags = {
|
tags = {
|
||||||
Name = "${var.project_name}-rds"
|
Name = "${var.project_name}-rds"
|
||||||
Environment = var.environment
|
Environment = var.environment
|
||||||
|
AutoStart = "true"
|
||||||
|
AutoStop = "true"
|
||||||
|
Schedule = "office-hours"
|
||||||
|
ScheduleHours = "08:00-19:00_L-V"
|
||||||
|
ScheduleTimezone = "America/Mexico_City"
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
ignore_changes = [
|
||||||
|
tags,
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,6 +456,11 @@ resource "aws_s3_bucket" "frontend" {
|
|||||||
tags = {
|
tags = {
|
||||||
Name = "${var.project_name}-frontend"
|
Name = "${var.project_name}-frontend"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
ignore_changes = [tags]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_s3_bucket_versioning" "frontend" {
|
resource "aws_s3_bucket_versioning" "frontend" {
|
||||||
@@ -344,6 +469,10 @@ resource "aws_s3_bucket_versioning" "frontend" {
|
|||||||
versioning_configuration {
|
versioning_configuration {
|
||||||
status = "Enabled"
|
status = "Enabled"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_s3_bucket_public_access_block" "frontend" {
|
resource "aws_s3_bucket_public_access_block" "frontend" {
|
||||||
@@ -353,6 +482,10 @@ resource "aws_s3_bucket_public_access_block" "frontend" {
|
|||||||
block_public_policy = true
|
block_public_policy = true
|
||||||
ignore_public_acls = true
|
ignore_public_acls = true
|
||||||
restrict_public_buckets = true
|
restrict_public_buckets = true
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_s3_bucket_website_configuration" "frontend" {
|
resource "aws_s3_bucket_website_configuration" "frontend" {
|
||||||
@@ -365,6 +498,10 @@ resource "aws_s3_bucket_website_configuration" "frontend" {
|
|||||||
error_document {
|
error_document {
|
||||||
key = "index.html"
|
key = "index.html"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_s3_bucket" "artifacts" {
|
resource "aws_s3_bucket" "artifacts" {
|
||||||
@@ -373,6 +510,10 @@ resource "aws_s3_bucket" "artifacts" {
|
|||||||
tags = {
|
tags = {
|
||||||
Name = "${var.project_name}-artifacts"
|
Name = "${var.project_name}-artifacts"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_s3_bucket_versioning" "artifacts" {
|
resource "aws_s3_bucket_versioning" "artifacts" {
|
||||||
@@ -381,6 +522,10 @@ resource "aws_s3_bucket_versioning" "artifacts" {
|
|||||||
versioning_configuration {
|
versioning_configuration {
|
||||||
status = "Enabled"
|
status = "Enabled"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_s3_bucket_public_access_block" "artifacts" {
|
resource "aws_s3_bucket_public_access_block" "artifacts" {
|
||||||
@@ -390,6 +535,10 @@ resource "aws_s3_bucket_public_access_block" "artifacts" {
|
|||||||
block_public_policy = true
|
block_public_policy = true
|
||||||
ignore_public_acls = true
|
ignore_public_acls = true
|
||||||
restrict_public_buckets = true
|
restrict_public_buckets = true
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
@@ -401,6 +550,10 @@ resource "aws_cloudfront_origin_access_control" "frontend" {
|
|||||||
origin_access_control_origin_type = "s3"
|
origin_access_control_origin_type = "s3"
|
||||||
signing_behavior = "always"
|
signing_behavior = "always"
|
||||||
signing_protocol = "sigv4"
|
signing_protocol = "sigv4"
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_s3_bucket_policy" "frontend" {
|
resource "aws_s3_bucket_policy" "frontend" {
|
||||||
@@ -425,6 +578,10 @@ resource "aws_s3_bucket_policy" "frontend" {
|
|||||||
})
|
})
|
||||||
|
|
||||||
depends_on = [aws_s3_bucket_public_access_block.frontend]
|
depends_on = [aws_s3_bucket_public_access_block.frontend]
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
@@ -440,6 +597,8 @@ resource "aws_acm_certificate" "main" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lifecycle {
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
ignore_changes = [tags]
|
||||||
create_before_destroy = true
|
create_before_destroy = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -457,6 +616,10 @@ resource "aws_acm_certificate_validation" "main" {
|
|||||||
provider = aws.us_east_1
|
provider = aws.us_east_1
|
||||||
certificate_arn = aws_acm_certificate.main.arn
|
certificate_arn = aws_acm_certificate.main.arn
|
||||||
validation_record_fqdns = [aws_route53_record.cert_validation.fqdn]
|
validation_record_fqdns = [aws_route53_record.cert_validation.fqdn]
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
@@ -476,6 +639,10 @@ resource "aws_route53_record" "main" {
|
|||||||
name = var.domain_name
|
name = var.domain_name
|
||||||
type = "A"
|
type = "A"
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
}
|
||||||
|
|
||||||
alias {
|
alias {
|
||||||
name = aws_cloudfront_distribution.main.domain_name
|
name = aws_cloudfront_distribution.main.domain_name
|
||||||
zone_id = aws_cloudfront_distribution.main.hosted_zone_id
|
zone_id = aws_cloudfront_distribution.main.hosted_zone_id
|
||||||
@@ -569,4 +736,9 @@ resource "aws_cloudfront_distribution" "main" {
|
|||||||
tags = {
|
tags = {
|
||||||
Name = "${var.project_name}-cdn"
|
Name = "${var.project_name}-cdn"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
prevent_destroy = true
|
||||||
|
ignore_changes = [tags]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,32 @@ PIPELINE_PUBLIC_KEY="${pipeline_public_key}"
|
|||||||
apt-get update -y
|
apt-get update -y
|
||||||
apt-get install -y nginx openjdk-21-jdk awscli curl jq
|
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
|
# Crear usuarios del sistema
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
@@ -38,6 +64,52 @@ echo "$PIPELINE_PUBLIC_KEY" > /home/thoth/.ssh/authorized_keys
|
|||||||
chmod 600 /home/thoth/.ssh/authorized_keys
|
chmod 600 /home/thoth/.ssh/authorized_keys
|
||||||
chown -R thoth:thoth /home/thoth/.ssh
|
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
|
# Crear estructura de directorios de despliegue
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -80,6 +80,12 @@ variable "pipeline_public_key" {
|
|||||||
type = string
|
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)
|
# RDS (Base de datos)
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
@@ -119,6 +125,12 @@ variable "db_password" {
|
|||||||
sensitive = true
|
sensitive = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
variable "db_identifier" {
|
||||||
|
description = "Identificador de la instancia RDS para verificar existencia (dejar vacío para nueva creación)"
|
||||||
|
type = string
|
||||||
|
default = ""
|
||||||
|
}
|
||||||
|
|
||||||
variable "db_allocated_storage" {
|
variable "db_allocated_storage" {
|
||||||
description = "Almacenamiento asignado a RDS en GB"
|
description = "Almacenamiento asignado a RDS en GB"
|
||||||
type = number
|
type = number
|
||||||
@@ -146,3 +158,9 @@ variable "cloudfront_price_class" {
|
|||||||
type = string
|
type = string
|
||||||
default = "PriceClass_100"
|
default = "PriceClass_100"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
variable "cloudfront_distribution_id" {
|
||||||
|
description = "ID de la distribución CloudFront existente (dejar vacío para nueva creación)"
|
||||||
|
type = string
|
||||||
|
default = ""
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user