Coincidentally, I did exactly this a while ago. Here is how I did it:
docker:build:
stage: build
image:
name: Kaniko image
entrypoint: [""]
script:
- mkdir tar_images
- mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
- /kaniko/executor --context ${CI_PROJECT_DIR} --no-push --destination $CI_REGISTRY/<image_name>:<image_tag> --tarPath tar_images/$file_name.tar
artifacts:
paths:
- tar_images
when: on_success
# scan all built images
# currently bug with grype as docker registry is not public!
docker:scan:
stage: scan
image:
name: trivy
entrypoint: [""]
script:
- mkdir scan_result
- cd tar_images
- |
for tar_image in *.tar;
do
[ -e "$tar_image" ] || continue;
file_name=${tar_image%.*};
echo $file_name;
if [ "$vulnerability_scanner" = "trivy" ]; then
trivy image --timeout 15m --offline-scan --input $tar_image -f json -o ../scan_result/$file_name.json --severity CRITICAL;
fi
done
artifacts:
paths:
- scan_result
expire_in: 1 month
# push all images without detected security issues
docker:push:
stage: push
image:
name: gcr.io/go-containerregistry/crane:debug
entrypoint: [""]
rules:
- if: $UPDATE
script:
- cd tar_images
- |
for tar_image in *.tar;
do
file_name=${tar_image%.*};
vulnerabilities=`awk -F '[:,]' '/"Vulnerabilities"/ {gsub("[[:blank:]]+", "", $2); print $2}' ../scan_result/$file_name.json`; # find vulnerabilities in json file
if ! [ -z "$vulnerabilities" ]; then # if vulnerabilities found in image
echo "There are security issues with the image $img.Dockerfile. Image is not pushed to registry!";
else # push image
crane auth login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY;
crane push $tar_image $CI_REGISTRY_IMAGE/<image_name>:<image_tag>;
fi
done
What happens here is that in the first job the images are built using kaniko. They are stored as tar files and made accessible to the next job via artifacts. In the next job they are scanned using trivy and the scan results are stored as artifacts. Then the scan reports are analyzed and if no vulnerabilities had been detected the image is pushed using crane.
The code above probably does not work out of the box as I copied it out of a bigger yaml file.