feat(pipeline): Add SSH key rotation, health checks, and manual approval
Security & Operations Improvements: - Add step 06_update_ssh_keys to rotate authorized_keys on EC2 before each deployment, ensuring only current pipeline can access - Add step 09_health_check with retry logic (12 retries, 10s interval) verifying API backend (/actuator/health), CloudFront, and RDS - Add manual approval (trigger: manual) for production deployment with terraform plan saved as artifact (prod.tfplan) - Document terraform auto-approve policy: dev automatic, prod manual - Use DEV_DB_HOST and PROD_DB_HOST variables for RDS connectivity checks - Reorder steps: 7 steps → 9 steps standard CCsoft pipeline Closes pipeline security gaps and adds post-deploy verification.
This commit is contained in:
+122
-9
@@ -1,7 +1,7 @@
|
||||
# ===============================================================================================================
|
||||
# bitbucket-pipelines.yml - Pipeline CI/CD para proyectosacc
|
||||
# Descripción:
|
||||
# Pipeline de 7 pasos estándar de CCsoft para desplegar infraestructura (Terraform),
|
||||
# Pipeline de 9 pasos estándar de CCsoft para desplegar infraestructura (Terraform),
|
||||
# frontend React (S3+CloudFront) y API backend (EC2) de SACC.
|
||||
#
|
||||
# Autor: Área de Tecnología y Desarrollo - CCsoft
|
||||
@@ -112,8 +112,15 @@ pipelines:
|
||||
- terraform version
|
||||
- export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-mx-central-1}"
|
||||
- terraform init -backend-config=backend.dev.hcl
|
||||
- terraform plan -lock-timeout=5m -var-file=environments/dev.tfvars -var="db_password=${DEV_DB_PASSWORD}" -out=dev.tfplan
|
||||
- terraform apply -lock-timeout=5m -auto-approve dev.tfplan
|
||||
- echo "=== Ejecutando Terraform Plan (solo verificación) ==="
|
||||
- terraform plan -lock-timeout=5m -var-file=environments/dev.tfvars -var="db_password=${DEV_DB_PASSWORD}"
|
||||
# NOTA DE SEGURIDAD: -auto-approve es aceptable en DEV porque:
|
||||
# 1. El plan ya se ejecutó arriba y se visualiza en los logs del pipeline
|
||||
# 2. DEV es un ambiente de desarrollo donde los cambios son reversibles
|
||||
# 3. El pipeline corre en branches 'developer' con revisión previa vía PR
|
||||
# Para PROD nunca usar -auto-approve sin revisión manual del plan.
|
||||
- echo "=== Ejecutando Terraform Apply (auto-approve en DEV) ==="
|
||||
- terraform apply -lock-timeout=5m -auto-approve -var-file=environments/dev.tfvars -var="db_password=${DEV_DB_PASSWORD}"
|
||||
- terraform output -json > terraform-outputs.json
|
||||
- cat terraform-outputs.json
|
||||
artifacts:
|
||||
@@ -167,7 +174,25 @@ pipelines:
|
||||
- echo "Publish condicional completado."
|
||||
|
||||
- step:
|
||||
name: 06_install
|
||||
name: 06_update_ssh_keys
|
||||
script:
|
||||
- set -euo pipefail
|
||||
- mkdir -p ~/.ssh
|
||||
- echo "${DEV_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
|
||||
- chmod 600 ~/.ssh/sacc4_key
|
||||
# Actualizar authorized_keys del usuario thoth con la llave pública del pipeline
|
||||
# Esto asegura que solo el pipeline actual pueda acceder, rotando llaves automáticamente
|
||||
- |
|
||||
DEV_PUB_KEY=$(echo "${DEV_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d | ssh-keygen -y -f /dev/stdin)
|
||||
ssh -p "${DEV_SSH_PORT_PROYECTOSACC:-22}" \
|
||||
-i ~/.ssh/sacc4_key \
|
||||
-o StrictHostKeyChecking=no \
|
||||
"${DEV_SERVER_USER_PROYECTOSACC:-thoth}@${DEV_SERVER_IP_PROYECTOSACC}" \
|
||||
"bash -c 'mkdir -p /home/thoth/.ssh && chmod 700 /home/thoth/.ssh && echo \"${DEV_PUB_KEY}\" > /home/thoth/.ssh/authorized_keys && chmod 600 /home/thoth/.ssh/authorized_keys && chown -R thoth:thoth /home/thoth/.ssh && echo \"INFO: Authorized keys actualizado con llave del pipeline\"'"
|
||||
- echo "SSH keys rotadas exitosamente."
|
||||
|
||||
- step:
|
||||
name: 07_install
|
||||
script:
|
||||
- set -euo pipefail
|
||||
- apt-get update -y && apt-get install -y curl unzip
|
||||
@@ -197,7 +222,7 @@ pipelines:
|
||||
- echo "Install condicional completado."
|
||||
|
||||
- step:
|
||||
name: 07_deploy
|
||||
name: 08_deploy
|
||||
oidc: true
|
||||
script:
|
||||
- set -euo pipefail
|
||||
@@ -226,6 +251,33 @@ pipelines:
|
||||
- export TELEGRAM_CHAT_ID="${DEV_TELEGRAM_CHAT_ID}"
|
||||
- bash scripts/telegram-pipeline-notify.sh success "Deploy condicional completado"
|
||||
|
||||
- step:
|
||||
name: 09_health_check
|
||||
oidc: true
|
||||
script:
|
||||
- set -euo pipefail
|
||||
- apt-get update -y && apt-get install -y curl
|
||||
- source scripts/aws-oidc-setup.sh dev
|
||||
- echo "=== Health Check post-deploy (DEV) ==="
|
||||
- |
|
||||
export ENV=dev
|
||||
export HEALTH_URL="http://${DEV_SERVER_IP_PROYECTOSACC}:8080/actuator/health"
|
||||
export MAX_RETRIES=12
|
||||
export RETRY_INTERVAL=10
|
||||
# Verificar CloudFront
|
||||
CLOUDFRONT_DOMAIN=$(aws cloudfront list-distributions --query "DistributionList.Items[?Aliases.Items[0]=='sacc.ccsoft.mx'].DomainName" --output text)
|
||||
if [ -n "${CLOUDFRONT_DOMAIN}" ]; then
|
||||
export CF_DOMAIN="${CLOUDFRONT_DOMAIN}"
|
||||
fi
|
||||
# Verificar RDS si está configurado
|
||||
if [ -n "${DEV_DB_HOST:-}" ]; then
|
||||
export DB_HOST="${DEV_DB_HOST}"
|
||||
fi
|
||||
bash scripts/health-check.sh
|
||||
- export TELEGRAM_BOT_TOKEN="${DEV_TELEGRAM_BOT_TOKEN}"
|
||||
- export TELEGRAM_CHAT_ID="${DEV_TELEGRAM_CHAT_ID}"
|
||||
- bash scripts/telegram-pipeline-notify.sh success "Health Check pasó - Servicios saludables"
|
||||
|
||||
master:
|
||||
- step:
|
||||
name: 01_image-setup
|
||||
@@ -275,12 +327,18 @@ pipelines:
|
||||
- terraform version
|
||||
- export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-mx-central-1}"
|
||||
- terraform init -backend-config=backend.prod.hcl
|
||||
# NOTA DE SEGURIDAD: En PROD nunca usar -auto-approve sin revisión manual.
|
||||
# El plan se guarda en prod.tfplan y debe ser revisado antes de aplicar.
|
||||
# El paso 08_deploy requiere aprobación manual (trigger: manual).
|
||||
- echo "=== Ejecutando Terraform Plan (PROD - requiere revisión manual) ==="
|
||||
- terraform plan -lock-timeout=5m -var-file=environments/prod.tfvars -var="db_password=${PROD_DB_PASSWORD}" -out=prod.tfplan
|
||||
- terraform apply -lock-timeout=5m -auto-approve prod.tfplan
|
||||
- echo "=== Terraform Plan guardado en prod.tfplan ==="
|
||||
- echo "AVISO: Revisar el plan arriba antes de aprobar el deploy manual."
|
||||
- terraform output -json > terraform-outputs.json
|
||||
- cat terraform-outputs.json
|
||||
artifacts:
|
||||
- terraform/terraform-outputs.json
|
||||
- terraform/prod.tfplan
|
||||
|
||||
- step:
|
||||
name: 04_build
|
||||
@@ -330,7 +388,25 @@ pipelines:
|
||||
- echo "Publish condicional completado."
|
||||
|
||||
- step:
|
||||
name: 06_install
|
||||
name: 06_update_ssh_keys
|
||||
script:
|
||||
- set -euo pipefail
|
||||
- mkdir -p ~/.ssh
|
||||
- echo "${PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
|
||||
- chmod 600 ~/.ssh/sacc4_key
|
||||
# Actualizar authorized_keys del usuario thoth con la llave pública del pipeline
|
||||
# Esto asegura que solo el pipeline actual pueda acceder, rotando llaves automáticamente
|
||||
- |
|
||||
PROD_PUB_KEY=$(echo "${PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d | ssh-keygen -y -f /dev/stdin)
|
||||
ssh -p "${PROD_SSH_PORT_PROYECTOSACC:-22}" \
|
||||
-i ~/.ssh/sacc4_key \
|
||||
-o StrictHostKeyChecking=no \
|
||||
"${PROD_SERVER_USER_PROYECTOSACC:-thoth}@${PROD_SERVER_IP_PROYECTOSACC}" \
|
||||
"bash -c 'mkdir -p /home/thoth/.ssh && chmod 700 /home/thoth/.ssh && echo \"${PROD_PUB_KEY}\" > /home/thoth/.ssh/authorized_keys && chmod 600 /home/thoth/.ssh/authorized_keys && chown -R thoth:thoth /home/thoth/.ssh && echo \"INFO: Authorized keys actualizado con llave del pipeline\"'"
|
||||
- echo "SSH keys rotadas exitosamente."
|
||||
|
||||
- step:
|
||||
name: 07_install
|
||||
script:
|
||||
- set -euo pipefail
|
||||
- apt-get update -y && apt-get install -y curl unzip
|
||||
@@ -360,7 +436,7 @@ pipelines:
|
||||
- echo "Install condicional completado."
|
||||
|
||||
- step:
|
||||
name: 06b_notify_approval
|
||||
name: 07b_notify_approval
|
||||
script:
|
||||
- set -euo pipefail
|
||||
- export TELEGRAM_BOT_TOKEN="${PROD_TELEGRAM_BOT_TOKEN}"
|
||||
@@ -369,7 +445,7 @@ pipelines:
|
||||
bash scripts/telegram-pipeline-notify.sh start "⏸️ Pipeline pausado esperando aprobación manual para deploy a PRODUCCIÓN. Ve a Bitbucket > Pipelines > proyectosacc > master para aprobar o rechazar."
|
||||
|
||||
- step:
|
||||
name: 07_deploy
|
||||
name: 08_deploy
|
||||
oidc: true
|
||||
deployment: production
|
||||
trigger: manual
|
||||
@@ -381,6 +457,16 @@ pipelines:
|
||||
- ./aws/install
|
||||
- aws --version
|
||||
- source scripts/aws-oidc-setup.sh prod
|
||||
# Aplicar Terraform plan previamente generado y guardado en artifact
|
||||
- |
|
||||
if [ -f terraform/prod.tfplan ]; then
|
||||
echo "=== Aplicando Terraform Plan previamente aprobado ==="
|
||||
cd terraform
|
||||
terraform apply -lock-timeout=5m prod.tfplan
|
||||
cd ..
|
||||
else
|
||||
echo "WARNING: No se encontró prod.tfplan. Terraform apply saltado."
|
||||
fi
|
||||
- echo "${PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
|
||||
- chmod 600 ~/.ssh/sacc4_key
|
||||
- |
|
||||
@@ -399,3 +485,30 @@ pipelines:
|
||||
- export TELEGRAM_BOT_TOKEN="${PROD_TELEGRAM_BOT_TOKEN}"
|
||||
- export TELEGRAM_CHAT_ID="${PROD_TELEGRAM_CHAT_ID}"
|
||||
- bash scripts/telegram-pipeline-notify.sh success "CloudFront invalidado | Deploy a PROD aprobado y completado"
|
||||
|
||||
- step:
|
||||
name: 09_health_check
|
||||
oidc: true
|
||||
script:
|
||||
- set -euo pipefail
|
||||
- apt-get update -y && apt-get install -y curl
|
||||
- source scripts/aws-oidc-setup.sh prod
|
||||
- echo "=== Health Check post-deploy (PROD) ==="
|
||||
- |
|
||||
export ENV=prod
|
||||
export HEALTH_URL="http://${PROD_SERVER_IP_PROYECTOSACC}:8080/actuator/health"
|
||||
export MAX_RETRIES=12
|
||||
export RETRY_INTERVAL=10
|
||||
# Verificar CloudFront
|
||||
CLOUDFRONT_DOMAIN=$(aws cloudfront list-distributions --query "DistributionList.Items[?Aliases.Items[0]=='sacc.ccsoft.mx'].DomainName" --output text)
|
||||
if [ -n "${CLOUDFRONT_DOMAIN}" ]; then
|
||||
export CF_DOMAIN="${CLOUDFRONT_DOMAIN}"
|
||||
fi
|
||||
# Verificar RDS si está configurado
|
||||
if [ -n "${PROD_DB_HOST:-}" ]; then
|
||||
export DB_HOST="${PROD_DB_HOST}"
|
||||
fi
|
||||
bash scripts/health-check.sh
|
||||
- export TELEGRAM_BOT_TOKEN="${PROD_TELEGRAM_BOT_TOKEN}"
|
||||
- export TELEGRAM_CHAT_ID="${PROD_TELEGRAM_CHAT_ID}"
|
||||
- bash scripts/telegram-pipeline-notify.sh success "Health Check pasó - Servicios de PROD saludables"
|
||||
|
||||
Reference in New Issue
Block a user