Recently, I have been working with users who are using GitLab for Source Code Management (SCM) that are still in the process of migrating to GitLab for CI/CD. In this case, the users are not ready to move everything from Jenkins directly to GitLab just yet, but want to take advantage of all the Security benefits of GitLab Ultimate/Gold .
In this blog, I will walk you through setting up external Jenkins jobs along with deterministic security scans. Deterministic security scans block the pipeline from proceeding if a vulnerability was detected. You can follow along with the video below to configure Jenkins and GitLab.
The sample project I am using contains code showing how to call Jenkins as well as how to setup deterministic security scans.
How to add external Jenkins jobs to GitLab
You can call jobs from external CI platforms such as Jenkins, from GitLab. You can use the Jenkins REST API to start a Jenkins job. In this demo, I show you how to do it in Python using python-jenkins, however there are many Jenkins Remote Access APIs available in different languages.
You can install python-jenkins by running:
pip3 install python-jenkins
Here's a simple script which a GitLab pipeline can run in order to call a Jenkins job:
import os import sys import jenkins import time server = jenkins.Jenkins(os.environ["JENKINS_URL"], username=os.environ["JENKINS_USER"], password=os.environ["JENKINS_PWRD"]) job_name = os.environ["JENKINS_JOB"] server.build_job(job_name) # wait until last build is complete and then get the result last_build_number = server.get_job_info(job_name)['lastCompletedBuild']['number'] next_build_number = server.get_job_info(job_name)['nextBuildNumber'] build_info = server.get_build_info(job_name, last_build_number) # try max 2 mins before timing out timeout = time.time() + 60 * 2 while last_build_number != next_build_number: if time.time() > timeout: sys.exit(1) last_build_number = server.get_job_info(job_name)['lastCompletedBuild']['number'] build_info = server.get_build_info(job_name, last_build_number) result = build_info["result"] # return the status of the job if result != "SUCCESS": print("Build Failed") sys.exit(1) sys.exit(0)
The required environment variables include:
- JENKINS_USER: Your Jenkins username
- JENKINS_PASSWORD: Your Jenkins password
- JENKINS_URL: Path to your Jenkins server
- JENKINS_JOB: Path of your Jenkins job
Deterministic security scans
Oftentimes, static security scans run after the build stage. If any of these scans detect vulnerabilities, there are cases when we should not move on to the deployment stage, because of limited resources and time constraints. We're discussing a built-in way of doing this but for now there is a simple work around by editing the gitlab-ci.yaml.
Here is a gitlab-ci.yaml file that runs Static Application Security Testing (SAST), then generates a report, and finally blocks the pipeline from proceeding if any vulnerability was detected. It achieves this by parsing the SAST report JSON.
stages: - test - verify include: - template: SAST.gitlab-ci.yml sast: stage: test variables: CI_DEBUG_TRACE: "true" allow_failure: true artifacts: paths: - gl-sast-report.json reports: sast: gl-sast-report.json sast-security-block: stage: verify needs: - job: sast artifacts: true script: - cat gl-sast-report.json - apk add jq - export VULNS=$(jq '.vulnerabilities | length' gl-sast-report.json) - if $VULNS -ne '0'; then exit 1; fi allow_failure: false
Note: Deterministic security scans can be setup for all of GitLab Security scans. The JSON reports may be different per scan.
- SAST.gitlab-ci.yml template: Uses the standard SAST template to run SAST against the repo
- sast: Configures the sast scanner
- sast-security-block: Looks at the report generated by the SAST scan and just checks if anything was detected
I hope this blog can help you get started using GitLab's security features while using Jenkins for CI.
Learn more about GitLab Security
- How secure is GitLab?
- GitLab instance: security best practices
- How to overcome toolchain security challenges with GitLab