Automating Container Image Security with GitLab: A Comprehensive Guide using Dockerlinter, Conftest, SNYK API, and Docker Hub
Published: 2023-12-14 13:13:56

**Introduction:**
_As containerized applications become integral to modern DevOps workflows, ensuring the security of container images is paramount. This guide explores a GitLab CI pipeline setup that seamlessly integrates security scanning using the SNYK API, validation with Conftest, and artifact management on Docker Hub and linting the Dockerfile using Dockerlinter_.

**Key Components and Tools:**

**Conftest**: _Policy as Code (PaC) tool for validating configuration files against Rego policies._

**SNYK Secure API:** _Leveraging Snyk for vulnerability scanning and image security._

**Docker Hub:** _Centralized container image registry for storing and managing Docker images._

**DockerLinter:** _Dockerlinter is a Dockerfile linter that focuses on best practices and recommendations from the official Docker documentation_

**_DockerFile Sample_**

```
# Use an official Python runtime as a parent image
FROM python:3.8-slim

# Set the working directory to /app
WORKDIR /app

# Copy the current directory contents into the container at /app
COPY . /app

# Install any needed packages specified in requirements.txt
RUN pip install --no-cache-dir -r requirements.txt

# Make port 80 available to the world outside this container
EXPOSE 80

# Define environment variable
ENV NAME World

# Run app.py when the container launches
CMD ["python", "app.py"]

```

**Sample Rego Code to Validate DockerFile**

```
package main

suspicious_env_keys = [
    "passwd",
    "password"
]

# Looking for suspicious environment variable settings
deny[msg] {
    dockerenvs := [val | input[i].Cmd == "env"; val := input[i].Value]
    dockerenv := dockerenvs[_]
    envvar := dockerenv[_]
    lower(envvar) == suspicious_env_keys[_]
    msg = sprintf("Potential secret in ENV found: %s", [envvar])
}

```

> You can create your own Rego code to enforce granular restrictions or policies in a more fine-grained manner, you can customize the Rego policy accordingly. 

**Here is the pipeline**

```
---
variables:
  IMAGE_CONFTEST: "omvedi25/conftest:v0.2"
  IMAGE_SNYK: "omvedi25/snyk:v0.1"
  IMAGE_LINTER: "omvedi25/dockerlinter:v0.0"

stages:
  - lint
  - validation
  - scan
  - push

linting:Dockerfile:
  image: "$IMAGE_LINTER"
  stage: lint
  script:
    - dockerlinter -f Dockerfile -e
    
validation:Dockerfile:
  image: "$IMAGE_CONFTEST"
  stage: validation
  script:
    - conftest test -p /policy Dockerfile

scanning:Image:
  image: "$IMAGE_SNYK"
  stage: scan
  script:
    - podman build -t test .
    - image_id=`podman images --format "table {{.Repository}}\t{{.ID}}" | awk '$1 == "localhost/test" {print $2}'`
    - podman save $image_id -o test.tar
    - snyk auth $SNYK_TOKEN
    - snyk container test docker-archive:test.tar --json > results.json || true
    - snyk-to-html -i results.json -o results.html
  artifacts:
    paths:
      - results.html
    expire_in: 1 week

pushing:Image:
  image: "$IMAGE_SNYK"
  stage: push
  script:
    - podman login -u $USERNAME -p $PASSWORD docker.io 
    - podman build -t test .
    - podman tag localhost/test:latest omvedi25/$CI_PROJECT_NAME:$CI_COMMIT_SHA
    - podman images
    - podman push omvedi25/$CI_PROJECT_NAME:$CI_COMMIT_SHA
  when: manual  

```
**Pipeline Stages**


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dsgaep27msp5uycxom26.png)

_Linting Output_


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jvqhhmsxh80ivmxmdbkd.png)

_Validation Output_


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/osdrpvo6wp0tfladlzps.png)

_SNKY HTML REPORT_


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9sx3bnlqnahhimzhzgpw.png)

_PUSH IMAGE_


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c5xhii7aesh5395pzacu.png)