Pipeline filtrando secretos

Pipeline filtrando secretos

Enunciado

El equipo de desarrollo lleva meses subiendo código a Gitea. Hace una semana, un API key de un servicio crítico apareció en un repositorio público de GitHub. El CTO te pide auditar todo el historial de los repositorios internos y evitar que esto vuelva a pasar.

Herramientas

  • Docker
  • Gitea
  • Jenkins
  • Gitleaks

Asignación

  1. Ejecuta gitleaks contra cada repositorio. Gitleaks recorre todo el historial de commits, no solo el actual.

  2. Añade un job en Jenkins que ejecute gitleaks antes de cada despliegue a producción. Si encuentra un secreto, el pipeline falla y bloquea la operación.

  3. Configurar un hook local para que los desarrolladores reciban una advertencia antes de hacer commit.

Pasos realizados

Tenemos nuestro servicio Gitea ejecutándose en nuestro servidor sin ningún inconveniente; este le da alojamiento al código del proyecto.

Gitea

Descargamos la última versión de Gitleaks desde su repositorio oficial Gitleaks. Para escanear el proyecto, debemos clonarlo y ejecutar la herramienta en el repositorio local.

Git Clone

Ejecutamos la herramienta con:

./gitleaks detect --source ./webapp/ --verbose

Observamos que en la rama Develop se encuentran claves y tokens de servicio que no deberían estar allí:

Git Clone

Automatizar Pruebas con Jenkins

Debemos impedir que los tokens lleguen al ambiente de producción; esto se logra integrando Gitleaks en el pipeline y haciendo que falle al detectar algún secreto en el código.

El siguiente pipeline de Jenkins descarga el código del repositorio, lo analiza con Gitleaks; si este descubre alguna credencial, detiene la ejecución y no realiza el despliegue.

pipeline {
    agent any

    environment {
        // Configuración del repositorio Git (usando el nombre del servicio Gitea en Docker)
        BRANCH_NAME = 'develop'
        REPO_URL = 'http://172.17.0.1:3000/admin/webapp.git'
        GIT_CREDENTIALS_ID = 'gitTeaUser'

        // Versión de Gitleaks a descargar
        GITLEAKS_VERSION = '8.30.1'
        // Para Linux x86_64 (el más común en agentes Jenkins)
        GITLEAKS_ARCH = 'linux_x64'
        GITLEAKS_TAR = "gitleaks_${GITLEAKS_VERSION}_${GITLEAKS_ARCH}.tar.gz"
        GITLEAKS_URL = "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/${GITLEAKS_TAR}"
        // Directorio donde se instalará Gitleaks (dentro del workspace)
        GITLEAKS_HOME = "${WORKSPACE}/gitleaks_bin"
        GITLEAKS_BIN = "${GITLEAKS_HOME}/gitleaks"
    }

    stages {
        // 1. Clonar el repositorio
        stage('Checkout') {
            steps {
                script {
                    cleanWs()
                    checkout scmGit(
                        branches: [[name: "*/${env.BRANCH_NAME}"]],
                        userRemoteConfigs: [[
                            url: env.REPO_URL,
                            credentialsId: env.GIT_CREDENTIALS_ID
                        ]]
                    )
                }
            }
        }

        // 2. Descargar e instalar Gitleaks (con rutas entre comillas)
        stage('Instalar Gitleaks') {
            steps {
                script {
                    sh """
                        echo "📦 Creando directorio para Gitleaks: ${GITLEAKS_HOME}"
                        mkdir -p "${GITLEAKS_HOME}"

                        echo "⬇️  Descargando Gitleaks ${GITLEAKS_VERSION} desde GitHub..."
                        curl -L -o "${GITLEAKS_HOME}/${GITLEAKS_TAR}" "${GITLEAKS_URL}"

                        # Verificar que el archivo se descargó
                        if [ ! -f "${GITLEAKS_HOME}/${GITLEAKS_TAR}" ]; then
                            echo "❌ Error: No se pudo descargar Gitleaks"
                            exit 1
                        fi

                        echo "📂 Extrayendo el binario en ${GITLEAKS_HOME}..."
                        tar -xzf "${GITLEAKS_HOME}/${GITLEAKS_TAR}" -C "${GITLEAKS_HOME}"

                        echo "🔧 Haciendo ejecutable el binario..."
                        chmod +x "${GITLEAKS_BIN}"

                        echo "✅ Gitleaks instalado correctamente en ${GITLEAKS_BIN}"
                    """
                }
            }
        }

        // 3. Análisis con Gitleaks (escanea el código clonado)
        stage('Análisis con Gitleaks') {
            steps {
                script {
                    sh """
                        echo "🔍 Ejecutando Gitleaks en el repositorio..."
                        "${GITLEAKS_BIN}" detect --source . --verbose --no-git
                        GITLEAKS_EXIT=\$?
                        if [ \$GITLEAKS_EXIT -ne 0 ]; then
                            echo "❌ Gitleaks ha encontrado secretos. Deteniendo el pipeline."
                            exit 1
                        else
                            echo "✅ No se encontraron secretos. Continuando..."
                        fi
                    """
                }
            }
        }

        // 4. Compilación (simulada)
        stage('Compilación (Simulada)') {
            steps {
                script {
                    echo "🛠️ Iniciando compilación simulada del proyecto..."
                    sh 'echo "   - Compilando código fuente..."'
                    sh 'echo "   - Ejecutando pruebas unitarias..."'
                    sh 'echo "   - Generando artefactos..."'
                    sleep time: 5, unit: 'SECONDS'
                    echo "✅ Compilación simulada completada con éxito."
                }
            }
        }

        // 5. Despliegue a producción (simulado)
        stage('Despliegue a Producción (Simulado)') {
            steps {
                script {
                    echo "🚀 Iniciando despliegue a producción (simulado)..."
                    sh 'echo "   - Empaquetando aplicación..."'
                    sh 'echo "   - Subiendo artefactos al servidor..."'
                    sh 'echo "   - Reiniciando servicios..."'
                    sleep time: 5, unit: 'SECONDS'
                    echo "✅ Despliegue a producción simulado completado."
                    echo "🎉 Pipeline finalizado exitosamente."
                }
            }
        }
    }

    post {
        success {
            echo "✅ ¡Pipeline exitoso! El proyecto ha sido compilado y desplegado (simulación)."
        }
        failure {
            echo "❌ El pipeline ha fallado. Revisa los logs para más detalles."
        }
        always {
            echo "🏁 Pipeline finalizado. Estado: ${currentBuild.currentResult}"
            // Limpieza opcional del binario descargado (descomentar si se desea)
            // sh "rm -rf \"${GITLEAKS_HOME}\""
        }
    }
}

