11. Security
GitLab offers a wide support for security issues. In this lab, we will cover the GitLabs internal security templates first, and then we will show you how to include custom security tools into your pipeline.
Task 11.1: Security Template Lab
In this lab we will show you how to extend a job with another job from a shared template. Execute following tasks
- Include the template
Security/SAST.gitlab-ci.yml - Include the template
templates/Secret-Detection.ymlfrom the same repository - Include the template
Security/License-Scanning.gitlab-ci.yml
Go to your GitLab project and check the pipeline for this lab. You should find the additional Jobs on the pipeline overview.
11.2: Security Template Lab Solution
Updated .gitlab-ci.yml file for this lab:
show solution
stages:
- info
- build
- test
- package
- deploy
include:
- project: '${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}'
file:
- 'templates/k8s.yml'
- 'templates/Secret-Detection.yml'
- template: Security/SAST.gitlab-ci.yml
- template: Security/License-Scanning.gitlab-ci.yml
variables:
GIT_STRATEGY: 'clone'
COMPILE: 'false'
IMAGE_HOST: 'quay.io'
IMAGE_REPOSITORY: 'puzzle'
IMAGE_NAME: 'example-spring-boot'
default:
timeout: 5 minutes
info:
stage: info
retry: 2
script:
- echo "This is your first stage (ツ)"
- echo "Username is ${USERNAME} with ${PASSWORD}"
build_application:
stage: build
tags:
- build
- mobiliar
image: registry.access.redhat.com/ubi9/openjdk-17:latest
script:
- ./gradlew assemble
test_application:
stage: test
image: registry.access.redhat.com/ubi9/openjdk-17:latest
script:
- ./gradlew check
artifacts:
when: always
reports:
junit: build/test-results/test/**/TEST-*.xml
build_image:
image: diemobiliar.azurecr.io/dlp-cicd-dockercli-image:254
tags:
- build
- mobiliar
services:
- docker:dind
stage: package
before_script:
- docker info
script:
- docker build --no-cache -t $IMAGE_NAME .
- IMAGE_PATH=$IMAGE_HOST/$IMAGE_REPOSITORY/$IMAGE_NAME:${CI_COMMIT_SHA:0:8}
- echo "docker image path is - $IMAGE_PATH"
- docker tag $IMAGE_NAME $IMAGE_PATH
# - docker login -u ${DOCKER_USERNAME} -p ${DOCKER_PASSWORD} ${IMAGE_HOST}
# - docker push $IMAGE_PATH
deploy_to_prod:
stage: deploy
when: manual
only:
- release
script:
- echo "Deployment triggered"
deploy_to_k8s:
stage: deploy
extends: .kubectl
tags:
- build
- mobiliar
script:
- echo "deploy your stuff here with kubectl commands"
- kubectl version --client --short
Task 11.3: Let the pipeline fail
To test the Secret Detection template we add a secret in our sample project. Create a new file in the root directory called private_key and copy the following content into it.
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----
Commit and push the changes to trigger the pipeline.
Now the pipeline should fail because the GitLab Secret Analyzer will find a private key inside our repository.

Here you see the failed pipeline. By clicking the download menu ⍗ on the right side you can download the job artifacts. To get the report of the failed job, click on Download artifacts and secret_detection_default_branch:secret_detection. We will learn more on artifacts in the next lab.
Warning
If you ever push a secret into a repository, YOU MUST CONSIDER THIS SECRET AS INSECURE. Remove the secret from the repository and change all related secrets!Now remove the private key from the repository to execute the next steps.
Task 11.4: Custom security tooling
In this section we’ll show you how to use custom security tools in your pipeline. For this example we use Trivy , a local Docker image security scanner.
Extend the existing build_image job inside .gitlab-ci.yml with the following configuration:
Add following commands into the before_script block to download Trivy:
export TRIVY_VERSION=$(wget -qO - "https://api.github.com/repos/aquasecurity/trivy/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
echo $TRIVY_VERSION
wget --no-verbose https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz -O - | tar -zxvf -
Then call Trivy in the script block (after the build command):
mkdir -p reports
./trivy i --exit-code 0 --severity CRITICAL -o reports/container-scanning-report_$CI_COMMIT_SHORT_SHA.txt $IMAGE_NAME
Commit and push the code to rerun the pipeline.
Check the Trivy log output of the build_image Job.
Info
You would do the security scanning in a separate job. Here we do not have an Image Registry to store the Container Image. This would be used to retrieve the Container Image in the next Job to do the scanning.11.5: Custom security tooling Solution
Updated .gitlab-ci.yml file for this lab:
show solution
stages:
- info
- build
- test
- package
- deploy
include:
- project: '${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}'
file:
- 'templates/k8s.yml'
- 'templates/Secret-Detection.yml'
- template: Security/SAST.gitlab-ci.yml
- template: Security/License-Scanning.gitlab-ci.yml
variables:
GIT_STRATEGY: 'clone'
COMPILE: 'false'
IMAGE_HOST: 'quay.io'
IMAGE_REPOSITORY: 'puzzle'
IMAGE_NAME: 'example-spring-boot'
default:
timeout: 5 minutes
info:
stage: info
retry: 2
script:
- echo "This is your first stage (ツ)"
- echo "Username is ${USERNAME} with ${PASSWORD}"
build_application:
stage: build
tags:
- build
- mobiliar
image: registry.access.redhat.com/ubi9/openjdk-17:latest
script:
- ./gradlew assemble
test_application:
stage: test
image: registry.access.redhat.com/ubi9/openjdk-17:latest
script:
- ./gradlew check
artifacts:
when: always
reports:
junit: build/test-results/test/**/TEST-*.xml
build_image:
image: diemobiliar.azurecr.io/dlp-cicd-dockercli-image:254
tags:
- build
- mobiliar
services:
- docker:dind
stage: package
before_script:
- docker info
- export TRIVY_VERSION=$(wget -qO - "https://api.github.com/repos/aquasecurity/trivy/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
- echo $TRIVY_VERSION
- wget --no-verbose https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz -O - | tar -zxvf -
script:
- docker build --no-cache -t $IMAGE_NAME .
- mkdir -p reports
- ./trivy i --exit-code 0 --severity CRITICAL -o reports/container-scanning-report_$CI_COMMIT_SHORT_SHA.txt $IMAGE_NAME
- IMAGE_PATH=$IMAGE_HOST/$IMAGE_REPOSITORY/$IMAGE_NAME:${CI_COMMIT_SHA:0:8}
- echo "docker image path is - $IMAGE_PATH"
- docker tag $IMAGE_NAME $IMAGE_PATH
# - docker login -u ${DOCKER_USERNAME} -p ${DOCKER_PASSWORD} ${IMAGE_HOST}
# - docker push $IMAGE_PATH
deploy_to_prod:
stage: deploy
when: manual
only:
- release
script:
- echo "Deployment triggered"
deploy_to_k8s:
stage: deploy
extends: .kubectl
tags:
- build
- mobiliar
script:
- echo "deploy your stuff here with kubectl commands"
- kubectl version --client --short