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.
This document uses the static check latest-tag, as an example to lay down the steps written below.
The
latest-tagvalidation 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/foldercd 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.gofile 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.gofile.
- 
        template_test.go And finally, the unit tests to validate the new check logic will be written in template_test.gofile
 
- 
        
[STEP 4] Make the new check visible to kube-linter
- 
    From the root of the project, modify the pkg/templates/all/all.gofile to include the path to our new test, i.e.pkg/templates/latesttagvim 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/yamlsfolder & 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.yamlfile does the following:- provide self-explanatory string values for name,description&remediationfields
- 
        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: []
 
- provide self-explanatory string values for 
(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.gofile & 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.mdThe 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 ofDeploymentLikeobject, I’m creating an example deployment manifest yaml to do the testing.
- 
    At the root of the project, create an examplesdirectory & put adeployment.yamlfile under it (as our test deployment yaml manifest):mkdir examples cd examples/ vim deployment.yamlEdit the deployment.yamlfile 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.yamlmanifest/bin/linux/kube-linter lint examples/deployment.yaml
- 
    In case, you want to run just the newly added latest-tagcheck, 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! 🙂