Jenkins


Jenkins

Como vemos, el pipeline no continúa, ya que detecta credenciales.

Creación de hook local

Para crear un hook local, accedemos al directorio .git del proyecto:

Hook

Dentro de la carpeta hooks debemos crear un archivo llamado pre-push sin extensión.

cd /var/lib/gitea/repositories/<usuario>/<repositorio>.git/hooks/
nano pre-push

Creamos el script a ejecutar al subir cambios; si Gitleaks encuentra un secreto, debe salir un error y rechazar el push.

#!/bin/bash
# Hook pre-push simple: escanea todo el directorio actual con Gitleaks

GITLEAKS_BIN="${GITLEAKS_BIN:-gitleaks}"

echo "🔍 Escaneando todos los archivos del repositorio (incluyendo historial)..."

# Escaneamos todo el repositorio (archivos actuales + historial con --no-git? no, usamos --source . pero eso escanea solo archivos actuales)
# Para escanear el historial de commits, Gitleaks debe ejecutarse con --source . y --no-git NO se usa, o se usa sin --no-git
# Pero sin --no-git, Gitleaks escanea el historial de Git directamente.
# Esa es la forma más simple y efectiva para un hook local:

$GITLEAKS_BIN detect --source . --verbose
GITLEAKS_EXIT=$?

if [ $GITLEAKS_EXIT -ne 0 ]; then
    echo "❌ Gitleaks ha detectado secretos en el repositorio. Push bloqueado."
    echo "   Utiliza 'git push --no-verify' para forzar (no recomendado)."
    exit 1
else
    echo "✅ Sin secretos detectados. Continuando..."
    exit 0
fi

Al intentar subir cambios al repositorio, Gitleaks impide la operación, ya que detecta tokens en el proyecto.

Hook


Hook

Con esto evitamos que sean subidos al repositorio tokens y claves de muchos de los servicios web y cloud.

Categories: DevSecOps 
Tags: Docker Git Gitleaks Jenkins 

See also