Faraday en el proceso de desarrollo de software - Jenkins - Parte 2

5 de diciembre de 2022

La última vez explicamos lo fácil que es integrar su instancia de Faraday en el proceso de desarrollo de software de una aplicación escrita en Python y desplegada en Heroku. En ese post, utilizamos Acciones de GitHub como herramienta de CD/CI. (enlazar todas las integraciones)

Ahora vamos a realizar la misma tarea pero en lugar de utilizar Acciones de GitHub, vamos a utilizar Jenkins con pipelines, ya que se trata de una de las herramientas de CD/CI más extendidas en la comunidad.

Consideraciones previas

Como hemos cubierto toda la teoría y los requisitos en el post anterior, vamos a suponer que ya tienes una aplicación escrita en Python, subida a un repositorio Git, y es fácilmente desplegable en Heroku. Si este no es el caso, por favor visita el post mencionado para tener más contexto.

Además, es necesario que tenga una instancia en ejecución de Jenkins con Océano Azul que soporta imágenes docker. Si este no es el caso, te recomendamos asegurarte de que toda esta configuración está hecha antes de empezar a leer este post, ya que intentar seguir este post al mismo tiempo que estás configurando Jenkins podría ser un poco lioso.

Por último, no vamos a explicar cómo configurar Océano Azul y cómo crear Jenkins trabajos porque entendemos que usted ya cuenta con unos conocimientos mínimos de Jenkins.

Paso 1: Creación del archivo Jenkins

Nuestro primer paso es crear un archivo en la raíz de nuestro repositorio llamado Jenkinsfile. Este archivo será leído por nuestra instancia de Jenkins y este archivo indica cómo será el flujo de trabajo de la tubería.

travis

Una vez creado este archivo, tenemos que escribir lo siguiente:

tubería {
      agente ninguno
      etapas {
        etapa('Build') {
        
        }
        stage('Deploy') {        }
        stage('Scan') {        }
        stage('Subir') {
        
        }
    }
}

Vamos a explicar qué significa cada línea:

1-Jenkins pipelines nos permite utilizar el lenguaje Groovy para ejecutar tareas. Además, la primera línea de cada Jenkinsfile debe empezar por tubería {} cierre.

2-Dentro del cierre del oleoducto podemos declarar muchas cosas, pero agentes etapas son obligatorios. Agentes indican dónde se van a ejecutar en diferentes etapas y pueden tener diferentes configuraciones (por ejemplo, pueden ejecutarse en Linux, en Windows, etc.) Etapas son grupos de pasos que pueden tener su propio conjunto de configuración. En nuestro ejemplo, hemos definido agente ninguno porque vamos a utilizar diferentes agentes en cada escenario. Debido a agente ninguno, tenemos que declarar el agente en cada escenario como obligatorio.

3-Por defecto, todas las etapas se ejecutarán secuencialmente, a menos que especifique que deben ejecutarse en paralelo. En nuestro ejemplo, estamos ejecutando sólo esta tubería en un orden secuencial. Además, cada etapa tendrá una etiqueta para reconocer fácilmente cuando se está ejecutando en Jenkins.

Paso 2: Declarar el Construya escenario

Nuestra primera declaración será la Construya etapa. Esta etapa instalará las dependencias necesarias y ejecutará Bandido para realizar la comprobación estática del código:

tubería {
    agente ninguno
    etapas {
        etapa('Build') {
            agente {
                docker {
                    imagen 'python:3.9.1'
                    args '-u root:root'
                }
            }
            steps {
            sh 'pip install -r requisitos.txt'
            sh 'bandit -r . -f xml -o flaskapp_faraday_bandit.xml || true'
            stash name: 'flaskapp_faraday_bandit.xml', includes: 'flaskapp_faraday_bandit.xml'
            }
        
        }
        stage('Deploy') {        }
        stage('Scan') {        }
        stage('Subir') {
        
        }        }
    }
}

Como puede ver, hemos definido la función agente dentro del Construya etapa. Como hemos explicado antes, esto es necesario aquí porque declaramos agente ninguno globalmente. Nuestro agente será un contenedor docker con la etiqueta python:3.9.1 y con permisos de root.

