What is KubeLinter?
The KubeLinter is an open-source command-line interface to identify misconfigurations in Kubernetes objects. KubeLinter offers the ability to integrate checks on Kubernetes YAML files and Helm charts before deployment into a Kubernetes cluster. With 19 standard built-in checks and the room to configure your own, you get immediate feedback about misconfigurations and Kubernetes security violations.
How Do I Use It?
KubeLinter requires very minimal configuration and is used by executing commands in your command-line shell. Developed using Go, KubeLinter is comparable to kubectl and made with a few of the same packages.
Microsoft Windows
Unix-like systems
How Do I Install KubeLinter?
Using Brew
If you have Homebrew for macOS or LinuxBrew, you’re in luck. KubeLinter installs with a simple command:
brew install kube-linter
Using the Latest Binary
You can download the latest binary from Releases and add it to your PATH. These binaries allow easy use in container images and CI pipelines.
Using Go
If you prefer using Go, you can use KubeLinter after the following command:
GO111MODULE=on go get golang.stackrox.io/kube-linter/cmd/kube-linter
Building From Source
Lastly, you can always build from source:
- Clone the KubeLinter repository.
- Compile the source code using the makefile.
- Move the binary (located in the .gobin folder).
- Lastly, verify your version to ensure you’ve successfully installed KubeLinter.
.gobin/kube-linter version
Or,
kube-linter version
What Can I Do with KubeLinter
Now that the formalities are out of the way, it’s time to get started.
Type kube-linter
into the command line, and you will see an output that looks remarkably similar to executing kubectl.
Usage:
kube-linter [command]
Available Commands:
checks View more information on lint checks
help Help about any command
lint Lint Kubernetes YAML files and Helm charts
templates View more information on check templates
version Print version and exit
Flags:
-h, --help help for kube-linter
Use "kube-linter [command] --help" for more information about a command.
KubeLinter has five commands:
1. checks
2. help
3. lint
4. templates
5. version
The second and fifth commands are relatively straight forward. The help
command gives further context, and the version
command will provide the current version of the CLI. The kube-linter check list
command will list all of the default policies that come with KubeLinter, and templates will show what you can do with them using custom checks.
Now, don’t jump ahead to custom checks yet. Let’s start by focusing on the core KubeLinter commands.
Linting Your YAMLs
The lint
command requires at least one argument and comes with additional flags. To lint a single file, provide KubeLinter to your Kubernetes yaml file:
kube-linter lint path/to/yaml-file.yaml
Or you can lint an entire directory and its sub contents:
kube-linter lint path/to/directory/containing/yaml-files/
Lastly, for a Helm chart:
kube-linter lint path/to/directory/containing/Chart.yaml-file/
By allowing for a single file, folder, and Helm charts, KubeLinter gives flexibility in how its policies can be used and applied. Extremely useful considering there are many ways that YAML files and Helm charts are stored.
In the kube-linter-walkthrough GitHub repository, the sock-shop application configuration files are waiting to be checked. From here on out, all examples will be from the walkthrough repository.
Applying Policies
To learn about the 19 default checks, go to the KubeLinter documentation or run thekube-linter check list
command for the complete list. Either way, you can review the 19 checks available to you, however, not all 19 checks are a part of the default lint
command.
Name | Enabled by default | Remediation |
---|---|---|
dangling-service |
Yes | Alert on services that don’t have any matching deployments |
default-service-account |
No | Alert on pods that use the default service account |
deprecated-service-account-field |
Yes | Alert on deployments that use the deprecated serviceAccount field |
env-var-secret |
Yes | Alert on objects using a secret in an environment variable, |
mismatching-selector |
Yes | Alert on deployments where the selector doesn’t match the pod template label |
no-anti-affinity |
Yes | Alert on deployments with multiple replicas that don’t specify inter pod anti-affinity to ensure that the orchestrator attempts to schedule replicas on different nodes |
no-extensions-v1beta |
Yes | Alert on objects using deprecated API versions under extensions v1beta |
no-liveness-probe |
No | Alert on containers that don’t specify a liveness probe |
no-read-only-root-fs |
Yes | Alert on containers not running with a read-only root filesystem |
no-readiness-probe |
No | Alert on containers that don’t specify a readiness probe |
non-existent-service-account |
Yes | Alert on pods referencing a service account that isn’t found |
privileged-container |
Yes | Alert on deployments with containers running in privileged mode |
required-annotation-email |
No | Alert on objects without an ‘email’ annotation with a valid email |
required-label-owner |
No | Alert on objects without the ‘owner’ label |
run-as-non-root |
Yes | Alert on containers not set to runAsNonRoot |
ssh-port |
Yes | Alert on deployments exposing port 22, commonly reserved for SSH access |
unset-cpu-requirements |
Yes | Alert on containers without CPU requests and limits set |
unset-memory-requirements |
Yes | Alert on containers without memory requests and limits set |
writable-host-mount |
No | Alert on containers that mount a host path as writable |
As you can see, there are 13 policies enabled by default and six that are not. Viswajith Venugopal commented about the decision around the choice of 13 default policies:
The idea was that it should be effortless to use out-of-the-box without a config file — I didn’t want people running it and feeling overwhelmed with the noise. They could use kube-linter lint --add-all-built-in
to get this behavior (and then rely on the config file or explicit --exclude
args) to remove what they don’t want.
Since KubeLinter applies the 13 checks by default, you will require an extra flag to your argument for more options. There are two main flags to add to the kube-linter lint <filepath>
command. The –add-all-built-in flag will enable all of the 19 checks, and the --do-not-auto-add-defaults
flag will disable all of the checks. You can put these flags to work using the example repo:
Input:
kube-linter lint manifests/compliance-yamls/non-compliant-app.yaml
Output:
Error: found 8 lint errors
And checking the same YAML but using the --add-all-built-in
flag:
Input:
kube-linter lint manifests/compliance-yamls/non-compliant-app.yaml --add-all-built-in
Output:
Error: found 12 lint errors
If you run the command in your terminal, you would see the error output and a detailed explanation of each error. The output will look like the following:
manifests/compliance-yamls/non-compliant-app.yaml: (object: my-namespace/non-compliant apps/v1, Kind=Deployment) container "nginx" has memory limit 0 (check: unset-memory-requirements, remediation: Set your container's memory requests and limits depending on its requirements. See
https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#requests-and-limits
for more details.
)
Each error will detail the object, the error that it has flagged, and the remediation steps. Part of KubeLinter’s goal is to educate users on the path forward for specific issues. Since many security tools expect security knowledge out the gate, the developers felt the giving guidance and extra reading would help educate the beginner/developer who may not have as much Kubernetes expertise.
And of course, you can just as quickly disable them using the --do-not-auto-add-defaults
flag. For example:
Input:
kube-linter lint manifests/compliance-yamls/non-compliant-app.yaml --do-not-auto-add-defaults
Output:
Warning: no checks enabled.
I’m sure you’re thinking, “Why would I disable all policies? This defeats the purpose, doesn’t it?” and you would be right! That is where the --include
and --exclude
flags come in.
Of all the checks to run, the non-compliant-app violates ten of them (12 errors since CPU and memory have a limit and request):
- env-var-secret
- no-liveness-probe
- no-read-only-root-fs
- no-readiness-probe
- non-existent-service-account
- required-annotation-email
- required-label-owner
- run-as-non-root
- unset-cpu-requirements
- unset-memory-requirements
However, for now, you only care about the run-as-non-root
check. In that case, you can disable all of the other checks and include only the checks you care about.
Input:
kube-linter lint manifests/compliance-yamls/non-compliant-app.yaml --do-not-auto-add-defaults --include run-as-non-root
Output:
Error: found 1 lint errors
Of course, you can always enable all checks and disable specific ones as well. For a single developer, using the CLI in this way is useful. However, CLI commands are imperative and can change between developers. The issues compound as teams grow and admins manage policies across teams. To enable policy adoptions at scale, KubeLinter needs to be repeatable, flexible, and portable, where the config file comes in.
Configuring Policies
One of the seven flags available for the kube-linter lint command is the --config
flag.
KubeLinter can use a configuration YAML file to manage checks declaratively. Let’s start with a couple of basic config files. In the kube-linter-walkthrough repository, there are a few config files that highlight the flexibility of KubeLinter.
The config_addAllBuiltIn.yaml
is setting all the built-in checks set to true. And vice-versa, the config_doNotAutoAddDefaults.yaml
removes all of the checks. If you point the CLI command to these files, you will get the same result using the previous section’s flags.
Input:
kube-linter lint manifests/compliance-yamls/non-compliant-app.yaml --config configs/config_addAllBuiltIn.yaml
Output:
Error: found 12 lint errors
Input:
kube-linter lint manifests/compliance-yamls/non-compliant-app.yaml --config configs/config_doNotAutoAddDefaults.yaml
Output:
Error: found 0 lint errors
And just like the --include
and --exclude
flags, you can set the policies you wish to include or exclude. With the addAllBuiltIn
YAML, you can exclude any check and do the opposite in the “do not add all config”, allowing you to use any mix of the 19 policies available.
The config file allows flexibility and repeatability in how policies are applied. Config files can be used in different pipeline stages, enabling new user awareness of the default policies before enforcement before production. With that in mind, the real flexibility of KubeLinter is in the ability to create custom checks and ignore specific cases.
Using Kubernetes Annotations for Ignoring Specific Cases
As you move to create policy enforcement across teams and users, there will be specific use cases that will require an exception. Exceptions need documentation, and what better way to use the declarative nature of Kubernetes than to make your code the documentation.
Kubernetes annotations allow for the injection of metadata into Kubernetes objects that can be used by external tools but not used by the Kubernetes API server. The application of annotations varies, from simple use-cases such as noting contact information to more complicated cases like side-car auto-injection in a namespace.
The core use of annotations is to allow users to give more context to their Kubernetes Objects. Using annotations to declare the specific exceptions, you can promote application knowledge communication throughout your pipeline since the developers or DevOps engineers know best about what each container requires.
For example, in the case below, the cart database requires root access on the host. Usually, this would be flagged by KubeLinter. However, for this demonstration, the database will be allowed to write to the host. For this example, the command points to a config_db.yaml
that only has the run-as-non-root
check enabled.
kube-linter lint manifests/carts-db/carts-db-dep.yaml --config configs/config_db.yaml
… Error: found 1 lint errors
Now, it’s time to remove the last lint error. To ensure KubeLinter ignores any checks, you need to add a specific annotation.
ignore-check.kube-linter.io/<check-name>
And since you want to ignore the run-as-non-root check, simply add that check name to the annotation.
ignore-check.kube-linter.io/run-as-non-root: ""
Lastly, this annotation allows for the addition of a description to be associated with the key. It is highly recommended that there is a detailed description if any check is ignored.
ignore-check.kube-linter.io/run-as-non-root: "this deployment needs privileged access to the host"
After adding the annotation, you can rerun the command and watch the changes.
kube-linter lint manifests/carts-db/carts-db-dep.yaml --config configs/config_db.yaml
… No lint errors found!
Customizing the Default Checks
Up to this point, you have worked with the 19 checks, enabled and disabled them, created config files to repeat the checks, and used annotations to ignore specific checks.
Another piece of KubeLinter is the personalization of the default checks. KubeLinter checks are built off templates, and you can adjust the parameters of these templates to suit your needs. Below are the templates available and the parameters that can be altered.
Template | Parameter |
---|---|
dangling-service |
{} |
service-account |
{"serviceAccount":"^(|default)$"} |
deprecated-service-account-field |
{} |
verify-container-capabilities |
{"forbiddenCapabilities":["NET_RAW"]} |
env-var |
{"name":"(?i). secret. ","valu_":".+"} |
mismatching-selector |
{} |
anti-affinity |
`{“minReplicas”:2}` |
disallowed-api-obj |
`{“group”:“extensions”,“version”:“v1beta.+"}` |
liveness-probe |
{} |
read-only-root-fs |
{} |
readiness-probe |
{} |
non-existent-service-account |
{} |
privileged |
{} |
required-annotation |
`{“key”:“email”,“value”:"[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+"}` |
required-label |
`required-label` |
run-as-non-root |
{} |
ports |
`{“port”:22,“protocol”:“TCP”}` |
cpu-requirements |
`{“lowerBoundMillis”:0,“requirementsType”:“any”,“upperBoundMillis”:0}` |
memory-requirements |
`{“lowerBoundMB”:0,“requirementsType”:“any”,“upperBoundMB”:0}` |
For example, the required-annotation
check contains two alterable parameters, the key
and value
parameters. In the config_customChecks.yaml
file, there are two custom checks as examples demonstrating this ability:
customChecks:
- name: required-annotation-responsible
template: required-annotation
params:
key: kube-linter/annotation
remediation: please add the "kube-linter/annotation" annotation to the deployment
- name: required-label-release
template: required-label
params:
key: team
remediation: please add a team label to the service
scope:
objectKinds:
- Service
With particular templates, you can change the Kubernetes object you are applying the check to. For example, with the required-label-release
check, you can change the scope to a service
where it would default to a deployment
. Putting this check into action, if you run:
kube-linter lint manifests/carts-db/carts-db-dep.yaml --config configs/config_customChecks.yaml
You will get the error message that matches your custom rule.
manifests/carts-db/carts-db-dep.yaml: (object: sock-shop/carts-db apps/v1, Kind=Deployment)
no annotation matching "kube-linter/demo=<any>" found (check: required-annotation-responsible, remediation: please add the "kube-linter/demo" annotation to the deployment)
Error: found 1 lint errors
Lastly, the remediation value can be altered for your needs. It enables pointed feedback to developers or users of the configuration file. Useful to help users get accustomed to a team or organizational best practices.
Using KubeLinter in a Pipeline
KubeLinter can be integrated into a new or existing pipeline, and the Go binary provides a simple download, install, and execution process. The strength of KubeLinter in these pipelines is implementing the configuration files and CLI commands to customize the checks for different YAML files.
In the configs directory, there are two configurations to illustrate this. The config_carts.yaml
is configured for the /manifests/carts/
yaml files, and the config_carts-db.yaml
is for the /manifests/carts-db/
yaml files. Both configuration files have the same custom checks, however, the application of the default checks is different. This example illustrates how you can enforce team, project, or organization defaults while simultaneously customizing for a specific use case.
You can simulate the example GitHub Action by running simultaneous commands:
1. kube-linter lint manifests/carts-db/ --config configs/config_db_carts.yaml
2. kube-linter lint manifests/orders-db/ --config configs/config_db_orders.yaml
The previous command will output different errors due to the difference in config files. However, you can use the flexibility of these pipeline for greater control.
By default, most pipelines will fail if there is a non-zero exit code. So, if both commands are executed, the second command’s output will not be seen unless the first command passes. To get around this, break the two commands into separate steps and create multiple workflow stages.
For example, the demo repository contains two similar jobs in a single GitHub action. The first is the “test” job. This job will continue in the event of an error. The second job is a “staging” job that will stop in the event of a non-zero exit code. You could use this to apply the checks to the development branches without breaking pipelines, allowing developers to learn about violations without breaking their pipelines. The pipeline will then exit and return an error if those issues are not fixed before the staging job.
There are many different configurations available to you depending on the CI tool you are using. The main takeaway is the flexibility that KubeLinter gives you in applying these security checks.
What’s Next
As the community continues to grow, the KubeLinter development team wants to hear from you.
- What new checks do you want to see?
- Do you want to see templates applied to other objectKinds?
- Do you want more example configuration files?
The team would love to hear from you! Make sure that you:
- Star and watch the KubeLinter public repository.
- Join KubeLinter on slack to get quicker feedback.
- Open an issue or request and let your feedback be known.
If you are interested in other writeups, take a look at Steven Vaughan-Nichols write-up in The New Stack or Jessica Cherry’s thoughts at Opensource.
Thanks for reading, and stay safe.
About the author
Browse by channel
Automation
The latest on IT automation for tech, teams, and environments
Artificial intelligence
Updates on the platforms that free customers to run AI workloads anywhere
Open hybrid cloud
Explore how we build a more flexible future with hybrid cloud
Security
The latest on how we reduce risks across environments and technologies
Edge computing
Updates on the platforms that simplify operations at the edge
Infrastructure
The latest on the world’s leading enterprise Linux platform
Applications
Inside our solutions to the toughest application challenges
Original shows
Entertaining stories from the makers and leaders in enterprise tech
Products
- Red Hat Enterprise Linux
- Red Hat OpenShift
- Red Hat Ansible Automation Platform
- Cloud services
- See all products
Tools
- Training and certification
- My account
- Customer support
- Developer resources
- Find a partner
- Red Hat Ecosystem Catalog
- Red Hat value calculator
- Documentation
Try, buy, & sell
Communicate
About Red Hat
We’re the world’s leading provider of enterprise open source solutions—including Linux, cloud, container, and Kubernetes. We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.
Select a language
Red Hat legal and privacy links
- About Red Hat
- Jobs
- Events
- Locations
- Contact Red Hat
- Red Hat Blog
- Inclusion at Red Hat
- Cool Stuff Store
- Red Hat Summit