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-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/
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.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: []
- 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.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 ofDeploymentLike
object, I’m creating an example deployment manifest yaml to do the testing. -
At the root of the project, create an
examples
directory & put adeployment.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! 🙂