A continuación, definimos los pasos. Cada paso se ejecutará secuencialmente.

En el primer paso se instalan las dependencias mediante el comando requisitos.txt (la dependencia de Bandit se encuentra allí). El segundo paso ejecuta Bandido y guarda la salida en el archivo flaskapp_faraday_bandit.xml archivo.

Algunas notas importantes al respecto:

1En nuestro ejemplo, ignoramos el Bandido resultado de la ejecución sólo para evitar hacer este archivo más complejo.

2Una aplicación real requiere más pasos, pero este ejemplo está simplificado para instalar sólo las dependencias.

Por último, dado que cada escenario podría utilizar diferentes agentes, tenemos que compartir el archivo de resultados entre ellos tal y como hicimos cuando utilizamos Acciones en GitHub. Para ello, utilizamos la función alijo que combinaremos con desempacar más tarde.

Paso 3: Declarar Despliegue escenario

En Despliegue subirá la solicitud a Heroku como se muestra aquí:

tubería {
    agente ninguno
    etapas {
        etapa('Build') {
            agente {
                docker {
                    imagen 'python:3.9.1'
                    args '-u root:root'
                }
            }
            steps {
                sh 'pip install -r requisitos.txt'
                sh 'bandit -r . -f xml -o flaskapp_faraday_bandit.xml || true'
                stash name: 'flaskapp_faraday_bandit.xml', includes: 'flaskapp_faraday_bandit.xml'
            }
         }
         stage('Deploy') {
             agente any
             pasos {
             conCredenciales([
                 string(credentialsId: 'HEROKU_API_KEY', variable: 'HEROKU_API_KEY'),
                 string(credentialsId: 'HEROKU_APP_NAME', variable: 'HEROKU_APP_NAME'),
             ]) {
                script {
                    try {
                        sh 'git remote add heroku https://heroku:$HEROKU_API_KEY@git.heroku.com/$HEROKU_APP_NAME.git'
                        }
                        catch(Exception e) {
                            echo 'El heroku remoto ya existe'
                        }
                    }
                    sh 'git push heroku HEAD:master -f'
                }
           }
        }
        stage('Scan') {        }
        stage('Subir') {
 
        }
    }
}

Hay algunos puntos interesantes que mencionar aquí:

1-Usamos el plugin de credenciales cuando llamamos a la aplicación withCredentials función. Este plugin nos permite almacenar secretos en Jenkins y recuperarlos en nuestros pipelines tal y como hicimos con Acciones en GitHub. Explicaremos cómo almacenar secretos en Jenkins más adelante en este post.

2-Como puede ver, en el primer paso estamos añadiendo el mando a distancia Heroku en Git y ahí es donde estamos usando los secretos.

3-Por último, desplegamos la aplicación en Heroku llamando a git push.

Paso 4: Declarar el Escanear escenario

En Escanear se ejecutará zap sobre nuestra aplicación recién desplegada. Esto será muy similar a lo que hicimos con Acciones en GitHub.

