I speak here {^_^}

How to add a new check in KubeLinter? #31

Aug 17, 2021

Contents

Introduction

The following document demonstrates the steps followed while adding a new validation check in the upstream kube-linter project.

KubeLinter is a static analysis tool that checks Kubernetes YAML files and Helm charts to ensure the applications represented in them adhere to best practices.

kube-linter GitHub Project

This document uses the static check latest-tag, as an example to lay down the steps written below.

The latest-tag validation check indicates when a deployment-like kubernetes object (i.e. deployments, statefulsets, etc) is running a container with an invalid container image (by default, flags for a floating image tag like “latest”)

Steps to add a new check


[STEP 1] Setup the project on your local machine

  • Clone the project repository & change directory to the project root

    git clone git@github.com:stackrox/kube-linter.git
      
    cd kube-linter/
    

[STEP 2] Setup a new directory for the new validation check

  • From the root of the project, change directory to the pkg/templates/ folder

    cd pkg/templates
    
  • Create a new directory with the name corresponding to the new check (for example, in this case, latesttag)

    mkdir latesttag
      
    cd latesttag/
    

[STEP 3] Implement the logic for the new check under the new directory

  • Create the following files & folders in the path pkg/templates/latesttag/

    mkdir -p internal/params
      
    touch internal/prarms/params.go
      
    touch template.go template_test.go
    
  • The above files can be understood as (& referred from):

    • internal/params/params.go

      The internal/params/params.go file will contain the paramaters required for the test check.

    • template.go

      The actual implementation logic for the new test check will be written in the template.go file.

    • template_test.go

      And finally, the unit tests to validate the new check logic will be written in template_test.go file

[STEP 4] Make the new check visible to kube-linter

  • From the root of the project, modify the pkg/templates/all/all.go file to include the path to our new test, i.e. pkg/templates/latesttag

    vim pkg/templates/all/all.go
    
  • And modify the file to look like following:

    package all
    
    import (
      // Import all check templates.
      ...
      _ "golang.stackrox.io/kube-linter/pkg/templates/latesttag"
      ...
    )
    

[STEP 5] Include the new check as a kube-linter builtin check

  • From the root of the project, go to the pkg/builtin-checks/yamls folder & make a new yaml file corresponding to the name of the new check, (for example, latest-tag.yaml).

    vim latest-tag.yaml
    
  • And edit the file to look like following:

    name: "latest-tag"
    description: "Indicates when a deployment-like object is running a container with an invalid container image"
    remediation: "Use a container image with a proper image tag satisfying the \"AllowList\" & \"BlockList\" regex patterns."
    scope:
      objectKinds:
        - DeploymentLike
    template: "latest-tag"
    params:
      BlockList: [".*:(latest)$"]
      AllowList: []
    
  • The latest-tag.yaml file does the following:

    • provide self-explanatory string values for name, description & remediation fields
    • define the scope of the check, using the following block

      scope:
        objectKinds:
          - DeploymentLike
      
    • references the new test check template (defined at pkg/templates/latesttag/template.go), for example, template: "latest-tag"
    • provides default paramaeter values required by the check validation logic (defined at pkg/templates/latesttag/internal/params/params.go)

      params:
        BlockList: [".*:(latest)$"]
        AllowList: []
      

(Optional) [STEP 6] Include the new check as a kube-linter default built-in check

  • So far, the new check is just a builtin check, and is not a default check yet (i.e. kube-linter won’t run this check by default unless specified)

    • In order to add the new check as a kube-linter default check:

    Edit the internal/defaultchecks/defaultcheck.go file & add the new check name, to look like the following:

      package defaultchecks
    
      import (
        "golang.stackrox.io/kube-linter/internal/set"
      )
    
      var (
        // List is the list of built-in checks that are enabled by default.
        List = set.NewFrozenStringSet(
          ...
          "latest-tag",
          ...
        )
      )
    

Once everything above is done, the next step is to test the the newly added check!

Steps to verify the new check


[STEP 1] Build the kube-linter binary from the local project source code.

The local kube-linter binary will contain the new check latest-tag

  • Run the following command:

    make generated-srcs
    
  • And the output would look something like:

    ❯ make generated-srcs 
    go generate ./...
    Compiling Go source in ./cmd/kube-linter to bin/darwin/kube-linter
    Compiling Go source in ./cmd/kube-linter to bin/linux/kube-linter
    Compiling Go source in ./cmd/kube-linter to bin/windows/kube-linter.exe
    kube-linter templates list --format markdown > docs/generated/templates.md
    kube-linter checks list --format markdown > docs/generated/checks.md
    

    The above command generate local kube-linter binary (for linux, windows & macOS), along with all other auto-generated check templates & markdown documentation required by the kube-linter command line binary tooling to interact with.

[STEP 2] Create an example static yaml manifest

  • As the new check latest-tag (we’re adding as part of this documentation), is defined for a scope of DeploymentLike object, I’m creating an example deployment manifest yaml to do the testing.

  • At the root of the project, create an examples directory & put a deployment.yaml file under it (as our test deployment yaml manifest):

    mkdir examples
    
    cd examples/
    
    vim deployment.yaml
    

    Edit the deployment.yaml file to look like the following:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-deployment
      labels:
        app: nginx
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - name: nginx
            image: example.com/test:v1.0.0
            ports:
            - containerPort: 80
          - name: frontend
            image: quay.io/django:latest
            ports:
            - containerPort: 8000
          - name: key-value-store
            image: docker.io/redis:v1.4.1
            ports:
            - containerPort: 6379
    

[STEP 3] Test the new check with the above example static yaml manifest

  • Execute the following command to run all the kube-linter checks on the test deployment.yaml manifest

    /bin/linux/kube-linter lint examples/deployment.yaml
    
  • In case, you want to run just the newly added latest-tag check, execute the following command:

    /bin/linux/kube-linter lint --do-not-include-builtin-checks --include latest-tag examples/deployment.yaml
    
  • And the output would look something like this:

    ❯ ./bin/linux/kube-linter lint /examples/deployment.yaml 
    KubeLinter 0.2.2-6-gc65dd74013-dirty
    
    examples/deployment.yaml: (object: <no namespace>/nginx-deployment apps/v1, Kind=Deployment) The container "nginx" is using an invalid container image, "example.com/test:v1.0.0". Please use images that satisfies the `AllowList` criteria : ["^(docker.io)"] (check: latest-tag, remediation: Use a container image with a proper image tag satisfying the "AllowList" & "BlockList" regex patterns.)
    
    examples/deployment.yaml: (object: <no namespace>/nginx-deployment apps/v1, Kind=Deployment) The container "frontend" is using an invalid container image, "quay.io/django:latest". Please use images that are not blocked by the `BlockList` criteria : [".*:(latest)$"] (check: latest-tag, remediation: Use a container image with a proper image tag satisfying the "AllowList" & "BlockList" regex patterns.)
    ...
    

[STEP 4] Verify the unit tests & the E2E (end-to-end) tests

  • Run the following command from the root of the project

    make test
      
    make e2e-test
    

[STEP 5] Lint the project

  • Run the following command from the root of the project to lint the project source code

    make lint
    

And finally, when you’re happy with how the new check performs, please raise a PR for the upstream kube-linter project & wait for a review! 🙂