-
Notifications
You must be signed in to change notification settings - Fork 1
DevSecOps Practices
DevSecOps integra la seguridad como un ciudadano de primera clase en todo el ciclo de vida del desarrollo, no como un paso final antes del release. En Linker1, la seguridad está embebida en el código, en el pipeline, en la infraestructura y en los procesos del equipo.
Todos los valores sensibles están almacenados como GitHub Secrets (encriptados, nunca visibles en logs):
| Secret | Nivel | Propósito |
|---|---|---|
LD_SDK_KEY |
Repositorio | LaunchDarkly SDK key |
DEPLOYMENT_PRIVATE_KEY |
Repositorio | SSH private key para deploy vía Bastion |
DEPLOYMENT_PUBLIC_KEY |
Repositorio | SSH public key para sesión Bastion |
OCI_CLI_USER |
Repositorio | Autenticación OCI API |
OCI_CLI_TENANCY |
Organización | Tenant de OCI |
OCI_CLI_FINGERPRINT |
Repositorio | Fingerprint de API key OCI |
OCI_CLI_KEY_CONTENT |
Repositorio | Private key de OCI |
OTEL_EXPORTER_OTLP_HEADERS |
Repositorio | Token de autenticación Grafana Cloud |
MYSQL_PWD |
Repositorio | Contraseña MySQL |
| Variable | Nivel | Propósito |
|---|---|---|
OCI_CLI_REGION |
Organización | Región OCI (sa-bogota-1) |
OCI_BASTION_OCID |
Organización | ID del servicio Bastion |
OCI_INSTANCE_OCID |
Repositorio | ID de la VM de producción |
OTEL_EXPORTER_OTLP_ENDPOINT |
Repositorio | URL del gateway OTLP |
MYSQL_HOST / MYSQL_DATABASE / MYSQL_USER
|
Repositorio | Config MySQL |
LOG_LEVEL |
Repositorio | Nivel de logging |
Auditoría honesta (2026-07): esta sección afirmaba que los secrets críticos también se almacenan en OCI Vault. Verificado directamente contra infra/*.tf y todos los workflows (grep por oci_vault, kms_key, "vault"): no existe ninguna referencia real a OCI Vault en el repositorio. Todos los secrets de infraestructura viven exclusivamente en GitHub Secrets (tabla arriba), sin una segunda capa de gestión de secrets vía OCI.
Esto es "0 operaciones manuales en la consola de OCI" cumplido por omisión (no hay ningún secret que dependa de un paso manual en la consola de Vault), pero también significa que no hay rotación automática ni auditoría de acceso más allá de lo que GitHub Secrets ya ofrece. Si se quiere una segunda capa (rotación sin tocar la config del pipeline, auditoría de acceso a nivel OCI), sería trabajo nuevo: crear el vault vía Terraform (oci_kms_vault + oci_kms_key), migrar los secrets más sensibles (OCI_CLI_KEY_CONTENT, MYSQL_PWD, DEPLOYMENT_PRIVATE_KEY), y ajustar bluegreen.yml/pipeline.yml para leerlos desde Vault en vez de secrets.*. No se ha priorizado porque GitHub Secrets ya cubre el requisito funcional (encriptado en reposo, nunca visible en logs) sin trabajo adicional.
Los secrets en el pipeline de deploy se transportan en base64 para evitar problemas de inyección de shell:
- name: Base64-encode secrets for safe transport
run: |
b64() { printf '%s' "$1" | base64 -w0; }
echo "ld_sdk_key=$(b64 "${{ secrets.LD_SDK_KEY }}")" >> "$GITHUB_OUTPUT"Y se decodifican + escapan para systemd solo en el script remoto:
LD_SDK_KEY=$(printf '%s' "${{ steps.b64.outputs.ld_sdk_key }}" | base64 -d)
LD_SDK_KEY=$(systemd_escape_value "$LD_SDK_KEY")| Regla | Estado |
|---|---|
| Requiere Pull Request | ✅ |
| Aprobaciones mínimas | 1 |
| Dismiss stale reviews | ✅ |
| Status checks requeridos | Build, Tests, Package, Summary, Smoke Test |
| Branch up to date | ✅ (strict: true) |
| Conversaciones resueltas | ✅ |
| Force push | ❌ Bloqueado |
| Eliminar branch | ❌ Bloqueado |
| Admin bypass | ❌ (enforce_admins: true) |
| Regla | Estado |
|---|---|
| Requiere Pull Request | ✅ |
| Aprobaciones mínimas | 1 |
| Dismiss stale reviews | ✅ |
| Status checks requeridos | Build, Tests, Package, Summary, Smoke Test |
| Branch up to date | ✅ (strict: true) |
| Force push | ❌ Bloqueado |
| Admin bypass | ❌ (enforce_admins: true) |
Lección aprendida: inicialmente incluimos checks de pipeline.yml como required en DEV. Ese workflow solo corre en push a main, no en PRs. El resultado: todos los PRs quedaron bloqueados. Se corrigió inmediatamente.
feature/* ──── PR ────▶ DEV ──── PR ────▶ main
│ │
▼ ▼
1 approval 1 approval
CI verde CI verde
Review comments Review comments
resueltos resueltos
El repositorio incluye un template de PR que guía al autor a documentar:
- Descripción de los cambios
- Tipo de cambio (bug fix, feature, breaking change)
- Checklist de calidad
* @AnderssonProgramming @Anderfg13 @daniel-pm19 @esteban0903 @Juan-Jose-D
Todo el equipo es CODEOWNER de todo el repositorio. Esto garantiza que:
- Cada PR tiene reviewers asignados automáticamente
- Nadie puede aprobar su propio código (GitHub lo previene)
- El conocimiento del codebase se distribuye entre todos los miembros
| Check | Workflow | Qué valida |
|---|---|---|
| Build | ci.yml |
El código compila |
| Tests | ci.yml |
Tests unitarios pasan + cobertura 100% |
| Package | ci.yml |
El fat JAR se genera correctamente |
| Summary | ci.yml |
El artefacto final es válido |
| Smoke Test | ci.yml |
La app inicia y los endpoints responden |
JaCoCo está configurado con un umbral de 100% de cobertura de líneas (excluyendo Main.class):
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>1.00</minimum>
</limit>
</limits>
</rule>Si la cobertura baja de 100%, el build falla y el PR no se puede mergear.
El job rollback en pipeline.yml se ejecuta automáticamente si el deploy o la validación fallan:
- Identifica el tag SemVer anterior al actual
- Descarga el JAR de ese release desde GitHub Releases
- Lo despliega vía Bastion
- Verifica que el servicio responde 200
ssh linker
cd linker1
bash scripts/rollback.sh v1.0.0El script:
- Verifica el tag existe
- Detiene el servicio
- Cambia al código del tag anterior
- Reconstruye con
deploy.sh - Verifica el servicio está activo y responde
Para features controladas por LaunchDarkly, el rollback es instantáneo: un toggle en el dashboard deshabilita la feature sin necesidad de re-deploy, rebuild, ni ninguna intervención en la infraestructura.
Un OCI Bastion es un servicio gestionado que permite acceso SSH a recursos en subnets privadas sin necesidad de IP pública, jump hosts, ni VPNs.
La VM de producción tiene assign_public_ip = false. No existe ninguna IP pública asociada a ella. El Bastion es el único mecanismo de acceso.
GitHub Actions OCI Bastion VM
│ │ │
├── Crea sesión SSH ────────────▶│ │
│ │ │
├── Envía JAR + script ────────▶│──── SSH tunnel ─────────▶│
│ │ │── ejecuta script
│ │ │── copia JAR
│ │ │── restart systemd
│ │◀─── resultado ────────────│
│◀── Resultado ─────────────────│ │
│ │ │
├── Destruye sesión ────────────▶│ │
- Sesiones efímeras: cada sesión tiene un TTL de 1800 segundos
- Autenticación dual: API key de OCI + par de claves SSH del deploy
- Sin estado: no hay un servidor permanente que pueda comprometerse
- Auditable: cada sesión queda registrada en OCI Audit
El repositorio incluye archivos de comunidad estándar de GitHub:
| Archivo | Propósito |
|---|---|
.github/SECURITY.md |
Política de reporte de vulnerabilidades |
.github/CONTRIBUTING.md |
Guía de contribución |
.github/CODE_OF_CONDUCT.md |
Código de conducta |
.github/SUPPORT.md |
Canales de soporte |
.github/PULL_REQUEST_TEMPLATE.md |
Template estandarizado para PRs |
.github/ISSUE_TEMPLATE/ |
Templates para bugs y feature requests |
.github/CODEOWNERS |
Asignación automática de reviewers |
┌──────────────────────────────────────────────────────────────────┐
│ CAPA DE SEGURIDAD │
├──────────────┬───────────────────────────────────────────────────┤
│ Código │ Branch protection, CODEOWNERS, PR obligatorio │
│ │ Code review, CI obligatorio │
├──────────────┼───────────────────────────────────────────────────┤
│ Secrets │ GitHub Secrets (encriptados), OCI Vault │
│ │ Base64 transport, systemd escaping │
├──────────────┼───────────────────────────────────────────────────┤
│ Pipeline │ Tests + cobertura 100%, smoke tests, │
│ │ rollback automático │
├──────────────┼───────────────────────────────────────────────────┤
│ Infraestructura│ Sin IP pública, Bastion, sesiones efímeras │
│ │ IaC (Terraform), cloud-init reproducible │
├──────────────┼───────────────────────────────────────────────────┤
│ Runtime │ systemd isolation (User=ubuntu), │
│ │ Nginx reverse proxy, health checks │
├──────────────┼───────────────────────────────────────────────────┤
│ Observabilidad│ Logs centralizados, métricas, traces, │
│ │ synthetic monitoring, alerting │
└──────────────┴───────────────────────────────────────────────────┘