tubería {
    agente ninguno
    etapas {
        etapa('Build') {
            agente {
                docker {
                    imagen 'python:3.9.1'
                    args '-u root:root'
                }
            }
            steps {
                sh 'pip install -r requisitos.txt'
                sh 'bandit -r . -f xml -o flaskapp_faraday_bandit.xml || true'
                stash name: 'flaskapp_faraday_bandit.xml', includes: 'flaskapp_faraday_bandit.xml'
            }
            }
            stage('Deploy') {
                agente any
                pasos {
                    conCredenciales([
                        string(credentialsId: 'HEROKU_API_KEY', variable: 'HEROKU_API_KEY'),
                        string(credentialsId: 'HEROKU_APP_NAME', variable: 'HEROKU_APP_NAME'),
                    ]) {
                        script {
                            try {
                                sh 'git remote add heroku https://heroku:$HEROKU_API_KEY@git.heroku.com/$HEROKU_APP_NAME.git'
                        }
                        catch(Exception e) {
                            echo 'El heroku remoto ya existe'
                        }
                    }
                    sh 'git push heroku HEAD:master -f'
                }
             }
        }
        stage('Scan') {
            agente cualquiera
            pasos {
                conCredenciales([
                    string(credentialsId: 'ZAP_SCAN_URL', variable: 'ZAP_SCAN_URL')
                ]) {
                    sh 'docker run -v $WORKSPACE:/zap/wrk/:rw --network=host -t owasp/zap2docker-stable zap-baseline.py
-t $ZAP_SCAN_URL -x flaskapp_faraday_zap.xml || echo 0'
                    stash name: 'flaskapp_faraday_zap.xml', includes: 'flaskapp_faraday_zap.xml'
                }
            }
        }
        stage('Subir') {
            agente {
                docker {
                    image 'python:3.9.1'
                    args '-u root:root -v $WORKSPACE:/reports'
                }
            }
            steps {
                conCredenciales([
                    string(credentialsId: 'FARADAY_URL', variable: 'FARADAY_URL'),
                    string(credentialsId: 'FARADAY_USERNAME', variable: 'FARADAY_USERNAME'),
                    string(credentialsId: 'FARADAY_PASSWORD', variable: 'FARADAY_PASSWORD')
                ]) {
                    unstash 'flaskapp_faraday_bandit.xml'
                    unstash 'flaskapp_faraday_zap.xml'
                    script {
                        FECHA_ACTUAL = (
                            script "echo ${fecha + '%Y-%m-%d'}"
                            returnStdout: true
                        ).trim()
                        NOMBRE_TRABAJO = (
                            script: "echo $JOB_NAME| cut -d'/' -f1"
                            returnStdout: true
                        ).trim()
                     }
                     sh "pip install faraday-cli"
                     sh "faraday-cli auth -f $FARADAY_VMPIPELINES_FARADAY_URL -u $FARADAY_VMPIPELINES_FARADAY_USERNAME -p $FARADAY_VMPIPELINES_FARADAY_PASSWORD"
                     sh "faraday-cli create_ws $JOB_NAME-$CURRENT_DATE-$BUILD_NUMBER"
                     sh "faraday-cli process_report -w $JOB_NAME-$CURRENT_DATE-$BUILD_NUMBER /reports/flaskapp_faraday_bandit.xml"
                     sh "faraday-cli process_report -w $JOB_NAME-$CURRENT_DATE-$BUILD_NUMBER /reports/flaskapp_faraday_zap.xml"
              }
           }
        }
    }
}

De nuevo, hemos utilizado secretos almacenados en JenkinsFARADAY_URL, FARADAY_USERNAME y FARADAY_PASSWORD.

Como puede ver, los dos primeros pasos son desempacar los archivos generados anteriormente, por lo que ahora son accesibles en el espacio de trabajo de la etapa.

El siguiente paso es la creación de las variables FECHA_ACTUAL y NOMBRE_TRABAJO que se utilizará para crear el Insights y Blog nombre del espacio de trabajo más adelante.

Por último, ejecutamos los mismos comandos que en el post anterior utilizando el comando faraday-cli biblioteca. Tenemos que prestar atención a la configuración del escenario ya que aquí estamos ejecutando todos los pasos dentro de una imagen docker con Python 3. Además, estamos vinculando el espacio de trabajo Jenkins a esta imagen en el directorio /reports.

Como ya hemos comentado, vamos a mostraros una alternativa a la configuración anterior pero ahora utilizando una imagen docker personalizada creada por nosotros

