Skip to content

DevSecOps Practices

Anderson Fabian Garcia Nieto edited this page Jul 9, 2026 · 5 revisions

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.


🔐 Gestión de Secrets

GitHub Secrets

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

GitHub Variables (no sensibles)

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

OCI Vault

Los secrets críticos de infraestructura (SSH keys, passwords de base de datos) se almacenan adicionalmente en OCI Vault (https://cloud.oracle.com/security/secrets?region=sa-bogota-1), proporcionando:

  • Encriptación en reposo con claves gestionadas por OCI
  • Acceso auditado
  • Rotación sin cambiar la configuración del pipeline

Transporte seguro de secrets

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")

🌿 Protected Branches

Rama main (Producción)

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)

Rama DEV (Integración)

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.


👥 Pull Requests

Flujo de trabajo

feature/* ──── PR ────▶ DEV ──── PR ────▶ main
                  │                  │
                  ▼                  ▼
            1 approval          1 approval
            CI verde            CI verde
            Review comments     Review comments
            resueltos           resueltos

Template de Pull Request

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

CODEOWNERS

* @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

🤖 CI Obligatorio

Checks que deben pasar antes de merge

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

Cobertura de código

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.


↩️ Rollback

Automático (Pipeline)

El job rollback en pipeline.yml se ejecuta automáticamente si el deploy o la validación fallan:

  1. Identifica el tag SemVer anterior al actual
  2. Descarga el JAR de ese release desde GitHub Releases
  3. Lo despliega vía Bastion
  4. Verifica que el servicio responde 200

Manual (SSH vía Cloud Shell)

ssh linker
cd linker1
bash scripts/rollback.sh v1.0.0

El script:

  1. Verifica el tag existe
  2. Detiene el servicio
  3. Cambia al código del tag anterior
  4. Reconstruye con deploy.sh
  5. Verifica el servicio está activo y responde

Rollback de Feature Flags

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.


🏰 Bastion (Acceso Seguro)

¿Qué es?

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.

¿Por qué?

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.

Cómo funciona

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 ────────────▶│                           │

Seguridad del Bastion

  • 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

📋 Community Health Files

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

🛡️ Resumen de Controles de Seguridad

┌──────────────────────────────────────────────────────────────────┐
│                    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                   │
└──────────────┴───────────────────────────────────────────────────┘

Clone this wiki locally