tubería {
    agente ninguno
    etapas {
        etapa('Build') {
            agente {
                docker {
                    imagen 'python:3.9.1'
                    args '-u root:root'
                }
            }
            steps {
                sh 'pip install -r requisitos.txt'
                sh 'bandit -r . -f xml -o flaskapp_faraday_bandit.xml || true'
                stash name: 'flaskapp_faraday_bandit.xml', includes: 'flaskapp_faraday_bandit.xml'
            }
        }
        stage('Deploy') {
            agente any
            pasos {
                conCredenciales([
                    string(credentialsId: 'HEROKU_API_KEY', variable: 'HEROKU_API_KEY'),
                    string(credentialsId: 'HEROKU_APP_NAME', variable: 'HEROKU_APP_NAME'),
                ]) {
                    script {
                        try {
                            sh 'git remote add heroku https://heroku:$HEROKU_API_KEY@git.heroku.com/$HEROKU_APP_NAME.git'
                        }
                        catch(Exception e) {
                            echo 'El heroku remoto ya existe'
                        }
                    }
                    sh 'git push heroku HEAD:master -f'
                }
            }
        }
        stage('Scan') {
            agente cualquiera
            pasos {
                conCredenciales([
                    string(credentialsId: 'ZAP_SCAN_URL', variable: 'ZAP_SCAN_URL')
                ]) {
                    sh 'docker run -v $WORKSPACE:/zap/wrk/:rw --network=host -t owasp/zap2docker-stable zap-baseline.py -t $ZAP_SCAN_URL -x flaskapp_faraday_zap.xml || echo 0'
                    stash name: 'flaskapp_faraday_zap.xml', includes: 'flaskapp_faraday_zap.xml'
                }
            }
        }
        stage('Subir') {
            agente any
            pasos {
                conCredenciales([
                    string(credentialsId: 'FARADAY_URL', variable: 'FARADAY_URL'),
                    string(credentialsId: 'FARADAY_USERNAME', variable: 'FARADAY_USERNAME'),
                    string(credentialsId: 'FARADAY_PASSWORD', variable: 'FARADAY_PASSWORD')
                ]) {
                    unstash 'flaskapp_faraday_bandit.xml'
                    unstash 'flaskapp_faraday_zap.xml'
                    script {
                        FECHA_ACTUAL = (
                            script "echo ${fecha + '%Y-%m-%d'}"
                            returnStdout: true
                        ).trim()
                        NOMBRE_TRABAJO = (
                            script: "echo $JOB_NAME| cut -d'/' -f1"
                            returnStdout: true
                        ).trim()
                    }                    sh "docker build https://github.com/infobyte/docker-faraday-report-
uplodaer.git#master -t faraday-uploader"sh "docker run --rm -v $WORKSPACE:$WORKSPACE -e HOST=$FARADAY_URL -e USERNAME=$FARADAY_USERNAME -e PASSWORD=$FARADAY_PASSWORD -e WORKSPACE=$JOB_NAME-$CURRENT_DATE-$BUILD_NUMBER -e FILES=$WORKSPACE/flaskapp_faraday_bandit.xml faraday-uploader"
                   sh "docker run --rm -v $WORKSPACE:$WORKSPACE -e HOST=$FARADAY_URL -e USERNAME=$FARADAY_USERNAME -e PASSWORD=$FARADAY_PASSWORD -e WORKSPACE=$JOB_NAME-$CURRENT_DATE-$BUILD_NUMBER -e FILES=$WORKSPACE/flaskapp_faraday_zap.xml faraday-uploader"
                }
            }
        }
    }
}

Este enfoque es bastante similar al anterior pero, en lugar de ejecutarse dentro de una imagen docker, se ejecuta en un nodo Jenkins.

La parte más importante de este planteamiento aparece en las 3 últimas líneas de la etapa:

1-Primero realizamos una compilación de nuestra imagen docker personalizada.

2-Luego ejecutamos el contenedor dos veces usando la imagen construida previamente (un paso por fichero de informe). Es importante mencionar que, para permitir que la imagen docker acceda a los archivos de informes no almacenados, hemos ejecutado los contenedores utilizando el parámetro -v $WORKSPACE:$WORKSPACE que permite a los contenedores docker ver el espacio de trabajo Jenkins en una ruta absoluta.

Como puede ver, la configuración de Jenkinsfile es bastante sencilla y no tiene muchas diferencias en comparación con Acciones en GitHub. Ahora vamos a explicar cómo ejecutar nuestro nuevo Jenkinsfile en Jenkins utilizando el Océano Azul plugin.

Paso 7: Almacenamiento de secretos en Jenkins

Como mencionamos, usamos secretos para las variables más importantes durante la creación del Jenkinsfile. Hicimos esto porque almacenar credenciales o claves en un repositorio público no es una buena práctica.

Para añadir nuevos secretos, tenemos que ir a nuestro Jenkins (en nuestro caso es http://localhost:8080). A continuación, tenemos que hacer clic en Gestionar Jenkins en el menú de la izquierda como puedes ver en la siguiente imagen

jenkins34

Ahora tenemos que encontrar el Gestionar credenciales en el Seguridad sección:

jenkins52

Una vez dentro, haremos clic en el botón (globales) opción:

jenki74s

Si nunca hemos añadido un secreto, aparecerá un mensaje diciendo que nuestra bóveda de credenciales está vacía con un enlace para que creemos un nuevo secreto. Además, podemos hacer clic en la opción del menú de la izquierda llamada Añadir credenciales:

jen63

Una vez que nos encontremos en la nueva página de credenciales, necesitamos cambiar el tipo de credencial. Para propósitos de ejemplo, vamos a usar Texto Secreto de tipo de credencial (para nombre de usuario y contraseña de Faraday también). Después de seleccionar el tipo de credencial, necesitamos escribir el ID y el Secreto. El ID debe ser el mismo que hemos utilizado en el Jenkinsfile y en el secreto es el valor:

jenkins14

Todas las credenciales están disponibles en Jenkins para todos los trabajos, por lo que nuestra recomendación es utilizar un prefijo por trabajo para evitar problemas futuros. Recuerda que debes actualizar el archivo Jenkinsfile con las nuevas credenciales. En nuestro caso, hemos añadido el prefijo FARADAY_VMPIPELINES_.

jenkins85

Una vez que hemos añadido todos los secretos, tenemos que configurar nuestra tubería de entrar en /blue url bajo nuestra Jenkins (en nuestro caso es http://localhost:8080/blue).

Si es la primera vez que utiliza este plugin, sólo habrá una opción y será Añadir nueva tubería. Si no es la primera vez, puede hacer clic en el botón Nuevo gasoducto botón.

El nuevo asistente de canalización es bastante sencillo, por lo que no vamos a detallar aquí cómo añadir el repositorio. Recuerda que si tienes un repositorio en GitHub necesitas crear un token personal y añadirlo en Jenkins. Esto permitirá a Jenkins ver todos sus repositorios.

Una vez seleccionado el repositorio que tiene el Jenkinsfile, Jenkins lo ejecutará por primera vez. Como ocurrió con Acciones de GitHub, es posible configurar las condiciones para activar la ejecución de los trabajos haciendo clic en el icono de configuración.

jenkins95

Una vez finalizada la ejecución, verá los resultados como en la siguiente imagen:

jenkins82

Como habrás notado, los resultados de la ejecución fueron exitosos. Ahora podemos ver nuestro nuevo espacio de trabajo en nuestra instancia Faraday.

image (17)

Y eso es todo. Ahora puede ir a su Insights y Blog y comprueba el nuevo espacio de trabajo como hicimos en las entradas anteriores.

image (16)image (15)

Descargue aquí todos nuestros documentos sobre DevSecOps

Seguir leyendo

Los últimos artículos del blog

Esta versión responde a las antiguas peticiones de los clientes y refuerza nuestra misión de hacer que la gestión de vulnerabilidades sea más sencilla, transparente y práctica.

19 de febrero de 2026

Nuestro equipo de investigación de seguridad investiga activamente vulnerabilidades en tecnologías de uso generalizado, con especial atención a la infraestructura de redes y los sistemas integrados. A lo largo de 2024, nuestros investigadores informaron de múltiples problemas de seguridad que afectaban a

8 de enero de 2026

Cada año, la Ekoparty nos recuerda por qué amamos lo que hacemos.Nos preparamos con la misma energía de siempre: para reencontrarnos con la comunidad, compartir lo que investigamos, aprender de otros y..,

6 de enero de 2026

Manténgase informado, suscríbase a nuestro boletín

Introduzca su correo electrónico y no se pierda nunca las alertas y consejos de seguridad de los expertos de Faraday.

Faraday ayuda a grandes empresas, MSSPs y equipos de seguridad de aplicaciones a aprovechar mejor su ecosistema de seguridad, optimizando lo que ya utilizan.

Sede central

Laboratorio de investigación y desarrollo

Soluciones

Código abierto

2025 Faraday Security. Todos los derechos reservados.
Términos y condiciones | Política de privacidad