├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yaml ├── release.yml └── workflows │ ├── auto-merge.yaml │ ├── build.yaml │ └── release.yaml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yaml ├── .pre-commit-hooks.yaml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── RELEASE.md ├── SECURITY.md ├── cmd └── kube-linter │ └── kube-linter.go ├── config.yaml.example ├── docs ├── .nojekyll ├── CNAME ├── README.md ├── _coverpage.md ├── _navbar.md ├── _sidebar.md ├── configuring-kubelinter.md ├── custom_resource_template_test.go ├── generated │ ├── checks.md │ └── templates.md ├── index.html ├── style.css └── using-kubelinter.md ├── e2etests ├── bats-support-clone.bash ├── bats-tests.sh ├── check-bats-tests.sh ├── empty.go ├── sanity_test.go └── testdata │ ├── all-built-in-config.yaml │ └── forbidden-annotation-config.yaml ├── go.mod ├── go.sum ├── image ├── Dockerfile ├── Dockerfile_alpine └── bin │ └── .gitignore ├── images └── logo │ ├── KubeLinter-horizontal.svg │ ├── KubeLinter-vertical.svg │ └── favicon.ico ├── internal ├── consts │ └── consts.go ├── defaultchecks │ ├── default_checks.go │ └── default_test.go ├── errorhelpers │ └── format.go ├── flagutil │ └── enum.go ├── pointers │ └── pointers.go ├── set │ └── gen-string-generic.go ├── stringutils │ ├── consume.go │ ├── default.go │ ├── repeat.go │ ├── split.go │ └── ternary.go ├── utils │ ├── ignore_error.go │ └── must.go └── version │ └── version.go ├── kubelinter-cosign.pub ├── pkg ├── builtinchecks │ ├── built_in_checks.go │ ├── built_in_checks_test.go │ └── yamls │ │ ├── access-to-create-pods.yaml │ │ ├── access-to-secrets.yaml │ │ ├── cluster-admin-role-binding.yaml │ │ ├── dangling-horizontalpodautoscaler.yaml │ │ ├── dangling-ingress.yaml │ │ ├── dangling-networkpolicy.yaml │ │ ├── dangling-networkpolicypeer-podselector.yaml │ │ ├── dangling-service.yaml │ │ ├── dangling-servicemonitor.yaml │ │ ├── default-service-account.yaml │ │ ├── deprecated-service-account.yaml │ │ ├── dnsconfig-options.yaml │ │ ├── docker-sock.yaml │ │ ├── drop-net-raw-capability.yaml │ │ ├── duplicate-env-var.yaml │ │ ├── env-var-secret.yaml │ │ ├── host-mounts.yaml │ │ ├── hostipc.yaml │ │ ├── hostnetwork.yaml │ │ ├── hostpid.yaml │ │ ├── hpa-minimum-replicas.yaml │ │ ├── invalid-target-ports.yaml │ │ ├── latest-tag.yaml │ │ ├── liveness-port.yaml │ │ ├── minimum-replicas.yaml │ │ ├── mismatching-selector.yaml │ │ ├── no-anti-affinity.yaml │ │ ├── no-extensions-v1beta.yaml │ │ ├── no-liveness-probe.yaml │ │ ├── no-node-affinity.yaml │ │ ├── no-readiness-probe.yaml │ │ ├── no-rolling-update-strategy.yaml │ │ ├── non-existent-service-account.yaml │ │ ├── non-isolated-pod.yaml │ │ ├── pdb-unhealthy-pod-eviction-policy.yaml │ │ ├── pdbs-max-unavailable.yaml │ │ ├── pdbs-min-available.yaml │ │ ├── privilege-escalation.yaml │ │ ├── privileged.yaml │ │ ├── privilegedports.yaml │ │ ├── read-only-root-fs.yaml │ │ ├── read-secret-from-env-var.yaml │ │ ├── readiness-port.yaml │ │ ├── required-annotation-email.yaml │ │ ├── required-label-owner.yaml │ │ ├── restart-policy.yaml │ │ ├── run-as-non-root.yaml │ │ ├── scc-deny-privileged-container.yaml │ │ ├── servicetype.yaml │ │ ├── ssh-port.yaml │ │ ├── startup-port.yaml │ │ ├── sysctls.yaml │ │ ├── unsafe-proc-mount.yaml │ │ ├── unset-cpu-requirements.yaml │ │ ├── unset-memory-requirements.yaml │ │ ├── usenamespace.yaml │ │ ├── wildcard-use-in-rules.yaml │ │ └── writable-host-mount.yaml ├── check │ ├── parameter_desc.go │ └── template.go ├── checkregistry │ └── check_registry.go ├── command │ ├── checks │ │ └── command.go │ ├── common │ │ ├── format_wrapper.go │ │ ├── template.go │ │ └── template_test.go │ ├── lint │ │ ├── command.go │ │ ├── command_test.go │ │ ├── sarif_format.go │ │ └── testdata │ │ │ ├── invalid-pod-resources.yaml │ │ │ ├── invalid-pvc-resources.yaml │ │ │ └── valid-pod.yaml │ ├── root │ │ ├── command.go │ │ └── command_test.go │ ├── templates │ │ └── command.go │ └── version │ │ └── command.go ├── config │ ├── check.go │ ├── codegen │ │ └── parse.go │ ├── config.go │ ├── flags.go │ └── gen.go ├── configresolver │ ├── config_resolver.go │ └── config_resolver_test.go ├── diagnostic │ └── diagnostic.go ├── extract │ ├── customtypes │ │ └── pod_spec.go │ ├── gvk.go │ ├── hpa_spec.go │ ├── metadata.go │ ├── pod_spec.go │ ├── scc_spec.go │ └── update_strategy.go ├── ignore │ ├── ignore.go │ └── ignore_test.go ├── instantiatedcheck │ └── instantiated_check.go ├── k8sutil │ └── object.go ├── lintcontext │ ├── context.go │ ├── create_contexts.go │ ├── create_contexts_test.go │ ├── mocks │ │ ├── clusterrole.go │ │ ├── clusterrolebinding.go │ │ ├── container.go │ │ ├── context.go │ │ ├── horizontalpodautoscaler.go │ │ ├── ingress.go │ │ ├── networkpolicy.go │ │ ├── pod.go │ │ ├── role.go │ │ ├── rolebinding.go │ │ ├── scaledobject.go │ │ ├── scc.go │ │ ├── service.go │ │ └── servicemonitor.go │ └── parse_yaml.go ├── matcher │ └── string.go ├── objectkinds │ ├── any.go │ ├── clusterrole.go │ ├── clusterrolebinding.go │ ├── deployment_like.go │ ├── horizontalpodautoscaler.go │ ├── ingress.go │ ├── networkpolicy.go │ ├── poddisruptionbudget.go │ ├── registry.go │ ├── role.go │ ├── rolebinding.go │ ├── scaledobject.go │ ├── securitycontext.go │ ├── service.go │ ├── serviceMonitor.go │ ├── serviceaccount.go │ └── types.go ├── pathutil │ └── path.go ├── run │ └── run.go └── templates │ ├── accesstoresources │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── all │ ├── all.go │ └── all_test.go │ ├── antiaffinity │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── clusteradminrolebinding │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── codegen │ └── main.go │ ├── containercapabilities │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── cpurequirements │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── danglinghpa │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── danglingingress │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── danglingnetworkpolicy │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── danglingnetworkpolicypeer │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── danglingservice │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── danglingservicemonitor │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── deprecatedserviceaccount │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── disallowedgvk │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── dnsconfigoptions │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── duplicatenvvar │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── envvar │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── forbiddenannotation │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── gen.go │ ├── hostipc │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── hostmounts │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── hostnetwork │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── hostpid │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── hpareplicas │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── imagepullpolicy │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── latesttag │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── livenessport │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── livenessprobe │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── memoryrequirements │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── mismatchingselector │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── namespace │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── nodeaffinity │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── nonexistentserviceaccount │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── nonisolatedpod │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── pdbmaxunavailable │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── pdbminavailable │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── pdbunhealthypodevictionpolicy │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── ports │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── privileged │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── privilegedports │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── privilegeescalation │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── readinessport │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── readinessprobe │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── readonlyrootfs │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── readsecret │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── registry.go │ ├── replicas │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── requiredannotation │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── requiredlabel │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── restartpolicy │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── runasnonroot │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── sccdenypriv │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── serviceaccount │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── servicetype │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── startupport │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── sysctl │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── targetport │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── templates_testutils.go │ ├── unsafeprocmount │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ ├── updateconfig │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ ├── template.go │ └── template_test.go │ ├── util │ ├── check_probe_port.go │ ├── forbidden_matcher.go │ ├── forbiden_matcher_test.go │ ├── json.go │ ├── map_structure.go │ ├── per_container_check.go │ ├── required_matcher.go │ ├── required_matcher_test.go │ ├── value_in_range.go │ └── value_in_range_test.go │ ├── wildcardinrules │ ├── internal │ │ └── params │ │ │ ├── gen-params.go │ │ │ └── params.go │ └── template.go │ └── writablehostmount │ ├── internal │ └── params │ │ ├── gen-params.go │ │ └── params.go │ └── template.go ├── scripts └── sarif │ └── sarif-schema-2.1.0.json ├── tests ├── checks │ ├── access-to-create-pods.yml │ ├── access-to-secrets.yml │ ├── cluster-admin-role-binding.yml │ ├── dangling-hpa.yml │ ├── dangling-ingress.yml │ ├── dangling-networkpolicy.yml │ ├── dangling-networkpolicypeer-podselector.yml │ ├── dangling-service.yml │ ├── dangling-servicemonitor.yml │ ├── default-service-account.yml │ ├── deprecated-service-account-field.yml │ ├── dnsconfig-options-ndots.yml │ ├── docker-sock.yml │ ├── drop-net-raw-capability.yml │ ├── duplicate-env-var.yaml │ ├── env-var-secret.yml │ ├── exposed-services.yml │ ├── forbidden-annotation.yml │ ├── host-ipc.yml │ ├── host-network.yml │ ├── host-pid.yml │ ├── hpa-minimum-three-replicas.yml │ ├── invalid-target-ports.yaml │ ├── latest-tag.yml │ ├── liveness-port.yml │ ├── minimum-three-replicas.yml │ ├── mismatching-selector.yml │ ├── no-anti-affinity.yml │ ├── no-extensions-v1beta.yml │ ├── no-liveness-probe.yml │ ├── no-node-affinity.yml │ ├── no-read-only-root-fs.yml │ ├── no-readiness-probe.yml │ ├── no-rolling-update-strategy.yml │ ├── non-existent-service-account.yml │ ├── non-isolated-pod.yml │ ├── pdb-max-unavailable.yaml │ ├── pdb-min-available.yaml │ ├── pdb-unhealthy-pod-eviction-policy.yaml │ ├── privilege-escalation-container.yml │ ├── privileged-container.yml │ ├── privileged-ports.yml │ ├── read-secret-from-env-var.yml │ ├── readiness-port.yml │ ├── required-annotation-email.yml │ ├── required-label-owner.yml │ ├── restart-policy.yaml │ ├── run-as-non-root.yml │ ├── scc-deny-privileged-container.yml │ ├── sensitive-host-mounts.yml │ ├── ssh-port.yml │ ├── startup-port.yml │ ├── unsafe-proc-mount.yml │ ├── unsafe-sysctls.yml │ ├── unset-cpu-requirements.yml │ ├── unset-memory-requirements.yml │ ├── use-namespace.yml │ ├── wildcard-in-rules.yml │ └── writable-host-mount.yml └── testdata │ ├── mychart-0.1.0.tgz │ ├── mychart │ ├── .helmignore │ ├── Chart.lock │ ├── Chart.yaml │ ├── charts │ │ └── subchart-0.1.0.tgz │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── deployment.yaml │ │ ├── hpa.yaml │ │ ├── ingress.yaml │ │ ├── service.yaml │ │ ├── serviceaccount.yaml │ │ └── tests │ │ │ └── test-connection.yaml │ └── values.yaml │ ├── splunk.yaml │ └── subchart │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ └── deployment.yaml │ └── values.yaml └── tool-imports ├── empty.go ├── go.mod ├── go.sum └── tools.go /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @janisz 2 | * @rhybrillou 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **System info:** 11 | - OS: [e.g. Linux? MaxOS? Windows?] 12 | 13 | **Describe the bug** 14 | A clear and concise description of the bug. 15 | 16 | **To Reproduce** 17 | Steps to reproduce the behavior: 18 | 19 | **Sample YAML input** 20 | If applicable, sample YAML input which reproduces the issue. 21 | 22 | **Expected behavior** 23 | A clear and concise description of what you expected to happen. 24 | 25 | **Screenshots** 26 | If applicable, add screenshots to help explain your problem. 27 | 28 | **Additional context** 29 | Add any other context about the problem here. 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE_REQUEST]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Description of the problem/feature request** 11 | A clear and concise description of the problem, or the proposed new feature. 12 | 13 | **Description of the existing behavior vs. expected behavior** 14 | If applicable, please paste in the existing KubeLinter output along with the input used, and point out which part should be modified (expected output). 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | open-pull-requests-limit: 3 6 | reviewers: 7 | - "janisz" 8 | schedule: 9 | interval: 'weekly' 10 | day: 'wednesday' 11 | - package-ecosystem: 'gomod' 12 | directory: '/' 13 | schedule: 14 | interval: 'weekly' 15 | day: 'wednesday' 16 | open-pull-requests-limit: 3 17 | reviewers: 18 | - "janisz" 19 | groups: 20 | k8s.io: 21 | patterns: 22 | - "k8s.io/*" 23 | - package-ecosystem: 'gomod' 24 | directory: 'tool-imports' 25 | schedule: 26 | interval: 'weekly' 27 | day: 'wednesday' 28 | open-pull-requests-limit: 3 29 | reviewers: 30 | - "janisz" 31 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | categories: 3 | - title: "✔️ New checks" 4 | labels: 5 | - "new-check" 6 | - title: "🚀 Features" 7 | labels: 8 | - "feature" 9 | - "enhancement" 10 | - title: "🐛 Bug Fixes" 11 | labels: 12 | - "fix" 13 | - "bugfix" 14 | - "bug" 15 | - title: "🧰 Maintenance" 16 | labels: 17 | - "chore" 18 | - title: Other Changes 19 | labels: 20 | - '*' 21 | - title: '⬆️ Dependencies' 22 | labels: 23 | - 'dependencies' 24 | -------------------------------------------------------------------------------- /.github/workflows/auto-merge.yaml: -------------------------------------------------------------------------------- 1 | name: auto-merge 2 | 3 | on: 4 | pull_request_target: 5 | 6 | jobs: 7 | auto-merge: 8 | runs-on: ubuntu-latest 9 | if: github.actor == 'dependabot[bot]' 10 | steps: 11 | - uses: ahmadnassri/action-dependabot-auto-merge@v2.6 12 | with: 13 | github-token: '${{ secrets.RHACS_BOT_GITHUB_TOKEN }}' 14 | command: "squash and merge" 15 | approve: true 16 | target: minor 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Settings files for JetBrains IDEs 2 | /.idea 3 | 4 | # Vim swap files 5 | *.swp 6 | 7 | # Project-specific $GOBIN 8 | /.gobin 9 | 10 | # Empty file touched by `make deps` 11 | /deps 12 | 13 | e2etests/test_helper 14 | dist/ 15 | coverage.out 16 | -------------------------------------------------------------------------------- /.pre-commit-hooks.yaml: -------------------------------------------------------------------------------- 1 | - id: kube-linter 2 | name: KubeLinter 3 | description: This hook installs (using Go) and runs the KubeLinter utility to lint Helm charts and Kubernetes YAML files. 4 | entry: kube-linter lint 5 | language: golang 6 | types: [yaml] 7 | 8 | - id: kube-linter-system 9 | name: KubeLinter System 10 | description: This hook runs the KubeLinter utility that exists already on the system to lint Helm charts and Kubernetes YAML files. 11 | entry: kube-linter lint 12 | language: system 13 | types: [yaml] 14 | 15 | - id: kube-linter-docker 16 | name: KubeLinter Docker 17 | description: This hook runs kube-linter using the project's official docker image 18 | language: docker_image 19 | types: [yaml] 20 | entry: stackrox/kube-linter:v0.6.4 lint 21 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to KubeLinter 2 | 3 | Thank you for your interest in contributing to KubeLinter! 4 | 5 | ### Code Contributions 6 | 7 | We welcome code contributions from the community. Anyone is welcome to create a pull request, 8 | and request a review from any of the project maintainers. 9 | 10 | By default, your pull requests should be linked to an [issue](https://github.com/stackrox/kube-linter/issues). 11 | If you're addressing an existing issue, great! If your change is unrelated to an existing issue, 12 | please file an issue first, and make sure you get buy-in from the maintainers. 13 | 14 | However, if your change is relatively trivial (say, a documentation update, or a simple bugfix), 15 | feel free to directly create a pull request, and explain your changes in the pull request. 16 | 17 | ### Feature Requests and Bug Reports 18 | 19 | If you find a bug, or have a request for a feature, 20 | please [create a GitHub issue](https://github.com/stackrox/kube-linter/issues/new/choose). 21 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting a vulnerability 2 | 3 | If you've found a security issue that you'd like to disclose confidentially please contact [Red Hat's Product Security team](https://access.redhat.com/security/team/contact). 4 | -------------------------------------------------------------------------------- /cmd/kube-linter/kube-linter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "golang.stackrox.io/kube-linter/pkg/command/root" 8 | // Register templates 9 | _ "golang.stackrox.io/kube-linter/pkg/templates/all" 10 | ) 11 | 12 | func main() { 13 | c := root.Command() 14 | if err := c.Execute(); err != nil { 15 | fmt.Fprintf(os.Stderr, "Error: %v\n", err) 16 | os.Exit(1) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /config.yaml.example: -------------------------------------------------------------------------------- 1 | # customChecks defines custom checks. 2 | customChecks: 3 | - name: "required-label-app" 4 | template: "required-label" 5 | params: 6 | key: "app" 7 | checks: 8 | # if doNotAutoAddDefaults is true, default checks are not automatically added. 9 | doNotAutoAddDefaults: false 10 | 11 | # addAllBuiltIn, if set, adds all built-in checks. This allows users to 12 | # explicitly opt-out of checks that are not relevant using Exclude. 13 | # Takes precedence over doNotAutoAddDefaults, if both are set. 14 | addAllBuiltIn: false 15 | 16 | # include explicitly adds checks, by name. You can reference any of the built-in checks. 17 | # Note that customChecks defined above are included automatically. 18 | include: 19 | - "required-label-owner" 20 | # exclude explicitly excludes checks, by name. exclude has the highest priority: if a check is 21 | # in exclude, then it is not considered, even if it is in include as well. 22 | exclude: 23 | - "privileged" 24 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackrox/kube-linter/e3a413f1435f6c1b4debd181c794ca0706e42660/docs/.nojekyll -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | docs.kubelinter.io -------------------------------------------------------------------------------- /docs/_coverpage.md: -------------------------------------------------------------------------------- 1 | ![logo](https://raw.githubusercontent.com/stackrox/kube-linter/main/images/logo/KubeLinter-horizontal.svg) 2 | 3 | > Static analysis for Kubernetes YAML files and Helm charts. 4 | 5 |
Made with
by StackRox
6 | 7 | [GitHub](https://github.com/stackrox/kube-linter) 8 | [Get Started](README) -------------------------------------------------------------------------------- /docs/_navbar.md: -------------------------------------------------------------------------------- 1 | [![GitHub Repo stars](https://img.shields.io/github/stars/stackrox/kube-linter?label=Give%20us%20a%20star%20on%20GitHub&style=social)](https://github.com/stackrox/kube-linter) -------------------------------------------------------------------------------- /docs/_sidebar.md: -------------------------------------------------------------------------------- 1 | * [Introduction](/) 2 | * [Using KubeLinter](/using-kubelinter.md) 3 | * [Configuring KubeLinter](/configuring-kubelinter.md) 4 | * [KubeLinter checks](/generated/checks.md) 5 | * [KubeLinter templates](/generated/templates.md) -------------------------------------------------------------------------------- /e2etests/bats-support-clone.bash: -------------------------------------------------------------------------------- 1 | if [[ ! -d "e2etests/test_helper/bats-support" ]]; then 2 | # Download bats-support dynamically so it doesnt need to be added into source 3 | git clone https://github.com/ztombol/bats-support e2etests/test_helper/bats-support --depth 1 4 | fi 5 | 6 | if [[ ! -d "e2etests/test_helper/redhatcop-bats-library" ]]; then 7 | # Download redhat-cop/bats-library dynamically so it doesnt need to be added into source 8 | git clone https://github.com/redhat-cop/bats-library e2etests/test_helper/redhatcop-bats-library --depth 1 9 | fi -------------------------------------------------------------------------------- /e2etests/check-bats-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | run() { 4 | local tmp_write_dir=/tmp/kubelinter/$(date +'%d-%m-%Y-%H-%M') 5 | mkdir -p "${tmp_write_dir}" 6 | 7 | grep "@test" e2etests/bats-tests.sh | grep -v 'flag-' | grep -v 'template-' | cut -d'"' -f2 > ${tmp_write_dir}/batstests.log 8 | ${KUBE_LINTER_BIN:-kube-linter} checks list --format json | jq -r '.[].name' > ${tmp_write_dir}/kubelinterchecks.log 9 | diff -c ${tmp_write_dir}/kubelinterchecks.log ${tmp_write_dir}/batstests.log || { echo >&2 "ERROR: The output of '${KUBE_LINTER_BIN} checks list' differs from the tests in 'e2etests/bats-tests.sh'. See above diff."; exit 1; } 10 | } 11 | 12 | run 13 | -------------------------------------------------------------------------------- /e2etests/empty.go: -------------------------------------------------------------------------------- 1 | package e2etests 2 | 3 | // Empty file with no build tag to keep the Go compiler happy. 4 | -------------------------------------------------------------------------------- /e2etests/testdata/all-built-in-config.yaml: -------------------------------------------------------------------------------- 1 | checks: 2 | addAllBuiltIn: true 3 | -------------------------------------------------------------------------------- /e2etests/testdata/forbidden-annotation-config.yaml: -------------------------------------------------------------------------------- 1 | checks: 2 | addAllBuiltIn: false 3 | customChecks: 4 | - name: "forbid-annotation-reloader-stakater-auto" 5 | description: "" 6 | remediation: "Remove reloader.stakater.com/auto annotation" 7 | scope: 8 | objectKinds: 9 | - DeploymentLike 10 | template: "forbidden-annotation" 11 | params: 12 | key: "reloader.stakater.com/auto" 13 | value: 'true' 14 | - name: invalid-irsa-role 15 | description: "IRSA annotations must have a valid IAM Role ARN value" 16 | remediation: "Validate the format of the annotation's value to ensure it is a valid IAM Role ARN" 17 | scope: 18 | objectKinds: 19 | - ServiceAccount 20 | template: "forbidden-annotation" 21 | params: 22 | key: "eks.amazonaws.com/role-arn" 23 | value: | 24 | !arn:aws:iam::\d{12}:role\/[\w+=,.@-]{1,64}$ -------------------------------------------------------------------------------- /image/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | 3 | COPY kube-linter / 4 | 5 | ENTRYPOINT ["/kube-linter"] 6 | -------------------------------------------------------------------------------- /image/Dockerfile_alpine: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | COPY kube-linter / 4 | 5 | ENTRYPOINT ["/kube-linter"] 6 | -------------------------------------------------------------------------------- /image/bin/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /images/logo/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackrox/kube-linter/e3a413f1435f6c1b4debd181c794ca0706e42660/images/logo/favicon.ico -------------------------------------------------------------------------------- /internal/consts/consts.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | const ( 4 | // ProgramName is for displaying help, etc. 5 | ProgramName = "kube-linter" 6 | // MainURL is where the project info can be found. 7 | MainURL = "https://github.com/stackrox/kube-linter" 8 | // TemplateURLFormat when formatted with template id, provides help link for the given template. 9 | TemplateURLFormat = "https://docs.kubelinter.io/#/generated/templates?id=%s" 10 | ) 11 | -------------------------------------------------------------------------------- /internal/defaultchecks/default_checks.go: -------------------------------------------------------------------------------- 1 | package defaultchecks 2 | 3 | import ( 4 | "golang.stackrox.io/kube-linter/internal/set" 5 | ) 6 | 7 | var ( 8 | // List is the list of built-in checks that are enabled by default. 9 | List = set.NewFrozenStringSet( 10 | "dangling-service", 11 | "deprecated-service-account-field", 12 | "docker-sock", 13 | "drop-net-raw-capability", 14 | "duplicate-env-var", 15 | "env-var-secret", 16 | "host-ipc", 17 | "host-network", 18 | "host-pid", 19 | "invalid-target-ports", 20 | "latest-tag", 21 | "liveness-port", 22 | "mismatching-selector", 23 | "no-anti-affinity", 24 | "no-extensions-v1beta", 25 | "no-read-only-root-fs", 26 | "non-existent-service-account", 27 | "pdb-max-unavailable", 28 | "pdb-min-available", 29 | "privilege-escalation-container", 30 | "privileged-container", 31 | "readiness-port", 32 | "run-as-non-root", 33 | "sensitive-host-mounts", 34 | "ssh-port", 35 | "startup-port", 36 | "unsafe-sysctls", 37 | "unset-cpu-requirements", 38 | "unset-memory-requirements", 39 | "pdb-unhealthy-pod-eviction-policy", 40 | ) 41 | ) 42 | -------------------------------------------------------------------------------- /internal/defaultchecks/default_test.go: -------------------------------------------------------------------------------- 1 | package defaultchecks 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "github.com/stretchr/testify/require" 8 | "golang.stackrox.io/kube-linter/internal/set" 9 | "golang.stackrox.io/kube-linter/pkg/builtinchecks" 10 | ) 11 | 12 | func TestListReferencesOnlyValidChecks(t *testing.T) { 13 | allChecks, err := builtinchecks.List() 14 | require.NoError(t, err) 15 | allCheckNames := set.NewStringSet() 16 | for _, check := range allChecks { 17 | allCheckNames.Add(check.Name) 18 | } 19 | for _, defaultCheck := range List.AsSlice() { 20 | assert.True(t, allCheckNames.Contains(defaultCheck), "default check %s invalid", defaultCheck) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /internal/pointers/pointers.go: -------------------------------------------------------------------------------- 1 | package pointers 2 | 3 | // Bool returns a pointer to a bool. 4 | func Bool(b bool) *bool { 5 | return &b 6 | } 7 | 8 | // Int32 returns a pointer to an int32. 9 | func Int32(i int32) *int32 { 10 | return &i 11 | } 12 | 13 | // Int64 returns a pointer to an int64. 14 | func Int64(i int64) *int64 { 15 | return &i 16 | } 17 | 18 | // Int returns a pointer to an int. 19 | func Int(i int) *int { 20 | return &i 21 | } 22 | -------------------------------------------------------------------------------- /internal/stringutils/consume.go: -------------------------------------------------------------------------------- 1 | package stringutils 2 | 3 | import "strings" 4 | 5 | // ConsumePrefix checks if *s has the given prefix, and if yes, modifies it 6 | // to remove the prefix. The return value indicates whether the original string 7 | // had the given prefix. 8 | func ConsumePrefix(s *string, prefix string) bool { 9 | orig := *s 10 | if !strings.HasPrefix(orig, prefix) { 11 | return false 12 | } 13 | *s = orig[len(prefix):] 14 | return true 15 | } 16 | 17 | // ConsumeSuffix checks if *s has the given suffix, and if yes, modifies it 18 | // to remove the suffix. The return value indicates whether the original string 19 | // had the given suffix. 20 | func ConsumeSuffix(s *string, suffix string) bool { 21 | orig := *s 22 | if !strings.HasSuffix(orig, suffix) { 23 | return false 24 | } 25 | *s = orig[:len(orig)-len(suffix)] 26 | return true 27 | } 28 | -------------------------------------------------------------------------------- /internal/stringutils/default.go: -------------------------------------------------------------------------------- 1 | package stringutils 2 | 3 | // OrDefault returns the string if it's not empty, or the default. 4 | func OrDefault(s, defaultValue string) string { 5 | if s != "" { 6 | return s 7 | } 8 | return defaultValue 9 | } 10 | 11 | // PointerOrDefault returns the string if it's not nil nor empty, or the default. 12 | func PointerOrDefault(s *string, defaultValue string) string { 13 | if s == nil { 14 | return defaultValue 15 | } 16 | 17 | return OrDefault(*s, defaultValue) 18 | } 19 | -------------------------------------------------------------------------------- /internal/stringutils/repeat.go: -------------------------------------------------------------------------------- 1 | package stringutils 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // Repeat repeats the given string `n` times efficiently. 8 | func Repeat(s string, n int) string { 9 | var sb strings.Builder 10 | sb.Grow(len([]byte(s)) * n) 11 | for i := 0; i < n; i++ { 12 | sb.WriteString(s) 13 | } 14 | return sb.String() 15 | } 16 | -------------------------------------------------------------------------------- /internal/stringutils/split.go: -------------------------------------------------------------------------------- 1 | package stringutils 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // Split2 splits the given string at the given separator, returning the part before and after the separator as two 8 | // separate return values. 9 | // If the string does not contain `sep`, the entire string is returned as the first return value. 10 | func Split2(str, sep string) (string, string) { 11 | splitIdx := strings.Index(str, sep) 12 | if splitIdx == -1 { 13 | return str, "" 14 | } 15 | return str[:splitIdx], str[splitIdx+len(sep):] 16 | } 17 | -------------------------------------------------------------------------------- /internal/stringutils/ternary.go: -------------------------------------------------------------------------------- 1 | package stringutils 2 | 3 | // Ternary does a ternary based on the condition. 4 | func Ternary(condition bool, ifTrue, ifFalse string) string { 5 | if condition { 6 | return ifTrue 7 | } 8 | return ifFalse 9 | } 10 | -------------------------------------------------------------------------------- /internal/utils/ignore_error.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | // IgnoreError is useful when you want to defer a func that returns an error, 4 | // but ignore the error without having the linter complain. 5 | func IgnoreError(f func() error) { 6 | _ = f() 7 | } 8 | -------------------------------------------------------------------------------- /internal/utils/must.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | // Must panics if any of the errors are not nil. 4 | // It is intended for use in cases where an error returned would 5 | // mean a programming error. 6 | func Must(errs ...error) { 7 | for _, err := range errs { 8 | if err != nil { 9 | panic(err) 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "golang.stackrox.io/kube-linter/internal/stringutils" 5 | ) 6 | 7 | var ( 8 | version string //XDef:VERSION 9 | ) 10 | 11 | // Get returns the version. 12 | func Get() string { 13 | return stringutils.OrDefault(version, "development") 14 | } 15 | -------------------------------------------------------------------------------- /kubelinter-cosign.pub: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEl0HCkCRzYv0qH5QiazoXeXe2qwFX 3 | DmAszeH26g1s3OSsG/focPWkN88wEKQ5eiE95v+Z2snUQPl/mjPdvqpyjA== 4 | -----END PUBLIC KEY----- -------------------------------------------------------------------------------- /pkg/builtinchecks/built_in_checks_test.go: -------------------------------------------------------------------------------- 1 | package builtinchecks 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestBuiltInChecksWellFormed(t *testing.T) { 12 | checks, err := List() 13 | require.NoError(t, err) 14 | for _, check := range checks { 15 | t.Run(check.Name, func(t *testing.T) { 16 | assert.NotEmpty(t, check.Remediation, "Please add remediation") 17 | assert.True(t, strings.HasSuffix(check.Remediation, "."), "Please end your remediation texts with a period (got %q)", check.Remediation) 18 | }) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/access-to-create-pods.yaml: -------------------------------------------------------------------------------- 1 | name: "access-to-create-pods" 2 | description: >- 3 | Indicates when a subject (Group/User/ServiceAccount) has create access to Pods. 4 | CIS Benchmark 5.1.4: The ability to create pods in a cluster opens up possibilities for privilege escalation and should be restricted, where possible. 5 | remediation: "Where possible, remove create access to pod objects in the cluster." 6 | scope: 7 | objectKinds: 8 | - ClusterRoleBinding 9 | - RoleBinding 10 | template: "access-to-resources" 11 | params: 12 | resources: ["^pods$", "^deployments$", "^statefulsets$", "^replicasets$", "^cronjob$", "^jobs$","^daemonsets$"] 13 | verbs: ["^create$"] 14 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/access-to-secrets.yaml: -------------------------------------------------------------------------------- 1 | name: "access-to-secrets" 2 | description: >- 3 | Indicates when a subject (Group/User/ServiceAccount) has access to Secrets. 4 | CIS Benchmark 5.1.2: Access to secrets should be restricted to the smallest possible group of users to reduce the risk of privilege escalation. 5 | remediation: "Where possible, remove get, list and watch access to secret objects in the cluster." 6 | scope: 7 | objectKinds: 8 | - ClusterRoleBinding 9 | - RoleBinding 10 | template: "access-to-resources" 11 | params: 12 | resources: ["^secrets$"] 13 | verbs: ["^get$", "^list$", "^delete$", "^create$", "^watch$", "^*$"] 14 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/cluster-admin-role-binding.yaml: -------------------------------------------------------------------------------- 1 | name: "cluster-admin-role-binding" 2 | description: "CIS Benchmark 5.1.1 Ensure that the cluster-admin role is only used where required" 3 | remediation: "Create and assign a separate role that has access to specific resources/actions needed for the service account." 4 | scope: 5 | objectKinds: 6 | - ClusterRoleBinding 7 | template: "cluster-admin-role-binding" 8 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/dangling-horizontalpodautoscaler.yaml: -------------------------------------------------------------------------------- 1 | name: "dangling-horizontalpodautoscaler" 2 | description: "Indicates when HorizontalPodAutoscalers target a missing resource." 3 | remediation: "Confirm that your HorizontalPodAutoscaler's scaleTargetRef correctly matches one of your deployments." 4 | scope: 5 | objectKinds: 6 | - HorizontalPodAutoscaler 7 | template: "dangling-horizontalpodautoscaler" -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/dangling-ingress.yaml: -------------------------------------------------------------------------------- 1 | name: "dangling-ingress" 2 | description: "Indicates when ingress do not have any associated services." 3 | remediation: "Confirm that your ingress's backend correctly matches the name and port on one of your services." 4 | scope: 5 | objectKinds: 6 | - Ingress 7 | template: "dangling-ingress" 8 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/dangling-networkpolicy.yaml: -------------------------------------------------------------------------------- 1 | name: "dangling-networkpolicy" 2 | description: "Indicates when networkpolicies do not have any associated deployments." 3 | remediation: "Confirm that your networkPolicy's podselector correctly matches the labels on one of your deployments." 4 | scope: 5 | objectKinds: 6 | - NetworkPolicy 7 | template: "dangling-networkpolicy" -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/dangling-networkpolicypeer-podselector.yaml: -------------------------------------------------------------------------------- 1 | name: "dangling-networkpolicypeer-podselector" 2 | description: "Indicates when NetworkPolicyPeer in Egress/Ingress rules -in the Spec of NetworkPolicy- do not have any associated deployments. Applied on peer specified with podSelectors only." 3 | remediation: "Confirm that your NetworkPolicy's Ingress/Egress peer's podselector correctly matches the labels on one of your deployments." 4 | scope: 5 | objectKinds: 6 | - NetworkPolicy 7 | template: "dangling-networkpolicypeer-podselector" -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/dangling-service.yaml: -------------------------------------------------------------------------------- 1 | name: "dangling-service" 2 | description: "Indicates when services do not have any associated deployments." 3 | remediation: "Confirm that your service's selector correctly matches the labels on one of your deployments." 4 | scope: 5 | objectKinds: 6 | - Service 7 | template: "dangling-service" 8 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/dangling-servicemonitor.yaml: -------------------------------------------------------------------------------- 1 | name: "dangling-servicemonitor" 2 | description: "Indicates when a service monitor's selectors don't match any service. ServiceMonitors are a custom resource only used by the Prometheus operator (https://prometheus-operator.dev/docs/operator/design/#servicemonitor)." 3 | remediation: "Check selectors and your services." 4 | scope: 5 | objectKinds: 6 | - ServiceMonitor 7 | template: "dangling-servicemonitor" 8 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/default-service-account.yaml: -------------------------------------------------------------------------------- 1 | name: "default-service-account" 2 | description: "Indicates when pods use the default service account." 3 | remediation: >- 4 | Create a dedicated service account for your pod. 5 | Refer to https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ for details. 6 | scope: 7 | objectKinds: 8 | - DeploymentLike 9 | template: "service-account" 10 | params: 11 | serviceAccount: "^(|default)$" 12 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/deprecated-service-account.yaml: -------------------------------------------------------------------------------- 1 | name: "deprecated-service-account-field" 2 | description: "Indicates when deployments use the deprecated serviceAccount field." 3 | remediation: "Use the serviceAccountName field instead. If you must specify serviceAccount, ensure values for serviceAccount and serviceAccountName match." 4 | scope: 5 | objectKinds: 6 | - DeploymentLike 7 | template: "deprecated-service-account-field" 8 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/dnsconfig-options.yaml: -------------------------------------------------------------------------------- 1 | name: "dnsconfig-options" 2 | description: "Alert on deployments that have no specified dnsConfig options" 3 | remediation: >- 4 | Specify dnsconfig options in your Pod specification to ensure the expected DNS setting on the Pod. 5 | Refer to https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-dns-config for details. 6 | scope: 7 | objectKinds: 8 | - DeploymentLike 9 | template: "dnsconfig-options" 10 | params: 11 | Key: ndots 12 | Value: "2" 13 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/docker-sock.yaml: -------------------------------------------------------------------------------- 1 | name: "docker-sock" 2 | description: "Alert on deployments with docker.sock mounted in containers. " 3 | remediation: >- 4 | Ensure the Docker socket is not mounted inside any containers by removing the associated 5 | Volume and VolumeMount in deployment yaml specification. 6 | If the Docker socket is mounted inside a container it could allow processes running within 7 | the container to execute Docker commands which would effectively allow for full control of the host. 8 | 9 | scope: 10 | objectKinds: 11 | - DeploymentLike 12 | template: "host-mounts" 13 | params: 14 | dirs: ["docker.sock$"] 15 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/drop-net-raw-capability.yaml: -------------------------------------------------------------------------------- 1 | name: "drop-net-raw-capability" 2 | description: "Indicates when containers do not drop NET_RAW capability" 3 | remediation: >- 4 | NET_RAW makes it so that an application within the container is able to craft raw packets, 5 | use raw sockets, and bind to any address. Remove this capability in the containers under 6 | containers security contexts. 7 | scope: 8 | objectKinds: 9 | - DeploymentLike 10 | template: "verify-container-capabilities" 11 | params: 12 | forbiddenCapabilities: ["NET_RAW"] 13 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/duplicate-env-var.yaml: -------------------------------------------------------------------------------- 1 | name: "duplicate-env-var" 2 | description: "Check that duplicate named env vars aren't passed to a deployment like." 3 | remediation: "Confirm that your DeploymentLike doesn't have duplicate env vars names." 4 | scope: 5 | objectKinds: 6 | - DeploymentLike 7 | template: "duplicate-env-var" 8 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/env-var-secret.yaml: -------------------------------------------------------------------------------- 1 | name: "env-var-secret" 2 | description: "Indicates when objects use a secret in an environment variable." 3 | remediation: >- 4 | Do not use raw secrets in environment variables. Instead, either mount the secret as a file or use a secretKeyRef. 5 | Refer to https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets for details. 6 | scope: 7 | objectKinds: 8 | - DeploymentLike 9 | template: "env-var" 10 | params: 11 | name: "(?i).*secret.*" 12 | value: ".+" 13 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/host-mounts.yaml: -------------------------------------------------------------------------------- 1 | name: "sensitive-host-mounts" 2 | description: "Alert on deployments with sensitive host system directories mounted in containers" 3 | remediation: "Ensure sensitive host system directories are not mounted in containers by removing those Volumes and VolumeMounts." 4 | scope: 5 | objectKinds: 6 | - DeploymentLike 7 | template: "host-mounts" 8 | params: 9 | dirs: ["^/$", "^/boot$", "^/dev$", "^/etc$", "^/lib$", "^/proc$", "^/sys$", "^/usr$"] 10 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/hostipc.yaml: -------------------------------------------------------------------------------- 1 | name: "host-ipc" 2 | description: "Alert on pods/deployment-likes with sharing host's IPC namespace" 3 | remediation: "Ensure the host's IPC namespace is not shared." 4 | scope: 5 | objectKinds: 6 | - DeploymentLike 7 | template: "host-ipc" 8 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/hostnetwork.yaml: -------------------------------------------------------------------------------- 1 | name: "host-network" 2 | description: "Alert on pods/deployment-likes with sharing host's network namespace" 3 | remediation: "Ensure the host's network namespace is not shared." 4 | scope: 5 | objectKinds: 6 | - DeploymentLike 7 | template: "host-network" 8 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/hostpid.yaml: -------------------------------------------------------------------------------- 1 | name: "host-pid" 2 | description: "Alert on pods/deployment-likes with sharing host's process namespace" 3 | remediation: "Ensure the host's process namespace is not shared." 4 | scope: 5 | objectKinds: 6 | - DeploymentLike 7 | template: "host-pid" 8 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/hpa-minimum-replicas.yaml: -------------------------------------------------------------------------------- 1 | name: "hpa-minimum-three-replicas" 2 | description: "Indicates when a HorizontalPodAutoscaler specifies less than three minReplicas" 3 | remediation: >- 4 | Increase the number of replicas in the HorizontalPodAutoscaler to at least three to increase fault tolerance. 5 | scope: 6 | objectKinds: 7 | - HorizontalPodAutoscaler 8 | template: "hpa-minimum-replicas" 9 | params: 10 | minReplicas: 3 11 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/invalid-target-ports.yaml: -------------------------------------------------------------------------------- 1 | name: "invalid-target-ports" 2 | description: "Indicates when deployments or services are using port names that are violating specifications." 3 | remediation: >- 4 | Ensure that port naming is in conjunction with the specification. For more information, 5 | please look at the Kubernetes Service specification on this page: 6 | https://kubernetes.io/docs/reference/_print/#ServiceSpec. And additional information 7 | about IANA Service naming can be found on the following page: 8 | https://www.rfc-editor.org/rfc/rfc6335.html#section-5.1. 9 | scope: 10 | objectKinds: 11 | - DeploymentLike 12 | - Service 13 | template: "target-port" 14 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/latest-tag.yaml: -------------------------------------------------------------------------------- 1 | name: "latest-tag" 2 | description: "Indicates when a deployment-like object is running a container with an invalid container image" 3 | remediation: "Use a container image with a specific tag other than latest." 4 | scope: 5 | objectKinds: 6 | - DeploymentLike 7 | template: "latest-tag" 8 | params: 9 | BlockList: [".*:(latest)$", "^[^:]*$", "(.*/[^:]+)$"] 10 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/liveness-port.yaml: -------------------------------------------------------------------------------- 1 | name: "liveness-port" 2 | description: "Indicates when containers have a liveness probe to a not exposed port." 3 | remediation: >- 4 | Check which ports you've exposed and ensure they match what you have specified 5 | in the liveness probe. 6 | scope: 7 | objectKinds: 8 | - DeploymentLike 9 | template: "liveness-port" 10 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/minimum-replicas.yaml: -------------------------------------------------------------------------------- 1 | name: "minimum-three-replicas" 2 | description: "Indicates when a deployment uses less than three replicas" 3 | remediation: >- 4 | Increase the number of replicas in the deployment to at least three to increase the fault tolerance of the deployment. 5 | scope: 6 | objectKinds: 7 | - DeploymentLike 8 | template: "minimum-replicas" 9 | params: 10 | minReplicas: 3 11 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/mismatching-selector.yaml: -------------------------------------------------------------------------------- 1 | name: "mismatching-selector" 2 | description: "Indicates when deployment selectors fail to match the pod template labels." 3 | remediation: "Confirm that your deployment selector correctly matches the labels in its pod template." 4 | scope: 5 | objectKinds: 6 | - DeploymentLike 7 | template: "mismatching-selector" 8 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/no-anti-affinity.yaml: -------------------------------------------------------------------------------- 1 | name: "no-anti-affinity" 2 | description: "Indicates when deployments with multiple replicas fail to specify inter-pod anti-affinity, to ensure that the orchestrator attempts to schedule replicas on different nodes." 3 | remediation: >- 4 | Specify anti-affinity in your pod specification to ensure that the orchestrator attempts to schedule replicas on different nodes. 5 | Using podAntiAffinity, specify a labelSelector that matches pods for the deployment, 6 | and set the topologyKey to kubernetes.io/hostname. 7 | Refer to https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity for details. 8 | scope: 9 | objectKinds: 10 | - DeploymentLike 11 | template: "anti-affinity" 12 | params: 13 | minReplicas: 2 14 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/no-extensions-v1beta.yaml: -------------------------------------------------------------------------------- 1 | name: "no-extensions-v1beta" 2 | description: "Indicates when objects use deprecated API versions under extensions/v1beta." 3 | remediation: >- 4 | Migrate using the apps/v1 API versions for the objects. 5 | Refer to https://kubernetes.io/blog/2019/07/18/api-deprecations-in-1-16/ for details. 6 | scope: 7 | objectKinds: 8 | - Any 9 | template: "disallowed-api-obj" 10 | params: 11 | group: "extensions" 12 | version: "v1beta.+" 13 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/no-liveness-probe.yaml: -------------------------------------------------------------------------------- 1 | name: "no-liveness-probe" 2 | description: "Indicates when containers fail to specify a liveness probe." 3 | remediation: >- 4 | Specify a liveness probe in your container. 5 | Refer to https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ for details. 6 | scope: 7 | objectKinds: 8 | - DeploymentLike 9 | template: "liveness-probe" 10 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/no-node-affinity.yaml: -------------------------------------------------------------------------------- 1 | name: "no-node-affinity" 2 | description: "Alert on deployments that have no node affinity defined" 3 | remediation: >- 4 | Specify node-affinity in your pod specification to ensure that the orchestrator attempts to schedule replicas on specified nodes. 5 | Refer to https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity for details. 6 | scope: 7 | objectKinds: 8 | - DeploymentLike 9 | template: "no-node-affinity" 10 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/no-readiness-probe.yaml: -------------------------------------------------------------------------------- 1 | name: "no-readiness-probe" 2 | description: "Indicates when containers fail to specify a readiness probe." 3 | remediation: >- 4 | Specify a readiness probe in your container. 5 | Refer to https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ for details. 6 | scope: 7 | objectKinds: 8 | - DeploymentLike 9 | template: "readiness-probe" 10 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/no-rolling-update-strategy.yaml: -------------------------------------------------------------------------------- 1 | name: "no-rolling-update-strategy" 2 | description: "Indicates when a deployment doesn't use a rolling update strategy" 3 | remediation: >- 4 | Use a rolling update strategy to avoid service disruption during an update. 5 | A rolling update strategy allows for pods to be systematicaly replaced in a 6 | controlled fashion to ensure no service disruption. 7 | scope: 8 | objectKinds: 9 | - DeploymentLike 10 | template: "update-configuration" 11 | params: 12 | strategyTypeRegex: "^(RollingUpdate|Rolling)$" 13 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/non-existent-service-account.yaml: -------------------------------------------------------------------------------- 1 | name: "non-existent-service-account" 2 | description: "Indicates when pods reference a service account that is not found." 3 | remediation: "Create the missing service account, or refer to an existing service account." 4 | scope: 5 | objectKinds: 6 | - DeploymentLike 7 | template: "non-existent-service-account" 8 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/non-isolated-pod.yaml: -------------------------------------------------------------------------------- 1 | name: "non-isolated-pod" 2 | description: "Alert on deployment-like objects that are not selected by any NetworkPolicy." 3 | remediation: "Ensure pod does not accept unsafe traffic by isolating it with a NetworkPolicy. See https://cloud.redhat.com/blog/guide-to-kubernetes-ingress-network-policies for more details." 4 | scope: 5 | objectKinds: 6 | - DeploymentLike 7 | template: "non-isolated-pod" -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/pdb-unhealthy-pod-eviction-policy.yaml: -------------------------------------------------------------------------------- 1 | name: "pdb-unhealthy-pod-eviction-policy" 2 | description: "Indicates when a PodDisruptionBudget does not explicitly set the unhealthyPodEvictionPolicy field." 3 | remediation: "Set unhealthyPodEvictionPolicy to AlwaysAllow. Refer to https://kubernetes.io/docs/tasks/run-application/configure-pdb/#unhealthy-pod-eviction-policy for more information." 4 | scope: 5 | objectKinds: 6 | - PodDisruptionBudget 7 | template: "pdb-unhealthy-pod-eviction-policy" 8 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/pdbs-max-unavailable.yaml: -------------------------------------------------------------------------------- 1 | name: "pdb-max-unavailable" 2 | description: "Indicates when a PodDisruptionBudget has a maxUnavailable value that will always prevent disruptions of pods created by related deployment-like objects." 3 | remediation: "Change the PodDisruptionBudget to have maxUnavailable set to a value greater than 0. Refer to https://kubernetes.io/docs/tasks/run-application/configure-pdb/ for more information." 4 | scope: 5 | objectKinds: 6 | - PodDisruptionBudget 7 | template: "pdb-max-unavailable" 8 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/pdbs-min-available.yaml: -------------------------------------------------------------------------------- 1 | name: "pdb-min-available" 2 | description: "Indicates when a PodDisruptionBudget sets a minAvailable value that will always prevent disruptions of pods created by related deployment-like objects." 3 | remediation: "Change the PodDisruptionBudget to have minAvailable set to a number lower than the number of replicas in the related deployment-like objects. Refer to https://kubernetes.io/docs/tasks/run-application/configure-pdb/ for more information." 4 | scope: 5 | objectKinds: 6 | - PodDisruptionBudget 7 | template: "pdb-min-available" 8 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/privilege-escalation.yaml: -------------------------------------------------------------------------------- 1 | name: "privilege-escalation-container" 2 | description: "Alert on containers of allowing privilege escalation that could gain more privileges than its parent process." 3 | remediation: >- 4 | Ensure containers do not allow privilege escalation by setting 5 | allowPrivilegeEscalation=false, privileged=false and removing CAP_SYS_ADMIN capability. 6 | See https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ for more details. 7 | scope: 8 | objectKinds: 9 | - DeploymentLike 10 | template: "privilege-escalation-container" 11 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/privileged.yaml: -------------------------------------------------------------------------------- 1 | name: "privileged-container" 2 | description: "Indicates when deployments have containers running in privileged mode." 3 | remediation: "Do not run your container as privileged unless it is required." 4 | scope: 5 | objectKinds: 6 | - DeploymentLike 7 | template: "privileged" 8 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/privilegedports.yaml: -------------------------------------------------------------------------------- 1 | name: "privileged-ports" 2 | description: "Alert on deployments with privileged ports mapped in containers" 3 | remediation: "Ensure privileged ports [0, 1024] are not mapped within containers." 4 | scope: 5 | objectKinds: 6 | - DeploymentLike 7 | template: "privileged-ports" 8 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/read-only-root-fs.yaml: -------------------------------------------------------------------------------- 1 | name: "no-read-only-root-fs" 2 | description: "Indicates when containers are running without a read-only root filesystem." 3 | remediation: "Set readOnlyRootFilesystem to true in the container securityContext." 4 | scope: 5 | objectKinds: 6 | - DeploymentLike 7 | template: "read-only-root-fs" 8 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/read-secret-from-env-var.yaml: -------------------------------------------------------------------------------- 1 | name: "read-secret-from-env-var" 2 | description: >- 3 | Indicates when a deployment reads secret from environment variables. 4 | CIS Benchmark 5.4.1: "Prefer using secrets as files over secrets as environment variables. " 5 | remediation: >- 6 | If possible, rewrite application code to read secrets from mounted secret files, rather than from environment variables. 7 | Refer to https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets for details. 8 | scope: 9 | objectKinds: 10 | - DeploymentLike 11 | template: "read-secret-from-env-var" 12 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/readiness-port.yaml: -------------------------------------------------------------------------------- 1 | name: "readiness-port" 2 | description: "Indicates when containers have a readiness probe to a not exposed port." 3 | remediation: >- 4 | Check which ports you've exposed and ensure they match what you have specified 5 | in the readiness probe. 6 | scope: 7 | objectKinds: 8 | - DeploymentLike 9 | template: "readiness-port" 10 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/required-annotation-email.yaml: -------------------------------------------------------------------------------- 1 | name: "required-annotation-email" 2 | description: "Indicates when objects do not have an email annotation with a valid email address." 3 | remediation: "Add an email annotation to your object with the email address of the object's owner." 4 | scope: 5 | objectKinds: 6 | - DeploymentLike 7 | template: "required-annotation" 8 | params: 9 | key: "email" 10 | value: '[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+' 11 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/required-label-owner.yaml: -------------------------------------------------------------------------------- 1 | name: "required-label-owner" 2 | description: "Indicates when objects do not have an email annotation with an owner label." 3 | remediation: "Add an email annotation to your object with the name of the object's owner." 4 | scope: 5 | objectKinds: 6 | - DeploymentLike 7 | template: "required-label" 8 | params: 9 | key: "owner" 10 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/restart-policy.yaml: -------------------------------------------------------------------------------- 1 | name: "restart-policy" 2 | description: "Indicates when a deployment-like object does not use a restart policy" 3 | remediation: >- 4 | Set up the restart policy for your object to 'Always' or 'OnFailure' to increase the fault tolerance. 5 | scope: 6 | objectKinds: 7 | - DeploymentLike 8 | template: "restart-policy" -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/run-as-non-root.yaml: -------------------------------------------------------------------------------- 1 | name: "run-as-non-root" 2 | description: "Indicates when containers are not set to runAsNonRoot." 3 | remediation: >- 4 | Set runAsUser to a non-zero number and runAsNonRoot to true in your pod or container securityContext. 5 | Refer to https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ for details. 6 | scope: 7 | objectKinds: 8 | - DeploymentLike 9 | template: "run-as-non-root" 10 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/scc-deny-privileged-container.yaml: -------------------------------------------------------------------------------- 1 | name: "scc-deny-privileged-container" 2 | description: "Indicates when allowPrivilegedContainer SecurityContextConstraints set to true" 3 | remediation: >- 4 | SecurityContextConstraints has AllowPrivilegedContainer set to "true". Using this option is dangerous, please consider using allowedCapabilities instead. Refer to https://docs.openshift.com/container-platform/4.12/authentication/managing-security-context-constraints.html#scc-settings_configuring-internal-oauth for details. 5 | scope: 6 | objectKinds: 7 | - SecurityContextConstraints 8 | template: "scc-deny-privileged-container" 9 | params: 10 | AllowPrivilegedContainer: true -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/servicetype.yaml: -------------------------------------------------------------------------------- 1 | name: "exposed-services" 2 | description: "Alert on services for forbidden types" 3 | remediation: "Ensure containers are not exposed through a forbidden service type such as NodePort or LoadBalancer." 4 | scope: 5 | objectKinds: 6 | - Service 7 | template: "forbidden-service-types" 8 | params: 9 | forbiddenServiceTypes: ["NodePort", "LoadBalancer"] 10 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/ssh-port.yaml: -------------------------------------------------------------------------------- 1 | name: "ssh-port" 2 | description: "Indicates when deployments expose port 22, which is commonly reserved for SSH access." 3 | remediation: "Ensure that non-SSH services are not using port 22. Confirm that any actual SSH servers have been vetted." 4 | scope: 5 | objectKinds: 6 | - DeploymentLike 7 | template: "ports" 8 | params: 9 | port: 22 10 | protocol: "TCP" 11 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/startup-port.yaml: -------------------------------------------------------------------------------- 1 | name: "startup-port" 2 | description: "Indicates when containers have a startup probe to a not exposed port." 3 | remediation: >- 4 | Check which ports you've exposed and ensure they match what you have specified 5 | in the startup probe. 6 | scope: 7 | objectKinds: 8 | - DeploymentLike 9 | template: "startup-port" 10 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/sysctls.yaml: -------------------------------------------------------------------------------- 1 | name: "unsafe-sysctls" 2 | description: "Alert on deployments specifying unsafe sysctls that may lead to severe problems like wrong behavior of containers" 3 | remediation: >- 4 | Ensure container does not allow unsafe allocation of system resources by removing unsafe sysctls configurations. 5 | For more details see https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ 6 | https://docs.docker.com/engine/reference/commandline/run/#configure-namespaced-kernel-parameters-sysctls-at-runtime. 7 | scope: 8 | objectKinds: 9 | - DeploymentLike 10 | template: "unsafe-sysctls" 11 | params: 12 | unsafeSysCtls: ["kernel.msg", "kernel.sem", "kernel.shm", "fs.mqueue.", "net."] 13 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/unsafe-proc-mount.yaml: -------------------------------------------------------------------------------- 1 | name: "unsafe-proc-mount" 2 | description: "Alert on deployments with unsafe /proc mount (procMount=Unmasked) that will bypass the default masking behavior of the container runtime" 3 | remediation: >- 4 | Ensure container does not unsafely exposes parts of /proc by setting procMount=Default. 5 | Unmasked ProcMount bypasses the default masking behavior of the container runtime. 6 | See https://kubernetes.io/docs/concepts/security/pod-security-standards/ for more details. 7 | scope: 8 | objectKinds: 9 | - DeploymentLike 10 | template: "unsafe-proc-mount" 11 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/unset-cpu-requirements.yaml: -------------------------------------------------------------------------------- 1 | name: "unset-cpu-requirements" 2 | description: "Indicates when containers do not have CPU requests and limits set." 3 | scope: 4 | objectKinds: 5 | - DeploymentLike 6 | remediation: >- 7 | Set CPU requests for your container based on its requirements. 8 | Refer to https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#requests-and-limits for details. 9 | template: "cpu-requirements" 10 | params: 11 | requirementsType: "request" 12 | lowerBoundMillis: 0 13 | upperBoundMillis: 0 14 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/unset-memory-requirements.yaml: -------------------------------------------------------------------------------- 1 | name: "unset-memory-requirements" 2 | description: "Indicates when containers do not have memory requests and limits set." 3 | remediation: >- 4 | Set memory limits for your container based on its requirements. 5 | Refer to https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#requests-and-limits for details. 6 | scope: 7 | objectKinds: 8 | - DeploymentLike 9 | template: "memory-requirements" 10 | params: 11 | requirementsType: "limit" 12 | lowerBoundMB: 0 13 | upperBoundMB: 0 14 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/usenamespace.yaml: -------------------------------------------------------------------------------- 1 | name: "use-namespace" 2 | description: >- 3 | Indicates when a resource is deployed to the default namespace. 4 | CIS Benchmark 5.7.1: Create administrative boundaries between resources using namespaces. 5 | CIS Benchmark 5.7.4: The default namespace should not be used. 6 | remediation: "Create namespaces for objects in your deployment." 7 | scope: 8 | objectKinds: 9 | - DeploymentLike 10 | - Service 11 | template: "use-namespace" 12 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/wildcard-use-in-rules.yaml: -------------------------------------------------------------------------------- 1 | name: "wildcard-in-rules" 2 | description: >- 3 | Indicate when a wildcard is used in Role or ClusterRole rules. 4 | CIS Benchmark 5.1.3 Use of wildcards is not optimal from a security perspective as it may allow for inadvertent access to be granted when new resources are added to the Kubernetes API either as CRDs or in later versions of the product. 5 | remediation: "Where possible replace any use of wildcards in clusterroles and roles with specific objects or actions." 6 | scope: 7 | objectKinds: 8 | - ClusterRole 9 | - Role 10 | template: "wildcard-in-rules" 11 | -------------------------------------------------------------------------------- /pkg/builtinchecks/yamls/writable-host-mount.yaml: -------------------------------------------------------------------------------- 1 | name: "writable-host-mount" 2 | description: "Indicates when containers mount a host path as writable." 3 | remediation: "Set containers to mount host paths as readOnly, if you need to access files on the host." 4 | scope: 5 | objectKinds: 6 | - DeploymentLike 7 | template: "writable-host-mount" 8 | -------------------------------------------------------------------------------- /pkg/checkregistry/check_registry.go: -------------------------------------------------------------------------------- 1 | package checkregistry 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | "golang.stackrox.io/kube-linter/pkg/config" 6 | "golang.stackrox.io/kube-linter/pkg/instantiatedcheck" 7 | ) 8 | 9 | // A CheckRegistry is a registry of checks. 10 | // It is not thread-safe. It is anticipated that checks will all be registered ahead of time 11 | // before calls to Load. 12 | type CheckRegistry interface { 13 | Register(checks ...*config.Check) error 14 | Load(name string) *instantiatedcheck.InstantiatedCheck 15 | } 16 | 17 | type checkRegistry map[string]*instantiatedcheck.InstantiatedCheck 18 | 19 | func (cr checkRegistry) Register(checks ...*config.Check) error { 20 | for _, c := range checks { 21 | instantiated, err := instantiatedcheck.ValidateAndInstantiate(c) 22 | if err != nil { 23 | return errors.Wrapf(err, "invalid check %s", c.Name) 24 | } 25 | if _, ok := cr[instantiated.Spec.Name]; ok { 26 | return errors.Errorf("duplicate check name: %s", instantiated.Spec.Name) 27 | } 28 | cr[instantiated.Spec.Name] = instantiated 29 | } 30 | return nil 31 | } 32 | 33 | func (cr checkRegistry) Load(name string) *instantiatedcheck.InstantiatedCheck { 34 | return cr[name] 35 | } 36 | 37 | // New returns a ready-to-use, empty CheckRegistry. 38 | func New() CheckRegistry { 39 | return make(checkRegistry) 40 | } 41 | -------------------------------------------------------------------------------- /pkg/command/lint/command_test.go: -------------------------------------------------------------------------------- 1 | package lint 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | // Register templates 9 | _ "golang.stackrox.io/kube-linter/pkg/templates/all" 10 | ) 11 | 12 | func TestCommand_InvalidResources(t *testing.T) { 13 | tests := []struct { 14 | name string 15 | cmd *cobra.Command 16 | failure bool 17 | output string 18 | }{ 19 | {name: "InvalidPodResource", cmd: createLintCommand("./testdata/invalid-pod-resources.yaml", "--fail-on-invalid-resource"), failure: true}, 20 | {name: "InvalidPVCResource", cmd: createLintCommand("./testdata/invalid-pvc-resources.yaml", "--fail-on-invalid-resource"), failure: true}, 21 | {name: "NonexistentFile", cmd: createLintCommand("./testdata/foo-bar.yaml", "--fail-on-invalid-resource"), failure: true}, 22 | {name: "ValidPod", cmd: createLintCommand("./testdata/valid-pod.yaml", "--fail-on-invalid-resource"), failure: false}, 23 | } 24 | for _, tt := range tests { 25 | t.Run(tt.name, func(t *testing.T) { 26 | err := tt.cmd.Execute() 27 | if err == nil && tt.failure || err != nil && !tt.failure { 28 | t.Fail() 29 | } 30 | }) 31 | } 32 | } 33 | 34 | func createLintCommand(args ...string) *cobra.Command { 35 | c := Command() 36 | c.SilenceUsage = true 37 | c.SetArgs(args) 38 | return c 39 | } 40 | -------------------------------------------------------------------------------- /pkg/command/lint/testdata/invalid-pod-resources.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | creationTimestamp: null 5 | name: foo-pod 6 | namespace: foo 7 | spec: 8 | containers: 9 | - image: busybox 10 | name: invalid 11 | command: 12 | - "sleep" 13 | args: 14 | - "infinity" 15 | resources: 16 | limits: 17 | cpu: 25m 18 | memory: 1GB 19 | requests: 20 | cpu: 25m 21 | memory: 1GB 22 | dnsPolicy: ClusterFirst 23 | restartPolicy: Always 24 | status: {} -------------------------------------------------------------------------------- /pkg/command/lint/testdata/invalid-pvc-resources.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: foo-pvc 5 | namespace: foo 6 | spec: 7 | accessModes: 8 | - ReadWriteOnce 9 | resources: 10 | requests: 11 | storage: 250GB 12 | storageClassName: thin-disk -------------------------------------------------------------------------------- /pkg/command/lint/testdata/valid-pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: homebrew-demo 5 | spec: 6 | securityContext: 7 | runAsUser: 1000 8 | runAsGroup: 3000 9 | fsGroup: 2000 10 | containers: 11 | - name: homebrew-test 12 | image: busybox:stable 13 | resources: 14 | limits: 15 | memory: "128Mi" 16 | cpu: "500m" 17 | requests: 18 | memory: "64Mi" 19 | cpu: "250m" 20 | securityContext: 21 | readOnlyRootFilesystem: true -------------------------------------------------------------------------------- /pkg/command/root/command.go: -------------------------------------------------------------------------------- 1 | package root 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "github.com/fatih/color" 8 | "github.com/spf13/cobra" 9 | "golang.stackrox.io/kube-linter/pkg/command/checks" 10 | "golang.stackrox.io/kube-linter/pkg/command/lint" 11 | "golang.stackrox.io/kube-linter/pkg/command/templates" 12 | "golang.stackrox.io/kube-linter/pkg/command/version" 13 | ) 14 | 15 | const ( 16 | colorFlag = "with-color" 17 | ) 18 | 19 | // Command is the root command. 20 | func Command() *cobra.Command { 21 | c := &cobra.Command{ 22 | Use: filepath.Base(os.Args[0]), 23 | SilenceUsage: true, 24 | SilenceErrors: true, 25 | PersistentPreRun: func(cmd *cobra.Command, _ []string) { 26 | // Only forcefully set colorful output if the flag has been set. 27 | if cmd.Flags().Changed(colorFlag) { 28 | color.NoColor = false 29 | } 30 | }, 31 | } 32 | c.AddCommand( 33 | checks.Command(), 34 | lint.Command(), 35 | templates.Command(), 36 | version.Command(), 37 | ) 38 | c.PersistentFlags().Bool(colorFlag, true, "Force color output") 39 | return c 40 | } 41 | -------------------------------------------------------------------------------- /pkg/command/root/command_test.go: -------------------------------------------------------------------------------- 1 | package root 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "testing" 7 | 8 | "github.com/fatih/color" 9 | "github.com/stretchr/testify/assert" 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | func TestCommand(t *testing.T) { 14 | c := Command() 15 | c.SetOut(io.Discard) 16 | c.SetArgs([]string{ 17 | "version", 18 | fmt.Sprintf("--%s", colorFlag), 19 | }) 20 | err := c.Execute() 21 | require.NoError(t, err) 22 | assert.False(t, color.NoColor) 23 | } 24 | -------------------------------------------------------------------------------- /pkg/command/version/command.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | "golang.stackrox.io/kube-linter/internal/version" 8 | ) 9 | 10 | // Command defines the version command 11 | func Command() *cobra.Command { 12 | c := &cobra.Command{ 13 | Use: "version", 14 | Short: "Print version and exit", 15 | Args: cobra.NoArgs, 16 | Run: func(cmd *cobra.Command, _ []string) { 17 | fmt.Println(version.Get()) 18 | }, 19 | } 20 | return c 21 | } 22 | -------------------------------------------------------------------------------- /pkg/config/check.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | // A Check represents a single check. It is serializable. 4 | type Check struct { 5 | Name string `json:"name"` 6 | Description string `json:"description"` 7 | Remediation string `json:"remediation"` 8 | Scope *ObjectKindsDesc `json:"scope"` 9 | Template string `json:"template"` 10 | Params map[string]interface{} `json:"params,omitempty"` 11 | } 12 | 13 | // ObjectKindsDesc describes a list of supported object kinds for a check template. 14 | type ObjectKindsDesc struct { 15 | ObjectKinds []string `json:"objectKinds"` 16 | } 17 | -------------------------------------------------------------------------------- /pkg/config/gen.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | //go:generate go run ./codegen flags.go 4 | -------------------------------------------------------------------------------- /pkg/configresolver/config_resolver_test.go: -------------------------------------------------------------------------------- 1 | package configresolver 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "testing" 7 | 8 | "github.com/mitchellh/go-homedir" 9 | "github.com/stretchr/testify/assert" 10 | "golang.stackrox.io/kube-linter/pkg/config" 11 | ) 12 | 13 | func TestIgnorePaths(t *testing.T) { 14 | home, homeErr := homedir.Dir() 15 | if homeErr != nil { 16 | t.Fatal(homeErr) 17 | } 18 | wd, wdErr := os.Getwd() 19 | if wdErr != nil { 20 | t.Fatal(wdErr) 21 | } 22 | 23 | parent := filepath.Dir(wd) 24 | c := new(config.Config) 25 | 26 | var tests = []struct { 27 | Paths []string 28 | Expected string 29 | ErrorExpeted bool 30 | }{ 31 | {[]string{"~/test"}, home + "/test", false}, 32 | {[]string{"~/*.yaml"}, home + "/*.yaml", false}, 33 | {[]string{"~~/test"}, "", true}, 34 | {[]string{"../test"}, parent + "/test", false}, 35 | {[]string{"../*.yaml"}, parent + "/*.yaml", false}, 36 | {[]string{"~/test", "~/test"}, home + "/test", false}, 37 | } 38 | 39 | for _, e := range tests { 40 | c.Checks.IgnorePaths = e.Paths 41 | paths, err := GetIgnorePaths(c) 42 | 43 | if e.ErrorExpeted { 44 | assert.Error(t, err) 45 | } else { 46 | for _, path := range paths { 47 | assert.NoError(t, err) 48 | assert.Equal(t, e.Expected, path) 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pkg/diagnostic/diagnostic.go: -------------------------------------------------------------------------------- 1 | package diagnostic 2 | 3 | import ( 4 | "golang.stackrox.io/kube-linter/pkg/lintcontext" 5 | ) 6 | 7 | // A Diagnostic represents one specific problem diagnosed by a check. 8 | type Diagnostic struct { 9 | Message string 10 | 11 | // TODO: add line number/col number 12 | } 13 | 14 | // WithContext puts a diagnostic in the context of which check emitted it, 15 | // and which object it applied to. 16 | type WithContext struct { 17 | Diagnostic Diagnostic 18 | Check string 19 | Remediation string 20 | Object lintcontext.Object 21 | } 22 | -------------------------------------------------------------------------------- /pkg/extract/gvk.go: -------------------------------------------------------------------------------- 1 | package extract 2 | 3 | import ( 4 | "golang.stackrox.io/kube-linter/pkg/k8sutil" 5 | "k8s.io/apimachinery/pkg/runtime/schema" 6 | ) 7 | 8 | // GVK extracts the GroupVersionKind of an object. 9 | func GVK(object k8sutil.Object) schema.GroupVersionKind { 10 | return object.GetObjectKind().GroupVersionKind() 11 | } 12 | -------------------------------------------------------------------------------- /pkg/extract/metadata.go: -------------------------------------------------------------------------------- 1 | package extract 2 | 3 | import ( 4 | "golang.stackrox.io/kube-linter/pkg/k8sutil" 5 | ) 6 | 7 | // Labels extracts labels from the given object. 8 | func Labels(object k8sutil.Object) map[string]string { 9 | return object.GetLabels() 10 | } 11 | 12 | // Annotations extracts annotations from the given object. 13 | func Annotations(object k8sutil.Object) map[string]string { 14 | return object.GetAnnotations() 15 | } 16 | -------------------------------------------------------------------------------- /pkg/extract/scc_spec.go: -------------------------------------------------------------------------------- 1 | package extract 2 | 3 | import ( 4 | ocpSecV1 "github.com/openshift/api/security/v1" 5 | "golang.stackrox.io/kube-linter/pkg/k8sutil" 6 | ) 7 | 8 | // SCCallowPrivilegedContainer extracts allowPrivilegedContainer from the given object, if available. 9 | func SCCallowPrivilegedContainer(obj k8sutil.Object) (bool, bool) { 10 | if scc, ok := obj.(*ocpSecV1.SecurityContextConstraints); ok { 11 | return scc.AllowPrivilegedContainer, true 12 | } 13 | return false, false 14 | } 15 | -------------------------------------------------------------------------------- /pkg/ignore/ignore.go: -------------------------------------------------------------------------------- 1 | package ignore 2 | 3 | import ( 4 | "golang.stackrox.io/kube-linter/internal/stringutils" 5 | ) 6 | 7 | const ( 8 | // AnnotationKeyPrefix is the prefix for annotations for kube-linter check ignores. 9 | AnnotationKeyPrefix = "ignore-check.kube-linter.io/" 10 | 11 | // AllAnnotationKey is used to ignore all checks for a given object. 12 | AllAnnotationKey = "kube-linter.io/ignore-all" 13 | ) 14 | 15 | // ObjectForCheck returns whether to ignore the given object for the passed check name. 16 | func ObjectForCheck(annotations map[string]string, checkName string) bool { 17 | for k := range annotations { 18 | if k == AllAnnotationKey { 19 | return true 20 | } 21 | key := k 22 | if stringutils.ConsumePrefix(&key, AnnotationKeyPrefix) && key == checkName { 23 | return true 24 | } 25 | } 26 | return false 27 | } 28 | -------------------------------------------------------------------------------- /pkg/k8sutil/object.go: -------------------------------------------------------------------------------- 1 | package k8sutil 2 | 3 | import ( 4 | metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | "k8s.io/apimachinery/pkg/runtime" 6 | ) 7 | 8 | // Object is a combination of `runtime.Object` and `metav1.Object`. 9 | type Object interface { 10 | runtime.Object 11 | metaV1.Object 12 | } 13 | -------------------------------------------------------------------------------- /pkg/lintcontext/mocks/clusterrole.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 8 | rbacV1 "k8s.io/api/rbac/v1" 9 | metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | ) 11 | 12 | // AddMockClusterRole adds a mock ClusterRole to LintContext 13 | func (l *MockLintContext) AddMockClusterRole(t *testing.T, name string) { 14 | require.NotEmpty(t, name) 15 | l.objects[name] = &rbacV1.ClusterRole{ 16 | TypeMeta: metaV1.TypeMeta{ 17 | Kind: objectkinds.ClusterRole, 18 | APIVersion: objectkinds.GetClusterRoleAPIVersion(), 19 | }, 20 | ObjectMeta: metaV1.ObjectMeta{Name: name}, 21 | Rules: []rbacV1.PolicyRule{}, 22 | AggregationRule: &rbacV1.AggregationRule{}, 23 | } 24 | } 25 | 26 | // ModifyClusterRole modifies a given clusterrole in the context via the passed function. 27 | func (l *MockLintContext) ModifyClusterRole(t *testing.T, name string, f func(clusterrole *rbacV1.ClusterRole)) { 28 | r, ok := l.objects[name].(*rbacV1.ClusterRole) 29 | require.True(t, ok) 30 | f(r) 31 | } 32 | -------------------------------------------------------------------------------- /pkg/lintcontext/mocks/clusterrolebinding.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 8 | rbacV1 "k8s.io/api/rbac/v1" 9 | metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | ) 11 | 12 | // AddMockClusterRoleBinding adds a mock ClusterRoleBinding to LintContext 13 | func (l *MockLintContext) AddMockClusterRoleBinding(t *testing.T, name string) { 14 | require.NotEmpty(t, name) 15 | l.objects[name] = &rbacV1.ClusterRoleBinding{ 16 | TypeMeta: metaV1.TypeMeta{ 17 | Kind: objectkinds.ClusterRoleBinding, 18 | APIVersion: objectkinds.GetClusterRoleBindingAPIVersion(), 19 | }, 20 | ObjectMeta: metaV1.ObjectMeta{Name: name}, 21 | Subjects: []rbacV1.Subject{}, 22 | RoleRef: rbacV1.RoleRef{}, 23 | } 24 | } 25 | 26 | // ModifyClusterRoleBinding modifies a given ClusterRoleBinding in the context via the passed function. 27 | func (l *MockLintContext) ModifyClusterRoleBinding(t *testing.T, name string, f func(clusterrolebinding *rbacV1.ClusterRoleBinding)) { 28 | crb, ok := l.objects[name].(*rbacV1.ClusterRoleBinding) 29 | require.True(t, ok) 30 | f(crb) 31 | } 32 | -------------------------------------------------------------------------------- /pkg/lintcontext/mocks/container.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | appsV1 "k8s.io/api/apps/v1" 8 | v1 "k8s.io/api/core/v1" 9 | ) 10 | 11 | // AddContainerToDeployment adds a mock container to the specified pod under context 12 | func (l *MockLintContext) AddContainerToDeployment(t *testing.T, deploymentName string, container v1.Container) { 13 | deployment, ok := l.objects[deploymentName].(*appsV1.Deployment) 14 | require.True(t, ok, "deployment with name %s not found", deploymentName) 15 | // TODO: keep supporting other fields 16 | deployment.Spec.Template.Spec.Containers = append(deployment.Spec.Template.Spec.Containers, container) 17 | } 18 | -------------------------------------------------------------------------------- /pkg/lintcontext/mocks/context.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "golang.stackrox.io/kube-linter/pkg/k8sutil" 5 | "golang.stackrox.io/kube-linter/pkg/lintcontext" 6 | ) 7 | 8 | // MockLintContext is mock implementation of the LintContext used in unit tests 9 | type MockLintContext struct { 10 | objects map[string]k8sutil.Object 11 | } 12 | 13 | // Objects returns all the objects under this MockLintContext 14 | func (l *MockLintContext) Objects() []lintcontext.Object { 15 | result := make([]lintcontext.Object, 0, len(l.objects)) 16 | for _, p := range l.objects { 17 | result = append(result, lintcontext.Object{Metadata: lintcontext.ObjectMetadata{}, K8sObject: p}) 18 | } 19 | return result 20 | } 21 | 22 | // InvalidObjects is not implemented. For now we don't care about invalid objects for mock context. 23 | func (l *MockLintContext) InvalidObjects() []lintcontext.InvalidObject { 24 | return nil 25 | } 26 | 27 | // NewMockContext returns an empty mockLintContext 28 | func NewMockContext() *MockLintContext { 29 | return &MockLintContext{objects: make(map[string]k8sutil.Object)} 30 | } 31 | -------------------------------------------------------------------------------- /pkg/lintcontext/mocks/ingress.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 8 | networkingV1 "k8s.io/api/networking/v1" 9 | metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | ) 11 | 12 | func (l *MockLintContext) AddMockIngress(t *testing.T, name string) { 13 | require.NotEmpty(t, name) 14 | l.objects[name] = &networkingV1.Ingress{ 15 | TypeMeta: metaV1.TypeMeta{ 16 | Kind: objectkinds.Ingress, 17 | APIVersion: objectkinds.GetIngressAPIVersion(), 18 | }, 19 | ObjectMeta: metaV1.ObjectMeta{Name: name}, 20 | Spec: networkingV1.IngressSpec{}, 21 | } 22 | } 23 | 24 | // ModifyIngress modifies a given networkpolicy in the context via the passed function. 25 | func (l *MockLintContext) ModifyIngress(t *testing.T, name string, f func(ingress *networkingV1.Ingress)) { 26 | r, ok := l.objects[name].(*networkingV1.Ingress) 27 | require.True(t, ok) 28 | f(r) 29 | } 30 | -------------------------------------------------------------------------------- /pkg/lintcontext/mocks/networkpolicy.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 8 | networkingV1 "k8s.io/api/networking/v1" 9 | metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | ) 11 | 12 | // AddMockNetworkPolicy adds a mock NetworkPolicy to LintContext 13 | func (l *MockLintContext) AddMockNetworkPolicy(t *testing.T, name string) { 14 | require.NotEmpty(t, name) 15 | l.objects[name] = &networkingV1.NetworkPolicy{ 16 | TypeMeta: metaV1.TypeMeta{ 17 | Kind: objectkinds.NetworkPolicy, 18 | APIVersion: objectkinds.GetNetworkPolicyAPIVersion(), 19 | }, 20 | ObjectMeta: metaV1.ObjectMeta{Name: name}, 21 | Spec: networkingV1.NetworkPolicySpec{}, 22 | } 23 | } 24 | 25 | // ModifyNetworkPolicy modifies a given networkpolicy in the context via the passed function. 26 | func (l *MockLintContext) ModifyNetworkPolicy(t *testing.T, name string, f func(networkpolicy *networkingV1.NetworkPolicy)) { 27 | r, ok := l.objects[name].(*networkingV1.NetworkPolicy) 28 | require.True(t, ok) 29 | f(r) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/lintcontext/mocks/role.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 8 | rbacV1 "k8s.io/api/rbac/v1" 9 | metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | ) 11 | 12 | // AddMockRole adds a mock Role to LintContext 13 | func (l *MockLintContext) AddMockRole(t *testing.T, name, namespace string) { 14 | require.NotEmpty(t, name) 15 | l.objects[name] = &rbacV1.Role{ 16 | TypeMeta: metaV1.TypeMeta{ 17 | Kind: objectkinds.Role, 18 | APIVersion: objectkinds.GetRoleAPIVersion(), 19 | }, 20 | ObjectMeta: metaV1.ObjectMeta{Name: name, Namespace: namespace}, 21 | Rules: []rbacV1.PolicyRule{}, 22 | } 23 | } 24 | 25 | // ModifyRole modifies a given Role in the context via the passed function. 26 | func (l *MockLintContext) ModifyRole(t *testing.T, name string, f func(role *rbacV1.Role)) { 27 | r, ok := l.objects[name].(*rbacV1.Role) 28 | require.True(t, ok) 29 | f(r) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/lintcontext/mocks/rolebinding.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 8 | rbacV1 "k8s.io/api/rbac/v1" 9 | metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | ) 11 | 12 | // AddMockRoleBinding adds a mock RoleBinding to LintContext 13 | func (l *MockLintContext) AddMockRoleBinding(t *testing.T, name, namespace string) { 14 | require.NotEmpty(t, name) 15 | l.objects[name] = &rbacV1.RoleBinding{ 16 | TypeMeta: metaV1.TypeMeta{ 17 | Kind: objectkinds.RoleBinding, 18 | APIVersion: objectkinds.GetRoleBindingAPIVersion(), 19 | }, 20 | ObjectMeta: metaV1.ObjectMeta{Name: name, Namespace: namespace}, 21 | Subjects: []rbacV1.Subject{}, 22 | RoleRef: rbacV1.RoleRef{}, 23 | } 24 | } 25 | 26 | // ModifyRoleBinding modifies a given RoleBinding in the context via the passed function. 27 | func (l *MockLintContext) ModifyRoleBinding(t *testing.T, name string, f func(rolebinding *rbacV1.RoleBinding)) { 28 | rb, ok := l.objects[name].(*rbacV1.RoleBinding) 29 | require.True(t, ok) 30 | f(rb) 31 | } 32 | -------------------------------------------------------------------------------- /pkg/lintcontext/mocks/scaledobject.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | kedaV1Alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1" 8 | "github.com/stretchr/testify/require" 9 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 10 | 11 | metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 | ) 13 | 14 | // AddMockScaledObject adds a mock ScaledObject to LintContext 15 | func (l *MockLintContext) AddMockScaledObject(t *testing.T, name, version string) { 16 | require.NotEmpty(t, name) 17 | switch version { 18 | case "v1alpha1": 19 | l.objects[name] = &kedaV1Alpha1.ScaledObject{ 20 | TypeMeta: metaV1.TypeMeta{ 21 | Kind: objectkinds.ScaledObject, 22 | APIVersion: objectkinds.GetScaledObjectAPIVersion(version), 23 | }, 24 | ObjectMeta: metaV1.ObjectMeta{Name: name}, 25 | Spec: kedaV1Alpha1.ScaledObjectSpec{}, 26 | } 27 | default: 28 | require.FailNow(t, fmt.Sprintf("Unknown scaled object version %s", version)) 29 | } 30 | } 31 | 32 | // ModifyScaledObjectV1Alpha1 modifies a given ScaledObject in the context via the passed function. 33 | func (l *MockLintContext) ModifyScaledObjectV1Alpha1(t *testing.T, name string, f func(hpa *kedaV1Alpha1.ScaledObject)) { 34 | r, ok := l.objects[name].(*kedaV1Alpha1.ScaledObject) 35 | require.True(t, ok) 36 | f(r) 37 | } 38 | -------------------------------------------------------------------------------- /pkg/lintcontext/mocks/scc.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "testing" 5 | 6 | ocpSecV1 "github.com/openshift/api/security/v1" 7 | "github.com/stretchr/testify/require" 8 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 9 | metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | ) 11 | 12 | // AddMockSecurityContextConstraints adds a mock SecurityContextConstraints to LintContext 13 | func (l *MockLintContext) AddMockSecurityContextConstraints(t *testing.T, name string, allowFlag bool) { 14 | require.NotEmpty(t, name) 15 | l.objects[name] = &ocpSecV1.SecurityContextConstraints{ 16 | TypeMeta: metaV1.TypeMeta{ 17 | Kind: objectkinds.SecurityContextConstraints, 18 | APIVersion: objectkinds.GetSCCAPIVersion(), 19 | }, 20 | ObjectMeta: metaV1.ObjectMeta{Name: name}, 21 | AllowPrivilegedContainer: allowFlag, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /pkg/lintcontext/mocks/service.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | coreV1 "k8s.io/api/core/v1" 8 | metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | // AddMockService adds a mock Service to LintContext 12 | func (l *MockLintContext) AddMockService(t *testing.T, name string) { 13 | require.NotEmpty(t, name) 14 | l.objects[name] = &coreV1.Service{ 15 | ObjectMeta: metaV1.ObjectMeta{Name: name}, 16 | } 17 | } 18 | 19 | // ModifyService modifies a given service in the context via the passed function 20 | func (l *MockLintContext) ModifyService(t *testing.T, name string, f func(service *coreV1.Service)) { 21 | dep, ok := l.objects[name].(*coreV1.Service) 22 | require.True(t, ok) 23 | f(dep) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/lintcontext/mocks/servicemonitor.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "testing" 5 | 6 | k8sMonitoring "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" 7 | "github.com/stretchr/testify/require" 8 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 9 | metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | ) 11 | 12 | // AddMockServiceMonitor adds a mock ServiceMonitor to LintContext 13 | func (l *MockLintContext) AddMockServiceMonitor(t *testing.T, name string) { 14 | require.NotEmpty(t, name) 15 | l.objects[name] = &k8sMonitoring.ServiceMonitor{ 16 | TypeMeta: metaV1.TypeMeta{ 17 | Kind: objectkinds.ServiceMonitor, 18 | APIVersion: objectkinds.GetServiceMonitorAPIVersion(), 19 | }, 20 | ObjectMeta: metaV1.ObjectMeta{Name: name}, 21 | Spec: k8sMonitoring.ServiceMonitorSpec{}, 22 | } 23 | } 24 | 25 | // ModifyServiceMonitor modifies a given servicemonitor in the context via the passed function 26 | func (l *MockLintContext) ModifyServiceMonitor(t *testing.T, name string, f func(servicemonitor *k8sMonitoring.ServiceMonitor)) { 27 | r, ok := l.objects[name].(*k8sMonitoring.ServiceMonitor) 28 | require.True(t, ok) 29 | f(r) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/matcher/string.go: -------------------------------------------------------------------------------- 1 | package matcher 2 | 3 | import ( 4 | "regexp" 5 | 6 | "golang.stackrox.io/kube-linter/internal/stringutils" 7 | ) 8 | 9 | const ( 10 | // NegationPrefix is the prefix used for negations. 11 | NegationPrefix = "!" 12 | ) 13 | 14 | func matchAny(_ string) bool { 15 | return true 16 | } 17 | 18 | // ForString constructs a string matcher for the given value. 19 | func ForString(value string) (func(string) bool, error) { 20 | if value == "" { 21 | return matchAny, nil 22 | } 23 | var negate bool 24 | if stringutils.ConsumePrefix(&value, NegationPrefix) { 25 | negate = true 26 | } 27 | re, err := regexp.Compile(value) 28 | if err != nil { 29 | return nil, err 30 | } 31 | return func(s string) bool { 32 | matched := re.MatchString(s) 33 | return matched != negate 34 | }, nil 35 | } 36 | -------------------------------------------------------------------------------- /pkg/objectkinds/any.go: -------------------------------------------------------------------------------- 1 | package objectkinds 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/runtime/schema" 5 | ) 6 | 7 | const ( 8 | // Any represents the ObjectKind that matches any object. 9 | Any = "Any" 10 | ) 11 | 12 | func init() { 13 | RegisterObjectKind(Any, MatcherFunc(func(gvk schema.GroupVersionKind) bool { 14 | return true 15 | })) 16 | } 17 | -------------------------------------------------------------------------------- /pkg/objectkinds/clusterrole.go: -------------------------------------------------------------------------------- 1 | package objectkinds 2 | 3 | import ( 4 | "fmt" 5 | 6 | rbacV1 "k8s.io/api/rbac/v1" 7 | "k8s.io/apimachinery/pkg/runtime/schema" 8 | ) 9 | 10 | const ( 11 | // ClusterRole represents Kubernetes ClusterRole objects. Case sensitive. 12 | ClusterRole = "ClusterRole" 13 | ) 14 | 15 | var ( 16 | clusterRoleGVK = rbacV1.SchemeGroupVersion.WithKind("ClusterRole") 17 | ) 18 | 19 | func init() { 20 | RegisterObjectKind(ClusterRole, MatcherFunc(func(gvk schema.GroupVersionKind) bool { 21 | return gvk == clusterRoleGVK 22 | })) 23 | } 24 | 25 | // GetClusterRoleAPIVersion returns ClusterRole's APIVersion 26 | func GetClusterRoleAPIVersion() string { 27 | return fmt.Sprintf("%s/%s", clusterRoleGVK.Group, clusterRoleGVK.Version) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/objectkinds/clusterrolebinding.go: -------------------------------------------------------------------------------- 1 | package objectkinds 2 | 3 | import ( 4 | rbacV1 "k8s.io/api/rbac/v1" 5 | "k8s.io/apimachinery/pkg/runtime/schema" 6 | ) 7 | 8 | const ( 9 | // ClusterRoleBinding represents Kubernetes ClusterRoleBinding objects. Case sensitive. 10 | ClusterRoleBinding = "ClusterRoleBinding" 11 | ) 12 | 13 | var ( 14 | clusterRoleBindingGVK = rbacV1.SchemeGroupVersion.WithKind("ClusterRoleBinding") 15 | ) 16 | 17 | func init() { 18 | RegisterObjectKind(ClusterRoleBinding, MatcherFunc(func(gvk schema.GroupVersionKind) bool { 19 | return gvk == clusterRoleBindingGVK 20 | })) 21 | } 22 | 23 | // GetClusterRoleBindingAPIVersion returns ClusterRoleBinding's APIVersion 24 | func GetClusterRoleBindingAPIVersion() string { 25 | return clusterRoleBindingGVK.GroupVersion().String() 26 | } 27 | -------------------------------------------------------------------------------- /pkg/objectkinds/deployment_like.go: -------------------------------------------------------------------------------- 1 | package objectkinds 2 | 3 | import ( 4 | "fmt" 5 | 6 | ocsAppsV1 "github.com/openshift/api/apps/v1" 7 | appsV1 "k8s.io/api/apps/v1" 8 | batchV1 "k8s.io/api/batch/v1" 9 | coreV1 "k8s.io/api/core/v1" 10 | "k8s.io/apimachinery/pkg/runtime/schema" 11 | ) 12 | 13 | var ( 14 | deploymentLikeGroupKinds = func() map[schema.GroupKind]struct{} { 15 | m := make(map[schema.GroupKind]struct{}) 16 | for _, gk := range []schema.GroupKind{ 17 | {Group: appsV1.GroupName, Kind: "Deployment"}, 18 | {Group: appsV1.GroupName, Kind: "DaemonSet"}, 19 | {Group: ocsAppsV1.GroupName, Kind: "DeploymentConfig"}, 20 | {Group: appsV1.GroupName, Kind: "StatefulSet"}, 21 | {Group: appsV1.GroupName, Kind: "ReplicaSet"}, 22 | {Group: coreV1.GroupName, Kind: "Pod"}, 23 | {Group: coreV1.GroupName, Kind: "ReplicationController"}, 24 | {Group: batchV1.GroupName, Kind: "Job"}, 25 | {Group: batchV1.GroupName, Kind: "CronJob"}, 26 | } { 27 | if _, ok := m[gk]; ok { 28 | panic(fmt.Sprintf("group kind double-registered: %v", gk)) 29 | } 30 | m[gk] = struct{}{} 31 | } 32 | return m 33 | }() 34 | ) 35 | 36 | func IsDeploymentLike(gvk schema.GroupVersionKind) bool { 37 | _, ok := deploymentLikeGroupKinds[gvk.GroupKind()] 38 | return ok 39 | } 40 | 41 | const ( 42 | // DeploymentLike is the name of the DeploymentLike ObjectKind. 43 | DeploymentLike = "DeploymentLike" 44 | ) 45 | 46 | func init() { 47 | RegisterObjectKind(DeploymentLike, MatcherFunc(IsDeploymentLike)) 48 | } 49 | -------------------------------------------------------------------------------- /pkg/objectkinds/horizontalpodautoscaler.go: -------------------------------------------------------------------------------- 1 | package objectkinds 2 | 3 | import ( 4 | "fmt" 5 | 6 | autoscalingV1 "k8s.io/api/autoscaling/v1" 7 | autoscalingV2 "k8s.io/api/autoscaling/v2" 8 | autoscalingV2Beta1 "k8s.io/api/autoscaling/v2beta1" 9 | autoscalingV2Beta2 "k8s.io/api/autoscaling/v2beta2" 10 | "k8s.io/apimachinery/pkg/runtime/schema" 11 | ) 12 | 13 | const ( 14 | // HorizontalPodAutoscaler represents Kubernetes HorizontalPodAutoscaler objects. Case sensitive. 15 | HorizontalPodAutoscaler = "HorizontalPodAutoscaler" 16 | ) 17 | 18 | var ( 19 | horizontalPodAutoscalerV2Beta1GVK = autoscalingV2Beta1.SchemeGroupVersion.WithKind(HorizontalPodAutoscaler) 20 | horizontalPodAutoscalerV2Beta2GVK = autoscalingV2Beta2.SchemeGroupVersion.WithKind(HorizontalPodAutoscaler) 21 | horizontalPodAutoscalerV2GVK = autoscalingV2.SchemeGroupVersion.WithKind(HorizontalPodAutoscaler) 22 | horizontalPodAutoscalerV1GVK = autoscalingV1.SchemeGroupVersion.WithKind(HorizontalPodAutoscaler) 23 | ) 24 | 25 | func isHorizontalPodAutoscaler(gvk schema.GroupVersionKind) bool { 26 | return gvk == horizontalPodAutoscalerV1GVK || 27 | gvk == horizontalPodAutoscalerV2GVK || 28 | gvk == horizontalPodAutoscalerV2Beta1GVK || 29 | gvk == horizontalPodAutoscalerV2Beta2GVK 30 | } 31 | 32 | func init() { 33 | RegisterObjectKind(HorizontalPodAutoscaler, MatcherFunc(isHorizontalPodAutoscaler)) 34 | } 35 | 36 | // GetHorizontalPodAutoscalerAPIVersion returns HorizontalPodAutoscaler's APIVersion 37 | func GetHorizontalPodAutoscalerAPIVersion(version string) string { 38 | return fmt.Sprintf("%s/%s", horizontalPodAutoscalerV2Beta1GVK.Group, version) 39 | } 40 | -------------------------------------------------------------------------------- /pkg/objectkinds/ingress.go: -------------------------------------------------------------------------------- 1 | package objectkinds 2 | 3 | import ( 4 | v1 "k8s.io/api/networking/v1" 5 | "k8s.io/apimachinery/pkg/runtime/schema" 6 | ) 7 | 8 | const ( 9 | // Ingress represents Kubernetes Ingress objects. 10 | Ingress = "Ingress" 11 | ) 12 | 13 | var ( 14 | ingressGVK = v1.SchemeGroupVersion.WithKind(Ingress) 15 | ) 16 | 17 | func init() { 18 | RegisterObjectKind(Ingress, MatcherFunc(func(gvk schema.GroupVersionKind) bool { 19 | return gvk == ingressGVK 20 | })) 21 | } 22 | 23 | // GetIngressAPIVersion returns Ingress's apiversion 24 | func GetIngressAPIVersion() string { 25 | return ingressGVK.GroupVersion().String() 26 | } 27 | -------------------------------------------------------------------------------- /pkg/objectkinds/networkpolicy.go: -------------------------------------------------------------------------------- 1 | package objectkinds 2 | 3 | import ( 4 | v1 "k8s.io/api/networking/v1" 5 | "k8s.io/apimachinery/pkg/runtime/schema" 6 | ) 7 | 8 | const ( 9 | // NetworkPolicy represents Kubernetes NetworkPolicy objects. 10 | NetworkPolicy = "NetworkPolicy" 11 | ) 12 | 13 | var ( 14 | networkpolicyGVK = v1.SchemeGroupVersion.WithKind("NetworkPolicy") 15 | ) 16 | 17 | func init() { 18 | RegisterObjectKind(NetworkPolicy, MatcherFunc(func(gvk schema.GroupVersionKind) bool { 19 | return gvk == networkpolicyGVK 20 | })) 21 | } 22 | 23 | // GetNetworkPolicyAPIVersion returns networkpolicy's apiversion 24 | func GetNetworkPolicyAPIVersion() string { 25 | return networkpolicyGVK.GroupVersion().String() 26 | } 27 | -------------------------------------------------------------------------------- /pkg/objectkinds/poddisruptionbudget.go: -------------------------------------------------------------------------------- 1 | package objectkinds 2 | 3 | import ( 4 | policyv1 "k8s.io/api/policy/v1" 5 | "k8s.io/apimachinery/pkg/runtime/schema" 6 | ) 7 | 8 | const ( 9 | // PodDisruptionBudget represents Kubernetes PodDisruptionBudget objects. 10 | PodDisruptionBudget = "PodDisruptionBudget" 11 | ) 12 | 13 | var ( 14 | pdbGVK = policyv1.SchemeGroupVersion.WithKind("PodDisruptionBudget") 15 | ) 16 | 17 | func init() { 18 | RegisterObjectKind(PodDisruptionBudget, MatcherFunc(func(gvk schema.GroupVersionKind) bool { 19 | return gvk == pdbGVK 20 | })) 21 | } 22 | 23 | // GetPodDisruptionBudgetAPIVersion returns pdb's apiversion 24 | func GetPodDisruptionBudgetAPIVersion() string { 25 | return pdbGVK.GroupVersion().String() 26 | } 27 | -------------------------------------------------------------------------------- /pkg/objectkinds/registry.go: -------------------------------------------------------------------------------- 1 | package objectkinds 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/pkg/errors" 7 | "k8s.io/apimachinery/pkg/runtime/schema" 8 | ) 9 | 10 | var ( 11 | allObjectKinds = make(map[string]Matcher) 12 | ) 13 | 14 | // RegisterObjectKind allows a matcher function to be registered for a given object kind 15 | func RegisterObjectKind(name string, objectKind Matcher) { 16 | if _, ok := allObjectKinds[name]; ok { 17 | panic(fmt.Sprintf("duplicate object kind: %v", name)) 18 | } 19 | allObjectKinds[name] = objectKind 20 | } 21 | 22 | // AllObjectKinds will return all the object kind names that are registered 23 | func AllObjectKinds() []string { 24 | kinds := make([]string, 0, len(allObjectKinds)) 25 | 26 | for k := range allObjectKinds { 27 | kinds = append(kinds, k) 28 | } 29 | return kinds 30 | } 31 | 32 | type orMatcher []Matcher 33 | 34 | func (o orMatcher) Matches(gvk schema.GroupVersionKind) bool { 35 | for _, m := range o { 36 | if m.Matches(gvk) { 37 | return true 38 | } 39 | } 40 | return false 41 | } 42 | 43 | // ConstructMatcher constructs a matcher that matches objects that fall 44 | // into one of the given object kinds. 45 | func ConstructMatcher(objectKinds ...string) (Matcher, error) { 46 | var matchers []Matcher 47 | for _, obj := range objectKinds { 48 | matcher := allObjectKinds[obj] 49 | if matcher == nil { 50 | return nil, errors.Errorf("unknown object kind: %v", obj) 51 | } 52 | matchers = append(matchers, matcher) 53 | } 54 | return orMatcher(matchers), nil 55 | } 56 | -------------------------------------------------------------------------------- /pkg/objectkinds/role.go: -------------------------------------------------------------------------------- 1 | package objectkinds 2 | 3 | import ( 4 | rbacV1 "k8s.io/api/rbac/v1" 5 | "k8s.io/apimachinery/pkg/runtime/schema" 6 | ) 7 | 8 | const ( 9 | // Role represents Kubernetes Role objects. Case sensitive. 10 | Role = "Role" 11 | ) 12 | 13 | var ( 14 | // roleGVK represents Kubernetes Role objects. Case sensitive. 15 | roleGVK = rbacV1.SchemeGroupVersion.WithKind("Role") 16 | ) 17 | 18 | func init() { 19 | RegisterObjectKind(Role, MatcherFunc(func(gvk schema.GroupVersionKind) bool { 20 | return gvk == roleGVK 21 | })) 22 | } 23 | 24 | // GetRoleAPIVersion returns Role's APIVersion 25 | func GetRoleAPIVersion() string { 26 | return roleGVK.GroupVersion().String() 27 | } 28 | -------------------------------------------------------------------------------- /pkg/objectkinds/rolebinding.go: -------------------------------------------------------------------------------- 1 | package objectkinds 2 | 3 | import ( 4 | rbacV1 "k8s.io/api/rbac/v1" 5 | "k8s.io/apimachinery/pkg/runtime/schema" 6 | ) 7 | 8 | const ( 9 | // RoleBinding represents Kubernetes RoleBinding objects. Case sensitive. 10 | RoleBinding = "RoleBinding" 11 | ) 12 | 13 | var ( 14 | roleBindingGVK = rbacV1.SchemeGroupVersion.WithKind("RoleBinding") 15 | ) 16 | 17 | func init() { 18 | RegisterObjectKind(RoleBinding, MatcherFunc(func(gvk schema.GroupVersionKind) bool { 19 | return gvk == roleBindingGVK 20 | })) 21 | } 22 | 23 | // GetRoleBindingAPIVersion returns RoleBinding's APIVersion 24 | func GetRoleBindingAPIVersion() string { 25 | return roleBindingGVK.GroupVersion().String() 26 | } 27 | -------------------------------------------------------------------------------- /pkg/objectkinds/scaledobject.go: -------------------------------------------------------------------------------- 1 | package objectkinds 2 | 3 | import ( 4 | "fmt" 5 | 6 | kedaV1Alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1" 7 | "k8s.io/apimachinery/pkg/runtime/schema" 8 | ) 9 | 10 | const ( 11 | // ScaledObject represents Kubernetes ScaledObject objects. Case sensitive. 12 | ScaledObject = "ScaledObject" 13 | ) 14 | 15 | var ( 16 | ScaledObjectV1Alpha1 = kedaV1Alpha1.SchemeGroupVersion.WithKind(ScaledObject) 17 | ) 18 | 19 | func isScaledObject(gvk schema.GroupVersionKind) bool { 20 | return gvk == ScaledObjectV1Alpha1 21 | } 22 | 23 | func init() { 24 | RegisterObjectKind(ScaledObject, MatcherFunc(isScaledObject)) 25 | } 26 | 27 | // GetScaledObjectAPIVersion returns ScaledObject's APIVersion 28 | func GetScaledObjectAPIVersion(version string) string { 29 | return fmt.Sprintf("%s/%s", ScaledObjectV1Alpha1.Group, version) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/objectkinds/securitycontext.go: -------------------------------------------------------------------------------- 1 | package objectkinds 2 | 3 | import ( 4 | ocpSecV1 "github.com/openshift/api/security/v1" 5 | "k8s.io/apimachinery/pkg/runtime/schema" 6 | ) 7 | 8 | const ( 9 | // Service represents Kubernetes Service objects. 10 | SecurityContextConstraints = "SecurityContextConstraints" 11 | ) 12 | 13 | var ( 14 | sccGVK = ocpSecV1.SchemeGroupVersion.WithKind("SecurityContextConstraints") 15 | ) 16 | 17 | func init() { 18 | RegisterObjectKind(SecurityContextConstraints, MatcherFunc(func(gvk schema.GroupVersionKind) bool { 19 | return gvk == sccGVK 20 | })) 21 | } 22 | 23 | // GetSCCAPIVersion returns SCC's apiversion 24 | func GetSCCAPIVersion() string { 25 | return sccGVK.GroupVersion().String() 26 | } 27 | -------------------------------------------------------------------------------- /pkg/objectkinds/service.go: -------------------------------------------------------------------------------- 1 | package objectkinds 2 | 3 | import ( 4 | v1 "k8s.io/api/core/v1" 5 | "k8s.io/apimachinery/pkg/runtime/schema" 6 | ) 7 | 8 | const ( 9 | // Service represents Kubernetes Service objects. 10 | Service = "Service" 11 | ) 12 | 13 | var ( 14 | serviceGVK = v1.SchemeGroupVersion.WithKind("Service") 15 | ) 16 | 17 | func init() { 18 | RegisterObjectKind(Service, MatcherFunc(func(gvk schema.GroupVersionKind) bool { 19 | return gvk == serviceGVK 20 | })) 21 | } 22 | -------------------------------------------------------------------------------- /pkg/objectkinds/serviceMonitor.go: -------------------------------------------------------------------------------- 1 | package objectkinds 2 | 3 | import ( 4 | k8sMonitoring "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" 5 | "k8s.io/apimachinery/pkg/runtime/schema" 6 | ) 7 | 8 | const ( 9 | // ServiceMonitor represents Prometheus Service Monitor objects. 10 | ServiceMonitor = k8sMonitoring.ServiceMonitorsKind 11 | ) 12 | 13 | var ( 14 | serviceMonitorGVK = k8sMonitoring.SchemeGroupVersion.WithKind(ServiceMonitor) 15 | ) 16 | 17 | func init() { 18 | RegisterObjectKind(ServiceMonitor, MatcherFunc(func(gvk schema.GroupVersionKind) bool { 19 | return gvk == serviceMonitorGVK 20 | })) 21 | } 22 | 23 | // GetServiceMonitorAPIVersion returns servicemonitor's apiversion 24 | func GetServiceMonitorAPIVersion() string { 25 | return serviceMonitorGVK.GroupVersion().String() 26 | } 27 | -------------------------------------------------------------------------------- /pkg/objectkinds/serviceaccount.go: -------------------------------------------------------------------------------- 1 | package objectkinds 2 | 3 | import ( 4 | v1 "k8s.io/api/core/v1" 5 | "k8s.io/apimachinery/pkg/runtime/schema" 6 | ) 7 | 8 | const ( 9 | // ServiceAccount represents Kubernetes ServiceAccount objects. 10 | ServiceAccount = "ServiceAccount" 11 | ) 12 | 13 | var ( 14 | serviceAccountGVK = v1.SchemeGroupVersion.WithKind("ServiceAccount") 15 | ) 16 | 17 | func init() { 18 | RegisterObjectKind(ServiceAccount, MatcherFunc(func(gvk schema.GroupVersionKind) bool { 19 | return gvk == serviceAccountGVK 20 | })) 21 | } 22 | -------------------------------------------------------------------------------- /pkg/objectkinds/types.go: -------------------------------------------------------------------------------- 1 | package objectkinds 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/runtime/schema" 5 | ) 6 | 7 | // A Matcher selects a certain subset of GVKs. 8 | type Matcher interface { 9 | Matches(gvk schema.GroupVersionKind) bool 10 | } 11 | 12 | // MatcherFunc takes in a GVK and decides if it matches an object kind 13 | type MatcherFunc func(gvk schema.GroupVersionKind) bool 14 | 15 | func (f MatcherFunc) Matches(gvk schema.GroupVersionKind) bool { 16 | return f(gvk) 17 | } 18 | -------------------------------------------------------------------------------- /pkg/pathutil/path.go: -------------------------------------------------------------------------------- 1 | package pathutil 2 | 3 | import ( 4 | "path/filepath" 5 | 6 | "github.com/mitchellh/go-homedir" 7 | "github.com/pkg/errors" 8 | ) 9 | 10 | // GetAbsolutPath returns the absolute representation of given path. 11 | func GetAbsolutPath(path string) (string, error) { 12 | switch { 13 | case path[0] == '~': 14 | expandedPath, err := homedir.Expand(path) 15 | if err != nil { 16 | return "", errors.Wrapf(err, "could not expand path: %q", expandedPath) 17 | } 18 | return expandedPath, nil 19 | case !filepath.IsAbs(path): 20 | absPath, err := filepath.Abs(path) 21 | if err != nil { 22 | return "", errors.Wrapf(err, "could not expand non-absolute path: %q", absPath) 23 | } 24 | return absPath, nil 25 | default: 26 | return path, nil 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /pkg/templates/accesstoresources/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | // Set to true to flag the roles that are referenced in bindings but not found in the context 6 | FlagRolesNotFound bool `json:"flagRolesNotFound"` 7 | // An array of regular expressions specifying resources. e.g. ^secrets$ for secrets and ^*$ for any resources 8 | // +notnegatable 9 | Resources []string `json:"resources"` 10 | // An array of regular expressions specifying verbs. e.g. ^create$ for create and ^*$ for any k8s verbs 11 | // +notnegatable 12 | Verbs []string `json:"verbs"` 13 | } 14 | -------------------------------------------------------------------------------- /pkg/templates/all/all_test.go: -------------------------------------------------------------------------------- 1 | package all 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "golang.stackrox.io/kube-linter/pkg/templates" 8 | ) 9 | 10 | func TestTemplatesAreValid(t *testing.T) { 11 | for _, template := range templates.List() { 12 | t.Run(template.HumanName, func(t *testing.T) { 13 | assert.NotEmpty(t, template.HumanName, "human name") 14 | assert.NotEmpty(t, template.Key, "name") 15 | assert.NotEmpty(t, template.Description, "description") 16 | assert.NotNil(t, template.ParseAndValidateParams, "parse and validate params") 17 | assert.NotNil(t, template.Parameters, "params") // We want people to use the generated code and explicitly set it to an empty list. 18 | assert.NotNil(t, template.Instantiate, "instantiate") 19 | }) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /pkg/templates/antiaffinity/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | 6 | // The minimum number of replicas a deployment must have before anti-affinity is enforced on it 7 | MinReplicas int 8 | 9 | // The topology key that the anti-affinity term should use. 10 | // If not specified, it defaults to "kubernetes.io/hostname". 11 | TopologyKey string 12 | } 13 | -------------------------------------------------------------------------------- /pkg/templates/clusteradminrolebinding/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/containercapabilities/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | 6 | // List of capabilities that needs to be removed from containers. 7 | // +noregex 8 | // +notnegatable 9 | ForbiddenCapabilities []string `json:"forbiddenCapabilities"` 10 | 11 | // List of capabilities that are exceptions to the above list. This should only be filled 12 | // when the above contains "all", and is used to forgive capabilities in ADD list. 13 | // +noregex 14 | // +notnegatable 15 | Exceptions []string `json:"exceptions"` 16 | } 17 | -------------------------------------------------------------------------------- /pkg/templates/cpurequirements/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | 6 | // The type of requirement. Use any to apply to both requests and limits. 7 | // +enum=request 8 | // +enum=limit 9 | // +enum=any 10 | // +required 11 | RequirementsType string 12 | 13 | // The lower bound of the requirement (inclusive), specified as 14 | // a number of milli-cores. 15 | // If not specified, it is treated as a lower bound of zero. 16 | LowerBoundMillis int `json:"lowerBoundMillis"` 17 | 18 | // The upper bound of the requirement (inclusive), specified as 19 | // a number of milli-cores. 20 | // If not specified, it is treated as "no upper bound". 21 | UpperBoundMillis *int `json:"upperBoundMillis"` 22 | } 23 | -------------------------------------------------------------------------------- /pkg/templates/danglinghpa/internal/params/gen-params.go: -------------------------------------------------------------------------------- 1 | // Code generated by kube-linter template codegen. DO NOT EDIT. 2 | // +build !templatecodegen 3 | 4 | package params 5 | 6 | import ( 7 | "fmt" 8 | "strings" 9 | 10 | "github.com/pkg/errors" 11 | "golang.stackrox.io/kube-linter/pkg/check" 12 | "golang.stackrox.io/kube-linter/pkg/templates/util" 13 | ) 14 | 15 | var ( 16 | // Use some imports in case they don't get used otherwise. 17 | _ = util.MustParseParameterDesc 18 | _ = fmt.Sprintf 19 | 20 | ParamDescs = []check.ParameterDesc{ 21 | } 22 | ) 23 | 24 | func (p *Params) Validate() error { 25 | var validationErrors []string 26 | if len(validationErrors) > 0 { 27 | return errors.Errorf("invalid parameters: %s", strings.Join(validationErrors, ", ")) 28 | } 29 | return nil 30 | } 31 | 32 | // ParseAndValidate instantiates a Params object out of the passed map[string]interface{}, 33 | // validates it, and returns it. 34 | // The return type is interface{} to satisfy the type in the Template struct. 35 | func ParseAndValidate(m map[string]interface{}) (interface{}, error) { 36 | var p Params 37 | if err := util.DecodeMapStructure(m, &p); err != nil { 38 | return nil, err 39 | } 40 | if err := p.Validate(); err != nil { 41 | return nil, err 42 | } 43 | return p, nil 44 | } 45 | 46 | // WrapInstantiateFunc is a convenience wrapper that wraps an untyped instantiate function 47 | // into a typed one. 48 | func WrapInstantiateFunc(f func(p Params) (check.Func, error)) func (interface{}) (check.Func, error) { 49 | return func(paramsInt interface{}) (check.Func, error) { 50 | return f(paramsInt.(Params)) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pkg/templates/danglinghpa/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/danglingingress/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/danglingnetworkpolicy/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/danglingnetworkpolicypeer/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/danglingservice/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | // A list of labels that will not cause the check to fail. For example, a label that is known to be populated at runtime by Kubernetes. 6 | IgnoredLabels []string `json:"ignoredLabels"` 7 | } 8 | -------------------------------------------------------------------------------- /pkg/templates/danglingservicemonitor/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/deprecatedserviceaccount/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/disallowedgvk/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | 6 | // The disallowed object group. 7 | // +example=apps 8 | Group string `json:"group"` 9 | 10 | // The disallowed object API version. 11 | // +example=v1 12 | // +example=v1beta1 13 | Version string 14 | 15 | // The disallowed kind. 16 | // +example=Deployment 17 | // +example=DaemonSet 18 | Kind string 19 | } 20 | -------------------------------------------------------------------------------- /pkg/templates/dnsconfigoptions/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | 6 | // Key of the dnsConfig option. 7 | Key string 8 | 9 | // Value of the dnsConfig option. 10 | Value string 11 | } 12 | -------------------------------------------------------------------------------- /pkg/templates/duplicatenvvar/internal/params/gen-params.go: -------------------------------------------------------------------------------- 1 | // Code generated by kube-linter template codegen. DO NOT EDIT. 2 | // +build !templatecodegen 3 | 4 | package params 5 | 6 | import ( 7 | "fmt" 8 | "strings" 9 | 10 | "github.com/pkg/errors" 11 | "golang.stackrox.io/kube-linter/pkg/check" 12 | "golang.stackrox.io/kube-linter/pkg/templates/util" 13 | ) 14 | 15 | var ( 16 | // Use some imports in case they don't get used otherwise. 17 | _ = util.MustParseParameterDesc 18 | _ = fmt.Sprintf 19 | 20 | ParamDescs = []check.ParameterDesc{ 21 | } 22 | ) 23 | 24 | func (p *Params) Validate() error { 25 | var validationErrors []string 26 | if len(validationErrors) > 0 { 27 | return errors.Errorf("invalid parameters: %s", strings.Join(validationErrors, ", ")) 28 | } 29 | return nil 30 | } 31 | 32 | // ParseAndValidate instantiates a Params object out of the passed map[string]interface{}, 33 | // validates it, and returns it. 34 | // The return type is interface{} to satisfy the type in the Template struct. 35 | func ParseAndValidate(m map[string]interface{}) (interface{}, error) { 36 | var p Params 37 | if err := util.DecodeMapStructure(m, &p); err != nil { 38 | return nil, err 39 | } 40 | if err := p.Validate(); err != nil { 41 | return nil, err 42 | } 43 | return p, nil 44 | } 45 | 46 | // WrapInstantiateFunc is a convenience wrapper that wraps an untyped instantiate function 47 | // into a typed one. 48 | func WrapInstantiateFunc(f func(p Params) (check.Func, error)) func (interface{}) (check.Func, error) { 49 | return func(paramsInt interface{}) (check.Func, error) { 50 | return f(paramsInt.(Params)) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pkg/templates/duplicatenvvar/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct{} 5 | -------------------------------------------------------------------------------- /pkg/templates/envvar/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | 6 | // The name of the environment variable. 7 | // +required 8 | Name string 9 | 10 | // The value of the environment variable. 11 | Value string 12 | } 13 | -------------------------------------------------------------------------------- /pkg/templates/forbiddenannotation/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | 6 | // Key of the forbidden annotation. 7 | // +required 8 | Key string 9 | 10 | // Value of the forbidden annotation. 11 | Value string 12 | } 13 | -------------------------------------------------------------------------------- /pkg/templates/forbiddenannotation/template.go: -------------------------------------------------------------------------------- 1 | package forbiddenannotation 2 | 3 | import ( 4 | "golang.stackrox.io/kube-linter/pkg/check" 5 | "golang.stackrox.io/kube-linter/pkg/config" 6 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 7 | "golang.stackrox.io/kube-linter/pkg/templates" 8 | "golang.stackrox.io/kube-linter/pkg/templates/forbiddenannotation/internal/params" 9 | "golang.stackrox.io/kube-linter/pkg/templates/util" 10 | ) 11 | 12 | func init() { 13 | templates.Register(check.Template{ 14 | HumanName: "Forbidden Annotation", 15 | Key: "forbidden-annotation", 16 | Description: "Flag objects carrying at least one annotation matching the provided patterns", 17 | SupportedObjectKinds: config.ObjectKindsDesc{ 18 | ObjectKinds: []string{objectkinds.Any}, 19 | }, 20 | Parameters: params.ParamDescs, 21 | ParseAndValidateParams: params.ParseAndValidate, 22 | Instantiate: params.WrapInstantiateFunc(func(p params.Params) (check.Func, error) { 23 | return util.ConstructForbiddenMapMatcher(p.Key, p.Value, "annotation") 24 | }), 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/templates/gen.go: -------------------------------------------------------------------------------- 1 | package templates 2 | 3 | //go:generate go run ./codegen 4 | -------------------------------------------------------------------------------- /pkg/templates/hostipc/internal/params/gen-params.go: -------------------------------------------------------------------------------- 1 | // Code generated by kube-linter template codegen. DO NOT EDIT. 2 | // +build !templatecodegen 3 | 4 | package params 5 | 6 | import ( 7 | "fmt" 8 | "strings" 9 | 10 | "github.com/pkg/errors" 11 | "golang.stackrox.io/kube-linter/pkg/check" 12 | "golang.stackrox.io/kube-linter/pkg/templates/util" 13 | ) 14 | 15 | var ( 16 | // Use some imports in case they don't get used otherwise. 17 | _ = util.MustParseParameterDesc 18 | _ = fmt.Sprintf 19 | 20 | ParamDescs = []check.ParameterDesc{ 21 | } 22 | ) 23 | 24 | func (p *Params) Validate() error { 25 | var validationErrors []string 26 | if len(validationErrors) > 0 { 27 | return errors.Errorf("invalid parameters: %s", strings.Join(validationErrors, ", ")) 28 | } 29 | return nil 30 | } 31 | 32 | // ParseAndValidate instantiates a Params object out of the passed map[string]interface{}, 33 | // validates it, and returns it. 34 | // The return type is interface{} to satisfy the type in the Template struct. 35 | func ParseAndValidate(m map[string]interface{}) (interface{}, error) { 36 | var p Params 37 | if err := util.DecodeMapStructure(m, &p); err != nil { 38 | return nil, err 39 | } 40 | if err := p.Validate(); err != nil { 41 | return nil, err 42 | } 43 | return p, nil 44 | } 45 | 46 | // WrapInstantiateFunc is a convenience wrapper that wraps an untyped instantiate function 47 | // into a typed one. 48 | func WrapInstantiateFunc(f func(p Params) (check.Func, error)) func (interface{}) (check.Func, error) { 49 | return func(paramsInt interface{}) (check.Func, error) { 50 | return f(paramsInt.(Params)) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pkg/templates/hostipc/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/hostipc/template.go: -------------------------------------------------------------------------------- 1 | package hostipc 2 | 3 | import ( 4 | "golang.stackrox.io/kube-linter/pkg/check" 5 | "golang.stackrox.io/kube-linter/pkg/config" 6 | "golang.stackrox.io/kube-linter/pkg/diagnostic" 7 | "golang.stackrox.io/kube-linter/pkg/extract" 8 | "golang.stackrox.io/kube-linter/pkg/lintcontext" 9 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 10 | "golang.stackrox.io/kube-linter/pkg/templates" 11 | "golang.stackrox.io/kube-linter/pkg/templates/hostipc/internal/params" 12 | ) 13 | 14 | func init() { 15 | templates.Register(check.Template{ 16 | HumanName: "Host IPC", 17 | Key: "host-ipc", 18 | Description: "Flag Pod sharing host's IPC namespace", 19 | SupportedObjectKinds: config.ObjectKindsDesc{ 20 | ObjectKinds: []string{objectkinds.DeploymentLike}, 21 | }, 22 | Parameters: params.ParamDescs, 23 | ParseAndValidateParams: params.ParseAndValidate, 24 | Instantiate: params.WrapInstantiateFunc(func(_ params.Params) (check.Func, error) { 25 | return func(_ lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic { 26 | podSpec, found := extract.PodSpec(object.K8sObject) 27 | if !found { 28 | return nil 29 | } 30 | if podSpec.HostIPC { 31 | return []diagnostic.Diagnostic{{Message: "resource shares host's IPC namespace (via hostIPC=true)."}} 32 | } 33 | return nil 34 | }, nil 35 | }), 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /pkg/templates/hostmounts/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | // An array of regular expressions specifying system directories to be mounted on containers. e.g. ^/usr$ for /usr 6 | // +notnegatable 7 | Dirs []string `json:"dirs"` 8 | } 9 | -------------------------------------------------------------------------------- /pkg/templates/hostnetwork/internal/params/gen-params.go: -------------------------------------------------------------------------------- 1 | // Code generated by kube-linter template codegen. DO NOT EDIT. 2 | // +build !templatecodegen 3 | 4 | package params 5 | 6 | import ( 7 | "fmt" 8 | "strings" 9 | 10 | "github.com/pkg/errors" 11 | "golang.stackrox.io/kube-linter/pkg/check" 12 | "golang.stackrox.io/kube-linter/pkg/templates/util" 13 | ) 14 | 15 | var ( 16 | // Use some imports in case they don't get used otherwise. 17 | _ = util.MustParseParameterDesc 18 | _ = fmt.Sprintf 19 | 20 | ParamDescs = []check.ParameterDesc{ 21 | } 22 | ) 23 | 24 | func (p *Params) Validate() error { 25 | var validationErrors []string 26 | if len(validationErrors) > 0 { 27 | return errors.Errorf("invalid parameters: %s", strings.Join(validationErrors, ", ")) 28 | } 29 | return nil 30 | } 31 | 32 | // ParseAndValidate instantiates a Params object out of the passed map[string]interface{}, 33 | // validates it, and returns it. 34 | // The return type is interface{} to satisfy the type in the Template struct. 35 | func ParseAndValidate(m map[string]interface{}) (interface{}, error) { 36 | var p Params 37 | if err := util.DecodeMapStructure(m, &p); err != nil { 38 | return nil, err 39 | } 40 | if err := p.Validate(); err != nil { 41 | return nil, err 42 | } 43 | return p, nil 44 | } 45 | 46 | // WrapInstantiateFunc is a convenience wrapper that wraps an untyped instantiate function 47 | // into a typed one. 48 | func WrapInstantiateFunc(f func(p Params) (check.Func, error)) func (interface{}) (check.Func, error) { 49 | return func(paramsInt interface{}) (check.Func, error) { 50 | return f(paramsInt.(Params)) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pkg/templates/hostnetwork/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/hostnetwork/template.go: -------------------------------------------------------------------------------- 1 | package hostnetwork 2 | 3 | import ( 4 | "golang.stackrox.io/kube-linter/pkg/check" 5 | "golang.stackrox.io/kube-linter/pkg/config" 6 | "golang.stackrox.io/kube-linter/pkg/diagnostic" 7 | "golang.stackrox.io/kube-linter/pkg/extract" 8 | "golang.stackrox.io/kube-linter/pkg/lintcontext" 9 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 10 | "golang.stackrox.io/kube-linter/pkg/templates" 11 | "golang.stackrox.io/kube-linter/pkg/templates/hostnetwork/internal/params" 12 | ) 13 | 14 | func init() { 15 | templates.Register(check.Template{ 16 | HumanName: "Host Network", 17 | Key: "host-network", 18 | Description: "Flag Pod sharing host's network namespace", 19 | SupportedObjectKinds: config.ObjectKindsDesc{ 20 | ObjectKinds: []string{objectkinds.DeploymentLike}, 21 | }, 22 | Parameters: params.ParamDescs, 23 | ParseAndValidateParams: params.ParseAndValidate, 24 | Instantiate: params.WrapInstantiateFunc(func(_ params.Params) (check.Func, error) { 25 | return func(_ lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic { 26 | podSpec, found := extract.PodSpec(object.K8sObject) 27 | if !found { 28 | return nil 29 | } 30 | if podSpec.HostNetwork { 31 | return []diagnostic.Diagnostic{{Message: "resource shares host's network namespace (via hostNetwork=true)."}} 32 | } 33 | return nil 34 | }, nil 35 | }), 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /pkg/templates/hostpid/internal/params/gen-params.go: -------------------------------------------------------------------------------- 1 | // Code generated by kube-linter template codegen. DO NOT EDIT. 2 | // +build !templatecodegen 3 | 4 | package params 5 | 6 | import ( 7 | "fmt" 8 | "strings" 9 | 10 | "github.com/pkg/errors" 11 | "golang.stackrox.io/kube-linter/pkg/check" 12 | "golang.stackrox.io/kube-linter/pkg/templates/util" 13 | ) 14 | 15 | var ( 16 | // Use some imports in case they don't get used otherwise. 17 | _ = util.MustParseParameterDesc 18 | _ = fmt.Sprintf 19 | 20 | ParamDescs = []check.ParameterDesc{ 21 | } 22 | ) 23 | 24 | func (p *Params) Validate() error { 25 | var validationErrors []string 26 | if len(validationErrors) > 0 { 27 | return errors.Errorf("invalid parameters: %s", strings.Join(validationErrors, ", ")) 28 | } 29 | return nil 30 | } 31 | 32 | // ParseAndValidate instantiates a Params object out of the passed map[string]interface{}, 33 | // validates it, and returns it. 34 | // The return type is interface{} to satisfy the type in the Template struct. 35 | func ParseAndValidate(m map[string]interface{}) (interface{}, error) { 36 | var p Params 37 | if err := util.DecodeMapStructure(m, &p); err != nil { 38 | return nil, err 39 | } 40 | if err := p.Validate(); err != nil { 41 | return nil, err 42 | } 43 | return p, nil 44 | } 45 | 46 | // WrapInstantiateFunc is a convenience wrapper that wraps an untyped instantiate function 47 | // into a typed one. 48 | func WrapInstantiateFunc(f func(p Params) (check.Func, error)) func (interface{}) (check.Func, error) { 49 | return func(paramsInt interface{}) (check.Func, error) { 50 | return f(paramsInt.(Params)) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pkg/templates/hostpid/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/hostpid/template.go: -------------------------------------------------------------------------------- 1 | package hostpid 2 | 3 | import ( 4 | "golang.stackrox.io/kube-linter/pkg/check" 5 | "golang.stackrox.io/kube-linter/pkg/config" 6 | "golang.stackrox.io/kube-linter/pkg/diagnostic" 7 | "golang.stackrox.io/kube-linter/pkg/extract" 8 | "golang.stackrox.io/kube-linter/pkg/lintcontext" 9 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 10 | "golang.stackrox.io/kube-linter/pkg/templates" 11 | "golang.stackrox.io/kube-linter/pkg/templates/hostpid/internal/params" 12 | ) 13 | 14 | func init() { 15 | templates.Register(check.Template{ 16 | HumanName: "Host PID", 17 | Key: "host-pid", 18 | Description: "Flag Pod sharing host's process namespace", 19 | SupportedObjectKinds: config.ObjectKindsDesc{ 20 | ObjectKinds: []string{objectkinds.DeploymentLike}, 21 | }, 22 | Parameters: params.ParamDescs, 23 | ParseAndValidateParams: params.ParseAndValidate, 24 | Instantiate: params.WrapInstantiateFunc(func(_ params.Params) (check.Func, error) { 25 | return func(_ lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic { 26 | podSpec, found := extract.PodSpec(object.K8sObject) 27 | if !found { 28 | return nil 29 | } 30 | if podSpec.HostPID { 31 | return []diagnostic.Diagnostic{{Message: "object shares the host's process namespace (via hostPID=true)."}} 32 | } 33 | return nil 34 | }, nil 35 | }), 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /pkg/templates/hpareplicas/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | 6 | // The minimum number of replicas a HorizontalPodAutoscaler should have 7 | MinReplicas int 8 | } 9 | -------------------------------------------------------------------------------- /pkg/templates/imagepullpolicy/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | // list of forbidden image pull policy 6 | // +noregex 7 | // +notnegatable 8 | // +enum=Always 9 | // +enum=IfNotPresent 10 | // +enum=Never 11 | ForbiddenPolicies []string 12 | } 13 | -------------------------------------------------------------------------------- /pkg/templates/imagepullpolicy/template.go: -------------------------------------------------------------------------------- 1 | package imagepullpolicy 2 | 3 | import ( 4 | "fmt" 5 | 6 | "golang.stackrox.io/kube-linter/internal/set" 7 | "golang.stackrox.io/kube-linter/pkg/check" 8 | "golang.stackrox.io/kube-linter/pkg/config" 9 | "golang.stackrox.io/kube-linter/pkg/diagnostic" 10 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 11 | "golang.stackrox.io/kube-linter/pkg/templates" 12 | "golang.stackrox.io/kube-linter/pkg/templates/imagepullpolicy/internal/params" 13 | "golang.stackrox.io/kube-linter/pkg/templates/util" 14 | v1 "k8s.io/api/core/v1" 15 | ) 16 | 17 | const ( 18 | templateKey = "image-pull-policy" 19 | ) 20 | 21 | func init() { 22 | templates.Register(check.Template{ 23 | HumanName: "Image Pull Policy", 24 | Key: templateKey, 25 | Description: "Flag containers with forbidden image pull policy", 26 | SupportedObjectKinds: config.ObjectKindsDesc{ 27 | ObjectKinds: []string{objectkinds.DeploymentLike}, 28 | }, 29 | Parameters: params.ParamDescs, 30 | ParseAndValidateParams: params.ParseAndValidate, 31 | Instantiate: params.WrapInstantiateFunc(func(p params.Params) (check.Func, error) { 32 | forbiddenPolicies := set.NewStringSet(p.ForbiddenPolicies...) 33 | return util.PerContainerCheck(func(container *v1.Container) []diagnostic.Diagnostic { 34 | if forbiddenPolicies.Contains(string(container.ImagePullPolicy)) { 35 | return []diagnostic.Diagnostic{{Message: fmt.Sprintf("container %q has imagePullPolicy set to %s", container.Name, container.ImagePullPolicy)}} 36 | } 37 | return nil 38 | }), nil 39 | }), 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /pkg/templates/latesttag/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | 6 | // list of regular expressions specifying pattern(s) for container images that will be blocked. */ 7 | BlockList []string 8 | 9 | // list of regular expressions specifying pattern(s) for container images that will be allowed. 10 | AllowList []string 11 | } 12 | -------------------------------------------------------------------------------- /pkg/templates/livenessport/internal/params/gen-params.go: -------------------------------------------------------------------------------- 1 | // Code generated by kube-linter template codegen. DO NOT EDIT. 2 | // +build !templatecodegen 3 | 4 | package params 5 | 6 | import ( 7 | "fmt" 8 | "strings" 9 | 10 | "github.com/pkg/errors" 11 | "golang.stackrox.io/kube-linter/pkg/check" 12 | "golang.stackrox.io/kube-linter/pkg/templates/util" 13 | ) 14 | 15 | var ( 16 | // Use some imports in case they don't get used otherwise. 17 | _ = util.MustParseParameterDesc 18 | _ = fmt.Sprintf 19 | 20 | ParamDescs = []check.ParameterDesc{ 21 | } 22 | ) 23 | 24 | func (p *Params) Validate() error { 25 | var validationErrors []string 26 | if len(validationErrors) > 0 { 27 | return errors.Errorf("invalid parameters: %s", strings.Join(validationErrors, ", ")) 28 | } 29 | return nil 30 | } 31 | 32 | // ParseAndValidate instantiates a Params object out of the passed map[string]interface{}, 33 | // validates it, and returns it. 34 | // The return type is interface{} to satisfy the type in the Template struct. 35 | func ParseAndValidate(m map[string]interface{}) (interface{}, error) { 36 | var p Params 37 | if err := util.DecodeMapStructure(m, &p); err != nil { 38 | return nil, err 39 | } 40 | if err := p.Validate(); err != nil { 41 | return nil, err 42 | } 43 | return p, nil 44 | } 45 | 46 | // WrapInstantiateFunc is a convenience wrapper that wraps an untyped instantiate function 47 | // into a typed one. 48 | func WrapInstantiateFunc(f func(p Params) (check.Func, error)) func (interface{}) (check.Func, error) { 49 | return func(paramsInt interface{}) (check.Func, error) { 50 | return f(paramsInt.(Params)) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pkg/templates/livenessport/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct{} 5 | -------------------------------------------------------------------------------- /pkg/templates/livenessport/template.go: -------------------------------------------------------------------------------- 1 | package livenessport 2 | 3 | import ( 4 | "golang.stackrox.io/kube-linter/pkg/check" 5 | "golang.stackrox.io/kube-linter/pkg/config" 6 | "golang.stackrox.io/kube-linter/pkg/diagnostic" 7 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 8 | "golang.stackrox.io/kube-linter/pkg/templates" 9 | "golang.stackrox.io/kube-linter/pkg/templates/livenessport/internal/params" 10 | "golang.stackrox.io/kube-linter/pkg/templates/util" 11 | v1 "k8s.io/api/core/v1" 12 | ) 13 | 14 | const templateKey = "liveness-port" 15 | 16 | func init() { 17 | templates.Register(check.Template{ 18 | HumanName: "Liveness Port Exposed", 19 | Key: templateKey, 20 | Description: "Flag containers with an liveness probe to not exposed port.", 21 | SupportedObjectKinds: config.ObjectKindsDesc{ 22 | ObjectKinds: []string{objectkinds.DeploymentLike}, 23 | }, 24 | Parameters: params.ParamDescs, 25 | ParseAndValidateParams: params.ParseAndValidate, 26 | Instantiate: params.WrapInstantiateFunc(func(_ params.Params) (check.Func, error) { 27 | return util.PerNonInitContainerCheck(func(container *v1.Container) []diagnostic.Diagnostic { 28 | return util.CheckProbePort(container, container.LivenessProbe) 29 | }), nil 30 | }), 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /pkg/templates/livenessprobe/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/livenessprobe/template.go: -------------------------------------------------------------------------------- 1 | package livenessprobe 2 | 3 | import ( 4 | "fmt" 5 | 6 | "golang.stackrox.io/kube-linter/pkg/check" 7 | "golang.stackrox.io/kube-linter/pkg/config" 8 | "golang.stackrox.io/kube-linter/pkg/diagnostic" 9 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 10 | "golang.stackrox.io/kube-linter/pkg/templates" 11 | "golang.stackrox.io/kube-linter/pkg/templates/livenessprobe/internal/params" 12 | "golang.stackrox.io/kube-linter/pkg/templates/util" 13 | v1 "k8s.io/api/core/v1" 14 | ) 15 | 16 | func init() { 17 | templates.Register(check.Template{ 18 | HumanName: "Liveness Probe Not Specified", 19 | Key: "liveness-probe", 20 | Description: "Flag containers that don't specify a liveness probe", 21 | SupportedObjectKinds: config.ObjectKindsDesc{ 22 | ObjectKinds: []string{objectkinds.DeploymentLike}, 23 | }, 24 | Parameters: params.ParamDescs, 25 | 26 | ParseAndValidateParams: params.ParseAndValidate, 27 | Instantiate: params.WrapInstantiateFunc(func(_ params.Params) (check.Func, error) { 28 | return util.PerNonInitContainerCheck(func(container *v1.Container) []diagnostic.Diagnostic { 29 | if container.LivenessProbe == nil { 30 | return []diagnostic.Diagnostic{{Message: fmt.Sprintf("container %q does not specify a liveness probe", container.Name)}} 31 | } 32 | return nil 33 | }), nil 34 | }), 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /pkg/templates/memoryrequirements/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | 6 | // The type of requirement. Use any to apply to both requests and limits. 7 | // +enum=request 8 | // +enum=limit 9 | // +enum=any 10 | // +required 11 | RequirementsType string 12 | 13 | // The lower bound of the requirement (inclusive), specified as 14 | // a number of MB. 15 | LowerBoundMB int `json:"lowerBoundMB"` 16 | 17 | // The upper bound of the requirement (inclusive), specified as 18 | // a number of MB. 19 | // If not specified, it is treated as "no upper bound". 20 | UpperBoundMB *int `json:"upperBoundMB"` 21 | } 22 | -------------------------------------------------------------------------------- /pkg/templates/mismatchingselector/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/namespace/internal/params/gen-params.go: -------------------------------------------------------------------------------- 1 | // Code generated by kube-linter template codegen. DO NOT EDIT. 2 | // +build !templatecodegen 3 | 4 | package params 5 | 6 | import ( 7 | "fmt" 8 | "strings" 9 | 10 | "github.com/pkg/errors" 11 | "golang.stackrox.io/kube-linter/pkg/check" 12 | "golang.stackrox.io/kube-linter/pkg/templates/util" 13 | ) 14 | 15 | var ( 16 | // Use some imports in case they don't get used otherwise. 17 | _ = util.MustParseParameterDesc 18 | _ = fmt.Sprintf 19 | 20 | ParamDescs = []check.ParameterDesc{ 21 | } 22 | ) 23 | 24 | func (p *Params) Validate() error { 25 | var validationErrors []string 26 | if len(validationErrors) > 0 { 27 | return errors.Errorf("invalid parameters: %s", strings.Join(validationErrors, ", ")) 28 | } 29 | return nil 30 | } 31 | 32 | // ParseAndValidate instantiates a Params object out of the passed map[string]interface{}, 33 | // validates it, and returns it. 34 | // The return type is interface{} to satisfy the type in the Template struct. 35 | func ParseAndValidate(m map[string]interface{}) (interface{}, error) { 36 | var p Params 37 | if err := util.DecodeMapStructure(m, &p); err != nil { 38 | return nil, err 39 | } 40 | if err := p.Validate(); err != nil { 41 | return nil, err 42 | } 43 | return p, nil 44 | } 45 | 46 | // WrapInstantiateFunc is a convenience wrapper that wraps an untyped instantiate function 47 | // into a typed one. 48 | func WrapInstantiateFunc(f func(p Params) (check.Func, error)) func (interface{}) (check.Func, error) { 49 | return func(paramsInt interface{}) (check.Func, error) { 50 | return f(paramsInt.(Params)) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pkg/templates/namespace/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/namespace/template.go: -------------------------------------------------------------------------------- 1 | package namespace 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.stackrox.io/kube-linter/internal/stringutils" 7 | "golang.stackrox.io/kube-linter/pkg/check" 8 | "golang.stackrox.io/kube-linter/pkg/config" 9 | "golang.stackrox.io/kube-linter/pkg/diagnostic" 10 | "golang.stackrox.io/kube-linter/pkg/lintcontext" 11 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 12 | "golang.stackrox.io/kube-linter/pkg/templates" 13 | "golang.stackrox.io/kube-linter/pkg/templates/namespace/internal/params" 14 | ) 15 | 16 | func init() { 17 | templates.Register(check.Template{ 18 | HumanName: "Use Namespaces for Administrative Boundaries between Resources", 19 | Key: "use-namespace", 20 | Description: "Flag resources with no namespace specified or using default namespace", 21 | SupportedObjectKinds: config.ObjectKindsDesc{ 22 | ObjectKinds: []string{objectkinds.DeploymentLike, objectkinds.Service}, 23 | }, 24 | Parameters: params.ParamDescs, 25 | ParseAndValidateParams: params.ParseAndValidate, 26 | Instantiate: params.WrapInstantiateFunc(func(_ params.Params) (check.Func, error) { 27 | return func(_ lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic { 28 | namespace := object.K8sObject.GetNamespace() 29 | ns := stringutils.OrDefault(namespace, "default") 30 | if strings.EqualFold(ns, "default") { 31 | return []diagnostic.Diagnostic{{Message: "object in default namespace"}} 32 | } 33 | return nil 34 | }, nil 35 | }), 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /pkg/templates/nodeaffinity/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/nonexistentserviceaccount/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/nonisolatedpod/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/pdbmaxunavailable/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/pdbminavailable/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/pdbunhealthypodevictionpolicy/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/ports/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | 6 | // The port 7 | Port int 8 | 9 | // The protocol 10 | Protocol string 11 | } 12 | -------------------------------------------------------------------------------- /pkg/templates/privileged/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/privileged/template.go: -------------------------------------------------------------------------------- 1 | package privileged 2 | 3 | import ( 4 | "fmt" 5 | 6 | "golang.stackrox.io/kube-linter/pkg/check" 7 | "golang.stackrox.io/kube-linter/pkg/config" 8 | "golang.stackrox.io/kube-linter/pkg/diagnostic" 9 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 10 | "golang.stackrox.io/kube-linter/pkg/templates" 11 | "golang.stackrox.io/kube-linter/pkg/templates/privileged/internal/params" 12 | "golang.stackrox.io/kube-linter/pkg/templates/util" 13 | v1 "k8s.io/api/core/v1" 14 | ) 15 | 16 | func init() { 17 | templates.Register(check.Template{ 18 | HumanName: "Privileged Containers", 19 | Key: "privileged", 20 | Description: "Flag privileged containers", 21 | SupportedObjectKinds: config.ObjectKindsDesc{ 22 | ObjectKinds: []string{objectkinds.DeploymentLike}, 23 | }, 24 | Parameters: params.ParamDescs, 25 | ParseAndValidateParams: params.ParseAndValidate, 26 | Instantiate: params.WrapInstantiateFunc(func(_ params.Params) (check.Func, error) { 27 | return util.PerContainerCheck(func(container *v1.Container) []diagnostic.Diagnostic { 28 | if securityContext := container.SecurityContext; securityContext != nil { 29 | if securityContext.Privileged != nil && *securityContext.Privileged { 30 | return []diagnostic.Diagnostic{{Message: fmt.Sprintf("container %q is privileged", container.Name)}} 31 | } 32 | } 33 | return nil 34 | }), nil 35 | }), 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /pkg/templates/privilegedports/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/privilegedports/template.go: -------------------------------------------------------------------------------- 1 | package privilegedports 2 | 3 | import ( 4 | "fmt" 5 | 6 | "golang.stackrox.io/kube-linter/pkg/check" 7 | "golang.stackrox.io/kube-linter/pkg/config" 8 | "golang.stackrox.io/kube-linter/pkg/diagnostic" 9 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 10 | "golang.stackrox.io/kube-linter/pkg/templates" 11 | "golang.stackrox.io/kube-linter/pkg/templates/privilegedports/internal/params" 12 | "golang.stackrox.io/kube-linter/pkg/templates/util" 13 | v1 "k8s.io/api/core/v1" 14 | ) 15 | 16 | func init() { 17 | templates.Register(check.Template{ 18 | HumanName: "Privileged Ports", 19 | Key: "privileged-ports", 20 | Description: "Flag privileged ports", 21 | SupportedObjectKinds: config.ObjectKindsDesc{ 22 | ObjectKinds: []string{objectkinds.DeploymentLike}, 23 | }, 24 | Parameters: params.ParamDescs, 25 | ParseAndValidateParams: params.ParseAndValidate, 26 | Instantiate: params.WrapInstantiateFunc(func(_ params.Params) (check.Func, error) { 27 | return util.PerContainerCheck(func(container *v1.Container) []diagnostic.Diagnostic { 28 | var results []diagnostic.Diagnostic 29 | for _, port := range container.Ports { 30 | if int(port.ContainerPort) > 0 && int(port.ContainerPort) < 1024 { 31 | results = append(results, diagnostic.Diagnostic{ 32 | Message: fmt.Sprintf("port %d is mapped in container %q.", port.ContainerPort, container.Name), 33 | }) 34 | } 35 | } 36 | return results 37 | }), nil 38 | }), 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /pkg/templates/privilegeescalation/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/readinessport/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct{} 5 | -------------------------------------------------------------------------------- /pkg/templates/readinessport/template.go: -------------------------------------------------------------------------------- 1 | package readinessport 2 | 3 | import ( 4 | "golang.stackrox.io/kube-linter/pkg/check" 5 | "golang.stackrox.io/kube-linter/pkg/config" 6 | "golang.stackrox.io/kube-linter/pkg/diagnostic" 7 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 8 | "golang.stackrox.io/kube-linter/pkg/templates" 9 | "golang.stackrox.io/kube-linter/pkg/templates/readinessport/internal/params" 10 | "golang.stackrox.io/kube-linter/pkg/templates/util" 11 | v1 "k8s.io/api/core/v1" 12 | ) 13 | 14 | const templateKey = "readiness-port" 15 | 16 | func init() { 17 | templates.Register(check.Template{ 18 | HumanName: "Readiness Port Not Exposed", 19 | Key: templateKey, 20 | Description: "Flag containers with an Readiness probe to not exposed port.", 21 | SupportedObjectKinds: config.ObjectKindsDesc{ 22 | ObjectKinds: []string{objectkinds.DeploymentLike}, 23 | }, 24 | Parameters: params.ParamDescs, 25 | ParseAndValidateParams: params.ParseAndValidate, 26 | Instantiate: params.WrapInstantiateFunc(func(_ params.Params) (check.Func, error) { 27 | return util.PerNonInitContainerCheck(func(container *v1.Container) []diagnostic.Diagnostic { 28 | return util.CheckProbePort(container, container.ReadinessProbe) 29 | }), nil 30 | }), 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /pkg/templates/readinessprobe/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/readinessprobe/template.go: -------------------------------------------------------------------------------- 1 | package readinessprobe 2 | 3 | import ( 4 | "fmt" 5 | 6 | "golang.stackrox.io/kube-linter/pkg/check" 7 | "golang.stackrox.io/kube-linter/pkg/config" 8 | "golang.stackrox.io/kube-linter/pkg/diagnostic" 9 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 10 | "golang.stackrox.io/kube-linter/pkg/templates" 11 | "golang.stackrox.io/kube-linter/pkg/templates/readinessprobe/internal/params" 12 | "golang.stackrox.io/kube-linter/pkg/templates/util" 13 | v1 "k8s.io/api/core/v1" 14 | ) 15 | 16 | func init() { 17 | templates.Register(check.Template{ 18 | HumanName: "Readiness Probe Not Specified", 19 | Key: "readiness-probe", 20 | Description: "Flag containers that don't specify a readiness probe", 21 | SupportedObjectKinds: config.ObjectKindsDesc{ 22 | ObjectKinds: []string{objectkinds.DeploymentLike}, 23 | }, 24 | Parameters: params.ParamDescs, 25 | ParseAndValidateParams: params.ParseAndValidate, 26 | Instantiate: params.WrapInstantiateFunc(func(_ params.Params) (check.Func, error) { 27 | return util.PerNonInitContainerCheck(func(container *v1.Container) []diagnostic.Diagnostic { 28 | if container.ReadinessProbe == nil { 29 | return []diagnostic.Diagnostic{{Message: fmt.Sprintf("container %q does not specify a readiness probe", container.Name)}} 30 | } 31 | return nil 32 | }), nil 33 | }), 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/templates/readonlyrootfs/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/readonlyrootfs/template.go: -------------------------------------------------------------------------------- 1 | package readonlyrootfs 2 | 3 | import ( 4 | "fmt" 5 | 6 | "golang.stackrox.io/kube-linter/pkg/check" 7 | "golang.stackrox.io/kube-linter/pkg/config" 8 | "golang.stackrox.io/kube-linter/pkg/diagnostic" 9 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 10 | "golang.stackrox.io/kube-linter/pkg/templates" 11 | "golang.stackrox.io/kube-linter/pkg/templates/readonlyrootfs/internal/params" 12 | "golang.stackrox.io/kube-linter/pkg/templates/util" 13 | v1 "k8s.io/api/core/v1" 14 | ) 15 | 16 | func init() { 17 | templates.Register(check.Template{ 18 | HumanName: "Read-only Root Filesystems", 19 | Key: "read-only-root-fs", 20 | Description: "Flag containers without read-only root file systems", 21 | SupportedObjectKinds: config.ObjectKindsDesc{ 22 | ObjectKinds: []string{objectkinds.DeploymentLike}, 23 | }, 24 | Parameters: params.ParamDescs, 25 | ParseAndValidateParams: params.ParseAndValidate, 26 | Instantiate: params.WrapInstantiateFunc(func(p params.Params) (check.Func, error) { 27 | return util.PerContainerCheck(func(container *v1.Container) []diagnostic.Diagnostic { 28 | sc := container.SecurityContext 29 | if sc == nil || sc.ReadOnlyRootFilesystem == nil || !*sc.ReadOnlyRootFilesystem { 30 | return []diagnostic.Diagnostic{{Message: fmt.Sprintf("container %q does not have a read-only root file system", container.Name)}} 31 | } 32 | return nil 33 | }), nil 34 | }), 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /pkg/templates/readsecret/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/readsecret/template.go: -------------------------------------------------------------------------------- 1 | package readsecret 2 | 3 | import ( 4 | "fmt" 5 | 6 | "golang.stackrox.io/kube-linter/pkg/check" 7 | "golang.stackrox.io/kube-linter/pkg/config" 8 | "golang.stackrox.io/kube-linter/pkg/diagnostic" 9 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 10 | "golang.stackrox.io/kube-linter/pkg/templates" 11 | "golang.stackrox.io/kube-linter/pkg/templates/readsecret/internal/params" 12 | "golang.stackrox.io/kube-linter/pkg/templates/util" 13 | v1 "k8s.io/api/core/v1" 14 | ) 15 | 16 | func init() { 17 | templates.Register(check.Template{ 18 | HumanName: "Read Secret From Environment Variables", 19 | Key: "read-secret-from-env-var", 20 | Description: "Flag environment variables that use SecretKeyRef", 21 | SupportedObjectKinds: config.ObjectKindsDesc{ 22 | ObjectKinds: []string{objectkinds.DeploymentLike}, 23 | }, 24 | Parameters: params.ParamDescs, 25 | ParseAndValidateParams: params.ParseAndValidate, 26 | Instantiate: params.WrapInstantiateFunc(func(_ params.Params) (check.Func, error) { 27 | return util.PerContainerCheck(func(container *v1.Container) []diagnostic.Diagnostic { 28 | var results []diagnostic.Diagnostic 29 | for _, envVar := range container.Env { 30 | if envVar.ValueFrom != nil && envVar.ValueFrom.SecretKeyRef != nil { 31 | results = append(results, diagnostic.Diagnostic{ 32 | Message: fmt.Sprintf("environment variable %q in container %q uses SecretKeyRef", envVar.Name, container.Name), 33 | }) 34 | } 35 | } 36 | return results 37 | }), nil 38 | }), 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /pkg/templates/registry.go: -------------------------------------------------------------------------------- 1 | package templates 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | 7 | "golang.stackrox.io/kube-linter/pkg/check" 8 | ) 9 | 10 | var ( 11 | allTemplates = make(map[string]check.Template) 12 | ) 13 | 14 | // Register registers a template with the given name. 15 | // Intended to be called at program init time. 16 | func Register(t check.Template) { 17 | if _, ok := allTemplates[t.Key]; ok { 18 | panic(fmt.Sprintf("duplicate template: %v", t.Key)) 19 | } 20 | allTemplates[t.Key] = t 21 | } 22 | 23 | // Get gets a template by name, returning a boolean indicating whether it was found. 24 | func Get(name string) (check.Template, bool) { 25 | t, ok := allTemplates[name] 26 | return t, ok 27 | } 28 | 29 | // List returns all known templates, sorted by name. 30 | func List() []check.Template { 31 | out := make([]check.Template, 0, len(allTemplates)) 32 | for _, t := range allTemplates { 33 | out = append(out, t) 34 | } 35 | sort.Slice(out, func(i, j int) bool { 36 | return out[i].Key < out[j].Key 37 | }) 38 | return out 39 | } 40 | -------------------------------------------------------------------------------- /pkg/templates/replicas/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | 6 | // The minimum number of replicas a deployment should have 7 | MinReplicas int 8 | } 9 | -------------------------------------------------------------------------------- /pkg/templates/requiredannotation/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | 6 | // Key of the required label. 7 | // +required 8 | Key string 9 | 10 | // Value of the required label. 11 | Value string 12 | } 13 | -------------------------------------------------------------------------------- /pkg/templates/requiredannotation/template.go: -------------------------------------------------------------------------------- 1 | package requiredannotation 2 | 3 | import ( 4 | "golang.stackrox.io/kube-linter/pkg/check" 5 | "golang.stackrox.io/kube-linter/pkg/config" 6 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 7 | "golang.stackrox.io/kube-linter/pkg/templates" 8 | "golang.stackrox.io/kube-linter/pkg/templates/requiredannotation/internal/params" 9 | "golang.stackrox.io/kube-linter/pkg/templates/util" 10 | ) 11 | 12 | func init() { 13 | templates.Register(check.Template{ 14 | HumanName: "Required Annotation", 15 | Key: "required-annotation", 16 | Description: "Flag objects not carrying at least one annotation matching the provided patterns", 17 | SupportedObjectKinds: config.ObjectKindsDesc{ 18 | ObjectKinds: []string{objectkinds.Any}, 19 | }, 20 | Parameters: params.ParamDescs, 21 | ParseAndValidateParams: params.ParseAndValidate, 22 | Instantiate: params.WrapInstantiateFunc(func(p params.Params) (check.Func, error) { 23 | return util.ConstructRequiredMapMatcher(p.Key, p.Value, "annotation") 24 | }), 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/templates/requiredlabel/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | 6 | // Key of the required label. 7 | // +required 8 | Key string 9 | 10 | // Value of the required label. 11 | Value string 12 | } 13 | -------------------------------------------------------------------------------- /pkg/templates/requiredlabel/template.go: -------------------------------------------------------------------------------- 1 | package requiredlabel 2 | 3 | import ( 4 | "golang.stackrox.io/kube-linter/pkg/check" 5 | "golang.stackrox.io/kube-linter/pkg/config" 6 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 7 | "golang.stackrox.io/kube-linter/pkg/templates" 8 | "golang.stackrox.io/kube-linter/pkg/templates/requiredlabel/internal/params" 9 | "golang.stackrox.io/kube-linter/pkg/templates/util" 10 | ) 11 | 12 | func init() { 13 | templates.Register(check.Template{ 14 | HumanName: "Required Label", 15 | Key: "required-label", 16 | Description: "Flag objects not carrying at least one label matching the provided patterns", 17 | SupportedObjectKinds: config.ObjectKindsDesc{ 18 | ObjectKinds: []string{objectkinds.Any}, 19 | }, 20 | Parameters: params.ParamDescs, 21 | ParseAndValidateParams: params.ParseAndValidate, 22 | Instantiate: params.WrapInstantiateFunc(func(p params.Params) (check.Func, error) { 23 | return util.ConstructRequiredMapMatcher(p.Key, p.Value, "label") 24 | }), 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/templates/restartpolicy/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/runasnonroot/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/sccdenypriv/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | 6 | // allowPrivilegedContainer value 7 | AllowPrivilegedContainer bool 8 | } 9 | -------------------------------------------------------------------------------- /pkg/templates/sccdenypriv/template.go: -------------------------------------------------------------------------------- 1 | package sccdenypriv 2 | 3 | import ( 4 | "fmt" 5 | 6 | "golang.stackrox.io/kube-linter/pkg/check" 7 | "golang.stackrox.io/kube-linter/pkg/config" 8 | "golang.stackrox.io/kube-linter/pkg/diagnostic" 9 | "golang.stackrox.io/kube-linter/pkg/extract" 10 | "golang.stackrox.io/kube-linter/pkg/lintcontext" 11 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 12 | "golang.stackrox.io/kube-linter/pkg/templates" 13 | "golang.stackrox.io/kube-linter/pkg/templates/sccdenypriv/internal/params" 14 | ) 15 | 16 | const ( 17 | templateKey = "scc-deny-privileged-container" 18 | ) 19 | 20 | func init() { 21 | templates.Register(check.Template{ 22 | HumanName: "SecurityContextConstraints allowPrivilegedContainer", 23 | Key: templateKey, 24 | Description: "Flag SCC with allowPrivilegedContainer set to true", 25 | SupportedObjectKinds: config.ObjectKindsDesc{ 26 | ObjectKinds: []string{objectkinds.SecurityContextConstraints}, 27 | }, 28 | Parameters: params.ParamDescs, 29 | ParseAndValidateParams: params.ParseAndValidate, 30 | Instantiate: params.WrapInstantiateFunc(func(p params.Params) (check.Func, error) { 31 | return func(_ lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic { 32 | state, found := extract.SCCallowPrivilegedContainer(object.K8sObject) 33 | if found && state == p.AllowPrivilegedContainer { 34 | return []diagnostic.Diagnostic{ 35 | {Message: fmt.Sprintf("SCC has allowPrivilegedContainer set to %v", state)}, 36 | } 37 | } 38 | return nil 39 | }, nil 40 | }), 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /pkg/templates/serviceaccount/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | 6 | // A regex specifying the required service account to match. 7 | // +required 8 | ServiceAccount string 9 | } 10 | -------------------------------------------------------------------------------- /pkg/templates/servicetype/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | // An array of service types that should not be used 6 | // +noregex 7 | // +notnegatable 8 | ForbiddenServiceTypes []string `json:"forbiddenServiceTypes"` 9 | } 10 | -------------------------------------------------------------------------------- /pkg/templates/servicetype/template.go: -------------------------------------------------------------------------------- 1 | package servicetype 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "golang.stackrox.io/kube-linter/pkg/check" 8 | "golang.stackrox.io/kube-linter/pkg/config" 9 | "golang.stackrox.io/kube-linter/pkg/diagnostic" 10 | "golang.stackrox.io/kube-linter/pkg/lintcontext" 11 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 12 | "golang.stackrox.io/kube-linter/pkg/templates" 13 | "golang.stackrox.io/kube-linter/pkg/templates/servicetype/internal/params" 14 | v1 "k8s.io/api/core/v1" 15 | ) 16 | 17 | func init() { 18 | templates.Register(check.Template{ 19 | HumanName: "Forbidden Service Types", 20 | Key: "forbidden-service-types", 21 | Description: "Flag forbidden services", 22 | SupportedObjectKinds: config.ObjectKindsDesc{ 23 | ObjectKinds: []string{objectkinds.Service}, 24 | }, 25 | Parameters: params.ParamDescs, 26 | ParseAndValidateParams: params.ParseAndValidate, 27 | Instantiate: params.WrapInstantiateFunc(func(p params.Params) (check.Func, error) { 28 | return func(lintCtx lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic { 29 | 30 | service, ok := object.K8sObject.(*v1.Service) 31 | if !ok { 32 | return nil 33 | } 34 | var results []diagnostic.Diagnostic 35 | for _, servicetype := range p.ForbiddenServiceTypes { 36 | if strings.EqualFold(string(service.Spec.Type), servicetype) { 37 | results = append(results, diagnostic.Diagnostic{Message: fmt.Sprintf("%q service type is forbidden.", servicetype)}) 38 | } 39 | } 40 | return results 41 | }, nil 42 | }), 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /pkg/templates/startupport/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct{} 5 | -------------------------------------------------------------------------------- /pkg/templates/startupport/template.go: -------------------------------------------------------------------------------- 1 | package startupport 2 | 3 | import ( 4 | "golang.stackrox.io/kube-linter/pkg/check" 5 | "golang.stackrox.io/kube-linter/pkg/config" 6 | "golang.stackrox.io/kube-linter/pkg/diagnostic" 7 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 8 | "golang.stackrox.io/kube-linter/pkg/templates" 9 | "golang.stackrox.io/kube-linter/pkg/templates/startupport/internal/params" 10 | "golang.stackrox.io/kube-linter/pkg/templates/util" 11 | v1 "k8s.io/api/core/v1" 12 | ) 13 | 14 | const templateKey = "startup-port" 15 | 16 | func init() { 17 | templates.Register(check.Template{ 18 | HumanName: "Startup Port Exposed", 19 | Key: templateKey, 20 | Description: "Flag containers with an Startup probe to not exposed port.", 21 | SupportedObjectKinds: config.ObjectKindsDesc{ 22 | ObjectKinds: []string{objectkinds.DeploymentLike}, 23 | }, 24 | Parameters: params.ParamDescs, 25 | ParseAndValidateParams: params.ParseAndValidate, 26 | Instantiate: params.WrapInstantiateFunc(func(_ params.Params) (check.Func, error) { 27 | return util.PerNonInitContainerCheck(func(container *v1.Container) []diagnostic.Diagnostic { 28 | return util.CheckProbePort(container, container.StartupProbe) 29 | }), nil 30 | }), 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /pkg/templates/sysctl/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | // An array of unsafe system controls 6 | // +noregex 7 | // +notnegatable 8 | UnsafeSysCtls []string `json:"unsafeSysCtls"` 9 | } 10 | -------------------------------------------------------------------------------- /pkg/templates/targetport/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/unsafeprocmount/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/unsafeprocmount/template.go: -------------------------------------------------------------------------------- 1 | package unsafeprocmount 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "golang.stackrox.io/kube-linter/pkg/check" 8 | "golang.stackrox.io/kube-linter/pkg/config" 9 | "golang.stackrox.io/kube-linter/pkg/diagnostic" 10 | "golang.stackrox.io/kube-linter/pkg/objectkinds" 11 | "golang.stackrox.io/kube-linter/pkg/templates" 12 | "golang.stackrox.io/kube-linter/pkg/templates/unsafeprocmount/internal/params" 13 | "golang.stackrox.io/kube-linter/pkg/templates/util" 14 | v1 "k8s.io/api/core/v1" 15 | ) 16 | 17 | func init() { 18 | templates.Register(check.Template{ 19 | HumanName: "Unsafe Proc Mount", 20 | Key: "unsafe-proc-mount", 21 | Description: "Flag containers of unsafe proc mount", 22 | SupportedObjectKinds: config.ObjectKindsDesc{ 23 | ObjectKinds: []string{objectkinds.DeploymentLike}, 24 | }, 25 | Parameters: params.ParamDescs, 26 | ParseAndValidateParams: params.ParseAndValidate, 27 | Instantiate: params.WrapInstantiateFunc(func(_ params.Params) (check.Func, error) { 28 | return util.PerContainerCheck(func(container *v1.Container) []diagnostic.Diagnostic { 29 | if container.SecurityContext != nil && container.SecurityContext.ProcMount != nil { 30 | if strings.EqualFold(string(*container.SecurityContext.ProcMount), "Unmasked") { 31 | return []diagnostic.Diagnostic{{Message: fmt.Sprintf("container %q exposes /proc unsafely (via procMount=Unmasked).", container.Name)}} 32 | } 33 | } 34 | return nil 35 | }), nil 36 | }), 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /pkg/templates/updateconfig/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | // A regular expression the defines the type of update 6 | // strategy allowed. 7 | // +required 8 | StrategyTypeRegex string 9 | 10 | // The maximum value that be set in a RollingUpdate 11 | // configuration for the MaxUnavailable. This can be 12 | // an integer or a percent. 13 | MaxPodsUnavailable string 14 | 15 | // The minimum value that be set in a RollingUpdate 16 | // configuration for the MaxUnavailable. This can be 17 | // an integer or a percent. 18 | MinPodsUnavailable string 19 | 20 | // The maximum value that be set in a RollingUpdate 21 | // configuration for the MaxSurge. This can be 22 | // an integer or a percent. 23 | MaxSurge string 24 | 25 | // The minimum value that be set in a RollingUpdate 26 | // configuration for the MaxSurge. This can be 27 | // an integer or a percent. 28 | MinSurge string 29 | } 30 | -------------------------------------------------------------------------------- /pkg/templates/util/check_probe_port.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | 6 | "golang.stackrox.io/kube-linter/pkg/diagnostic" 7 | v1 "k8s.io/api/core/v1" 8 | "k8s.io/apimachinery/pkg/util/intstr" 9 | ) 10 | 11 | var sentinel = struct{}{} 12 | 13 | func CheckProbePort(container *v1.Container, probe *v1.Probe) []diagnostic.Diagnostic { 14 | if probe == nil { 15 | return nil 16 | } 17 | 18 | ports := map[intstr.IntOrString]struct{}{} 19 | for _, port := range container.Ports { 20 | if port.Protocol != "" && port.Protocol != v1.ProtocolTCP { 21 | continue 22 | } 23 | ports[intstr.FromInt32(port.ContainerPort)] = sentinel 24 | ports[intstr.FromString(port.Name)] = sentinel 25 | } 26 | 27 | if httpProbe := probe.HTTPGet; httpProbe != nil { 28 | if _, ok := ports[httpProbe.Port]; !ok { 29 | return []diagnostic.Diagnostic{{ 30 | Message: fmt.Sprintf("container %q does not expose port %s for the HTTPGet", container.Name, httpProbe.Port.String()), 31 | }} 32 | } 33 | } 34 | 35 | if tcpProbe := probe.TCPSocket; tcpProbe != nil { 36 | if _, ok := ports[tcpProbe.Port]; !ok { 37 | return []diagnostic.Diagnostic{{ 38 | Message: fmt.Sprintf("container %q does not expose port %s for the TCPSocket", container.Name, tcpProbe.Port.String()), 39 | }} 40 | } 41 | } 42 | 43 | if grpcProbe := probe.GRPC; grpcProbe != nil { 44 | if _, ok := ports[intstr.FromInt32(grpcProbe.Port)]; !ok { 45 | return []diagnostic.Diagnostic{{ 46 | Message: fmt.Sprintf("container %q does not expose port %d for the GRPC check", container.Name, grpcProbe.Port), 47 | }} 48 | } 49 | } 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /pkg/templates/util/json.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "encoding/json" 5 | "strings" 6 | 7 | "golang.stackrox.io/kube-linter/pkg/check" 8 | ) 9 | 10 | // MustParseParameterDesc unmarshals the given JSON into a templates.ParameterDesc. 11 | func MustParseParameterDesc(asJSON string) check.ParameterDesc { 12 | var out check.ParameterDesc 13 | 14 | decoder := json.NewDecoder(strings.NewReader(asJSON)) 15 | decoder.DisallowUnknownFields() 16 | if err := decoder.Decode(&out); err != nil { 17 | panic(err) 18 | } 19 | return out 20 | } 21 | -------------------------------------------------------------------------------- /pkg/templates/util/map_structure.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "github.com/mitchellh/mapstructure" 5 | ) 6 | 7 | // DecodeMapStructure decodes the given map[string]interface{} into the given out variable, typically 8 | // a pointer to a struct. 9 | func DecodeMapStructure(m map[string]interface{}, out interface{}) error { 10 | dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 11 | ErrorUnused: true, 12 | TagName: "json", 13 | Result: out, 14 | }) 15 | if err != nil { 16 | return err 17 | } 18 | return dec.Decode(m) 19 | } 20 | -------------------------------------------------------------------------------- /pkg/templates/util/value_in_range.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | // ValueInRange returns whether the given quantity is in the range between the lowerBound and the upperBound (inclusive). 4 | // A nil upper bound is interpreted as infinity. 5 | func ValueInRange(value, lowerBound int, upperBound *int) bool { 6 | if value < lowerBound { 7 | return false 8 | } 9 | if upperBound != nil && value > *upperBound { 10 | return false 11 | } 12 | return true 13 | } 14 | -------------------------------------------------------------------------------- /pkg/templates/wildcardinrules/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /pkg/templates/writablehostmount/internal/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Params represents the params accepted by this template. 4 | type Params struct { 5 | } 6 | -------------------------------------------------------------------------------- /tests/checks/access-to-create-pods.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: dont-fire 6 | namespace: namespace-dev 7 | rules: 8 | - apiGroups: [""] 9 | resources: ["pods"] 10 | verbs: ["get"] 11 | --- 12 | apiVersion: rbac.authorization.k8s.io/v1 13 | kind: Role 14 | metadata: 15 | name: role1 16 | namespace: namespace-dev 17 | rules: 18 | - apiGroups: [""] 19 | resources: ["pods"] 20 | verbs: ["create"] 21 | --- 22 | apiVersion: rbac.authorization.k8s.io/v1 23 | kind: RoleBinding 24 | metadata: 25 | name: role-binding1 26 | namespace: namespace-dev 27 | subjects: 28 | - kind: ServiceAccount 29 | name: account1 30 | namespace: namespace-dev 31 | roleRef: 32 | apiGroup: rbac.authorization.k8s.io 33 | kind: Role 34 | name: role1 35 | -------------------------------------------------------------------------------- /tests/checks/access-to-secrets.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: dont-fire 6 | namespace: namespace-dev 7 | rules: 8 | - apiGroups: [""] 9 | resources: ["pods"] 10 | verbs: ["get"] 11 | --- 12 | apiVersion: rbac.authorization.k8s.io/v1 13 | kind: Role 14 | metadata: 15 | name: role1 16 | namespace: namespace-dev 17 | rules: 18 | - apiGroups: [""] 19 | resources: ["secrets"] 20 | verbs: ["get"] 21 | --- 22 | apiVersion: rbac.authorization.k8s.io/v1 23 | kind: RoleBinding 24 | metadata: 25 | name: role-binding1 26 | namespace: namespace-dev 27 | subjects: 28 | - kind: ServiceAccount 29 | name: account1 30 | namespace: namespace-dev 31 | roleRef: 32 | apiGroup: rbac.authorization.k8s.io 33 | kind: Role 34 | name: role1 35 | -------------------------------------------------------------------------------- /tests/checks/cluster-admin-role-binding.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRoleBinding 4 | metadata: 5 | name: dont-fire 6 | namespace: namespace-dev 7 | subjects: 8 | - kind: ServiceAccount 9 | name: account1 10 | namespace: namespace-dev 11 | roleRef: 12 | apiGroup: rbac.authorization.k8s.io 13 | kind: ClusterRole 14 | name: admin 15 | --- 16 | apiVersion: rbac.authorization.k8s.io/v1 17 | kind: ClusterRoleBinding 18 | metadata: 19 | name: role-binding1 20 | namespace: namespace-dev 21 | subjects: 22 | - kind: ServiceAccount 23 | name: account1 24 | namespace: namespace-dev 25 | roleRef: 26 | apiGroup: rbac.authorization.k8s.io 27 | kind: ClusterRole 28 | name: cluster-admin 29 | -------------------------------------------------------------------------------- /tests/checks/dangling-hpa.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: dont-fire 6 | spec: 7 | template: 8 | metadata: 9 | labels: 10 | app.kubernetes.io/name: dontfire 11 | --- 12 | apiVersion: autoscaling/v2beta1 13 | kind: HorizontalPodAutoscaler 14 | metadata: 15 | name: RELEASE-NAME-mychart 16 | spec: 17 | scaleTargetRef: 18 | apiVersion: apps/v1 19 | kind: Deployment 20 | name: dont-fire 21 | minReplicas: 1 22 | maxReplicas: 100 23 | metrics: 24 | - type: Resource 25 | resource: 26 | name: cpu 27 | targetAverageUtilization: 80 28 | --- 29 | apiVersion: apps/v1 30 | kind: Deployment 31 | metadata: 32 | name: app1 33 | spec: 34 | template: 35 | metadata: 36 | labels: 37 | app.kubernetes.io/name: app1 38 | --- 39 | apiVersion: autoscaling/v2beta1 40 | kind: HorizontalPodAutoscaler 41 | metadata: 42 | name: hpa1 43 | spec: 44 | scaleTargetRef: 45 | apiVersion: apps/v1 46 | kind: Deployment 47 | name: app2 48 | minReplicas: 1 49 | maxReplicas: 100 50 | metrics: 51 | - type: Resource 52 | resource: 53 | name: cpu 54 | targetAverageUtilization: 80 -------------------------------------------------------------------------------- /tests/checks/dangling-networkpolicy.yml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: app 5 | spec: 6 | podSelector: 7 | matchLabels: 8 | app.kubernetes.io/name: app1 -------------------------------------------------------------------------------- /tests/checks/dangling-networkpolicypeer-podselector.yml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: app 5 | spec: 6 | podSelector: 7 | matchLabels: 8 | app.kubernetes.io/name: app1 9 | ingress: 10 | - from: 11 | - podSelector: 12 | matchLabels: 13 | app.kubernetes.io/name: app2 14 | ports: 15 | - protocol: TCP 16 | port: 8080 -------------------------------------------------------------------------------- /tests/checks/dangling-service.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: dont-fire 6 | spec: 7 | template: 8 | metadata: 9 | labels: 10 | app.kubernetes.io/name: dontfire 11 | --- 12 | apiVersion: v1 13 | kind: Service 14 | metadata: 15 | name: dont-fire 16 | spec: 17 | ports: 18 | - name: 8080-tcp 19 | port: 8080 20 | selector: 21 | app.kubernetes.io/name: dontfire 22 | --- 23 | apiVersion: apps/v1 24 | kind: Deployment 25 | metadata: 26 | name: app1 27 | spec: 28 | template: 29 | metadata: 30 | labels: 31 | app.kubernetes.io/name: app1 32 | --- 33 | apiVersion: v1 34 | kind: Service 35 | metadata: 36 | name: app1 37 | spec: 38 | ports: 39 | - name: 8080-tcp 40 | port: 8080 41 | selector: 42 | app.kubernetes.io/name: app 43 | --- 44 | apiVersion: apps.openshift.io/v1 45 | kind: DeploymentConfig 46 | metadata: 47 | name: app2 48 | spec: 49 | template: 50 | metadata: 51 | labels: 52 | app.kubernetes.io/name: app2 53 | --- 54 | apiVersion: v1 55 | kind: Service 56 | metadata: 57 | name: app2 58 | spec: 59 | ports: 60 | - name: 8080-tcp 61 | port: 8080 62 | selector: 63 | app.kubernetes.io/name: app -------------------------------------------------------------------------------- /tests/checks/default-service-account.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: dont-fire 6 | spec: 7 | template: 8 | spec: 9 | serviceAccountName: app 10 | --- 11 | apiVersion: apps.openshift.io/v1 12 | kind: DeploymentConfig 13 | metadata: 14 | name: dont-fire 15 | spec: 16 | template: 17 | spec: 18 | serviceAccountName: app 19 | --- 20 | apiVersion: apps/v1 21 | kind: Deployment 22 | metadata: 23 | name: app 24 | spec: 25 | template: 26 | spec: 27 | serviceAccountName: default 28 | --- 29 | apiVersion: apps.openshift.io/v1 30 | kind: DeploymentConfig 31 | metadata: 32 | name: app 33 | spec: 34 | template: 35 | spec: 36 | serviceAccountName: default -------------------------------------------------------------------------------- /tests/checks/deprecated-service-account-field.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: dont-fire 6 | spec: 7 | template: 8 | spec: 9 | serviceAccountName: default 10 | --- 11 | apiVersion: apps.openshift.io/v1 12 | kind: DeploymentConfig 13 | metadata: 14 | name: dont-fire 15 | spec: 16 | template: 17 | spec: 18 | serviceAccountName: default 19 | --- 20 | apiVersion: apps/v1 21 | kind: Deployment 22 | metadata: 23 | name: app 24 | spec: 25 | template: 26 | spec: 27 | serviceAccount: default 28 | --- 29 | apiVersion: apps.openshift.io/v1 30 | kind: DeploymentConfig 31 | metadata: 32 | name: app 33 | spec: 34 | template: 35 | spec: 36 | serviceAccount: default -------------------------------------------------------------------------------- /tests/checks/drop-net-raw-capability.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: dont-fire 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: app 11 | securityContext: 12 | capabilities: 13 | --- 14 | apiVersion: apps.openshift.io/v1 15 | kind: DeploymentConfig 16 | metadata: 17 | name: dont-fire 18 | spec: 19 | template: 20 | spec: 21 | containers: 22 | - name: app 23 | securityContext: 24 | capabilities: 25 | --- 26 | apiVersion: apps/v1 27 | kind: Deployment 28 | metadata: 29 | name: app 30 | spec: 31 | template: 32 | spec: 33 | containers: 34 | - name: app 35 | securityContext: 36 | capabilities: 37 | add: 38 | - NET_RAW 39 | --- 40 | apiVersion: apps.openshift.io/v1 41 | kind: DeploymentConfig 42 | metadata: 43 | name: app 44 | spec: 45 | template: 46 | spec: 47 | containers: 48 | - name: app 49 | securityContext: 50 | capabilities: 51 | add: 52 | - NET_RAW -------------------------------------------------------------------------------- /tests/checks/duplicate-env-var.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | spec: 4 | template: 5 | spec: 6 | containers: 7 | - name: dont-fire-deployment 8 | env: 9 | - name: PORT 10 | value: "8080" 11 | - name: HOST 12 | value: "localhost" 13 | - name: fire-deployment 14 | env: 15 | - name: PORT 16 | value: "8080" 17 | - name: PORT 18 | value: "9090" 19 | --- 20 | apiVersion: apps/v1 21 | kind: StatefulSet 22 | spec: 23 | template: 24 | spec: 25 | containers: 26 | - name: dont-fire-stateful 27 | env: 28 | - name: PORT 29 | value: "8080" 30 | - name: HOST 31 | value: "localhost" 32 | - name: fire-stateful 33 | env: 34 | - name: PORT 35 | value: "8080" 36 | - name: PORT 37 | value: "9090" 38 | -------------------------------------------------------------------------------- /tests/checks/env-var-secret.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: dont-fire 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: app 11 | env: 12 | - name: BLAH 13 | value: secretsquirrels 14 | --- 15 | apiVersion: apps.openshift.io/v1 16 | kind: DeploymentConfig 17 | metadata: 18 | name: dont-fire 19 | spec: 20 | template: 21 | spec: 22 | containers: 23 | - name: app 24 | env: 25 | - name: BLAH 26 | value: secretsquirrels 27 | --- 28 | apiVersion: apps/v1 29 | kind: Deployment 30 | metadata: 31 | name: app 32 | spec: 33 | template: 34 | spec: 35 | containers: 36 | - name: app 37 | env: 38 | - name: SECRET_BLAH 39 | value: secretsquirrels 40 | --- 41 | apiVersion: apps.openshift.io/v1 42 | kind: DeploymentConfig 43 | metadata: 44 | name: app 45 | spec: 46 | template: 47 | spec: 48 | containers: 49 | - name: app 50 | env: 51 | - name: SECRET_BLAH 52 | value: secretsquirrels -------------------------------------------------------------------------------- /tests/checks/exposed-services.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: dont-fire 6 | spec: 7 | selector: 8 | app: app 9 | ports: 10 | - protocol: TCP 11 | port: 80 12 | targetPort: 8080 13 | --- 14 | apiVersion: v1 15 | kind: Service 16 | metadata: 17 | name: app 18 | spec: 19 | type: NodePort -------------------------------------------------------------------------------- /tests/checks/forbidden-annotation.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: app1 6 | annotations: 7 | reloader.stakater.com/auto: "true" 8 | --- 9 | apiVersion: apps/v1 10 | kind: Deployment 11 | metadata: 12 | name: dont-fire 13 | annotations: 14 | reloader.stakater.com/auto: "false" 15 | --- 16 | apiVersion: apps/v1 17 | kind: Deployment 18 | metadata: 19 | name: dont-fire 20 | annotations: 21 | reloader.stakater.com/auto: T 22 | --- 23 | apiVersion: v1 24 | kind: ServiceAccount 25 | metadata: 26 | name: bad-irsa-role 27 | annotations: 28 | eks.amazonaws.com/role-arn: this-is-not-a-valid-iam-role-arn 29 | --- 30 | apiVersion: v1 31 | kind: ServiceAccount 32 | metadata: 33 | name: good-irsa-role 34 | annotations: 35 | eks.amazonaws.com/role-arn: arn:aws:iam::121212121212:role/role-name-goes-here 36 | -------------------------------------------------------------------------------- /tests/checks/host-ipc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: dont-fire 6 | spec: 7 | template: 8 | spec: 9 | hostIPC: false 10 | --- 11 | apiVersion: apps.openshift.io/v1 12 | kind: DeploymentConfig 13 | metadata: 14 | name: dont-fire 15 | spec: 16 | template: 17 | spec: 18 | hostIPC: false 19 | --- 20 | apiVersion: apps/v1 21 | kind: Deployment 22 | metadata: 23 | name: app 24 | spec: 25 | template: 26 | spec: 27 | hostIPC: true 28 | --- 29 | apiVersion: apps.openshift.io/v1 30 | kind: DeploymentConfig 31 | metadata: 32 | name: app 33 | spec: 34 | template: 35 | spec: 36 | hostIPC: true -------------------------------------------------------------------------------- /tests/checks/host-network.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: dont-fire 6 | spec: 7 | template: 8 | spec: 9 | hostNetwork: false 10 | --- 11 | apiVersion: apps.openshift.io/v1 12 | kind: DeploymentConfig 13 | metadata: 14 | name: dont-fire 15 | spec: 16 | template: 17 | spec: 18 | hostNetwork: false 19 | --- 20 | apiVersion: apps/v1 21 | kind: Deployment 22 | metadata: 23 | name: app 24 | spec: 25 | template: 26 | spec: 27 | hostNetwork: true 28 | --- 29 | apiVersion: apps.openshift.io/v1 30 | kind: DeploymentConfig 31 | metadata: 32 | name: app 33 | spec: 34 | template: 35 | spec: 36 | hostNetwork: true -------------------------------------------------------------------------------- /tests/checks/host-pid.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: dont-fire 6 | spec: 7 | template: 8 | spec: 9 | hostPID: false 10 | --- 11 | apiVersion: apps.openshift.io/v1 12 | kind: DeploymentConfig 13 | metadata: 14 | name: dont-fire 15 | spec: 16 | template: 17 | spec: 18 | hostPID: false 19 | --- 20 | apiVersion: apps/v1 21 | kind: Deployment 22 | metadata: 23 | name: app 24 | spec: 25 | template: 26 | spec: 27 | hostPID: true 28 | --- 29 | apiVersion: apps.openshift.io/v1 30 | kind: DeploymentConfig 31 | metadata: 32 | name: app 33 | spec: 34 | template: 35 | spec: 36 | hostPID: true -------------------------------------------------------------------------------- /tests/checks/hpa-minimum-three-replicas.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: dont-fire 6 | spec: 7 | minReplicas: 3 8 | maxReplicas: 100 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: testing 13 | targetCPUUtilizationPercentage: 85 14 | --- 15 | apiVersion: autoscaling/v2beta1 16 | kind: HorizontalPodAutoscaler 17 | metadata: 18 | name: app 19 | spec: 20 | minReplicas: 2 21 | maxReplicas: 100 22 | scaleTargetRef: 23 | apiVersion: apps/v1 24 | kind: Deployment 25 | name: testing 26 | targetCPUUtilizationPercentage: 85 27 | -------------------------------------------------------------------------------- /tests/checks/invalid-target-ports.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | namespace: test 5 | name: invalid-target-ports 6 | spec: 7 | ports: 8 | - targetPort: 1234 9 | - targetPort: 'n-3456789012345' 10 | - targetPort: 123456 11 | - targetPort: 'n234567890123456' 12 | --- 13 | apiVersion: apps/v1 14 | kind: Deployment 15 | metadata: 16 | namespace: test 17 | name: invalid-target-ports 18 | spec: 19 | template: 20 | spec: 21 | containers: 22 | - name: invalid-target-ports 23 | ports: 24 | - name: 'n23456789012345' 25 | containerPort: 1234 26 | - name: 'n234567890123456' 27 | containerPort: 1235 28 | - name: '123456' 29 | containerPort: 123456 30 | --- 31 | apiVersion: v1 32 | kind: Service 33 | metadata: 34 | namespace: test 35 | name: empty-ports 36 | --- 37 | apiVersion: apps/v1 38 | kind: Deployment 39 | metadata: 40 | namespace: test 41 | name: empty-ports 42 | spec: 43 | template: 44 | spec: 45 | containers: 46 | - name: empty-ports 47 | --- 48 | apiVersion: v1 49 | kind: Service 50 | metadata: 51 | namespace: test 52 | name: no-target-port 53 | spec: 54 | ports: 55 | - port: 8080 56 | name: test 57 | --- 58 | apiVersion: apps/v1 59 | kind: Deployment 60 | metadata: 61 | namespace: test 62 | name: no-target-port 63 | spec: 64 | template: 65 | spec: 66 | containers: 67 | - name: no-target-port 68 | ports: 69 | - containerPort: 1234 70 | -------------------------------------------------------------------------------- /tests/checks/latest-tag.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: dont-fire 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: app 11 | image: app:v1 12 | --- 13 | apiVersion: apps.openshift.io/v1 14 | kind: DeploymentConfig 15 | metadata: 16 | name: dont-fire 17 | spec: 18 | template: 19 | spec: 20 | containers: 21 | - name: app 22 | image: app:v1 23 | --- 24 | apiVersion: apps/v1 25 | kind: Deployment 26 | metadata: 27 | name: app 28 | spec: 29 | template: 30 | spec: 31 | containers: 32 | - name: app 33 | image: app:latest 34 | --- 35 | apiVersion: apps.openshift.io/v1 36 | kind: DeploymentConfig 37 | metadata: 38 | name: app 39 | spec: 40 | template: 41 | spec: 42 | containers: 43 | - name: app 44 | image: app:latest -------------------------------------------------------------------------------- /tests/checks/minimum-three-replicas.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: dont-fire 6 | spec: 7 | replicas: 3 8 | --- 9 | apiVersion: apps.openshift.io/v1 10 | kind: DeploymentConfig 11 | metadata: 12 | name: dont-fire 13 | spec: 14 | replicas: 3 15 | --- 16 | apiVersion: apps/v1 17 | kind: Deployment 18 | metadata: 19 | name: app 20 | spec: 21 | replicas: 2 22 | --- 23 | apiVersion: apps.openshift.io/v1 24 | kind: DeploymentConfig 25 | metadata: 26 | name: app 27 | spec: 28 | replicas: 2 -------------------------------------------------------------------------------- /tests/checks/mismatching-selector.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: dont-fire 6 | spec: 7 | selector: 8 | matchLabels: 9 | app.kubernetes.io/name: dont-fire 10 | template: 11 | metadata: 12 | labels: 13 | app.kubernetes.io/name: dont-fire 14 | spec: 15 | containers: 16 | - name: app 17 | --- 18 | apiVersion: apps.openshift.io/v1 19 | kind: DeploymentConfig 20 | metadata: 21 | name: dont-fire 22 | spec: 23 | selector: 24 | app.kubernetes.io/name: dont-fire 25 | template: 26 | metadata: 27 | labels: 28 | app.kubernetes.io/name: dont-fire 29 | spec: 30 | containers: 31 | - name: app2 32 | --- 33 | apiVersion: apps/v1 34 | kind: Deployment 35 | metadata: 36 | name: app1 37 | spec: 38 | selector: 39 | matchLabels: 40 | app.kubernetes.io/name: app1 41 | template: 42 | spec: 43 | containers: 44 | - name: app 45 | --- 46 | apiVersion: apps.openshift.io/v1 47 | kind: DeploymentConfig 48 | metadata: 49 | name: app2 50 | spec: 51 | selector: 52 | app.kubernetes.io/name: app2 53 | template: 54 | spec: 55 | containers: 56 | - name: app2 -------------------------------------------------------------------------------- /tests/checks/no-extensions-v1beta.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: networking.k8s.io/v1 3 | kind: NetworkPolicy 4 | metadata: 5 | name: app 6 | --- 7 | apiVersion: extensions/v1beta1 8 | kind: NetworkPolicy 9 | metadata: 10 | name: app -------------------------------------------------------------------------------- /tests/checks/no-node-affinity.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: nginx-deployment 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: nginx 9 | replicas: 1 10 | template: 11 | metadata: 12 | labels: 13 | app: nginx 14 | spec: 15 | containers: 16 | - name: nginx 17 | image: nginx:1.14.2 18 | ports: 19 | - containerPort: 80 -------------------------------------------------------------------------------- /tests/checks/no-read-only-root-fs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: dont-fire 6 | spec: 7 | replicas: 3 8 | template: 9 | spec: 10 | containers: 11 | - name: app 12 | securityContext: 13 | readOnlyRootFilesystem: true 14 | --- 15 | apiVersion: apps.openshift.io/v1 16 | kind: DeploymentConfig 17 | metadata: 18 | name: dont-fire 19 | spec: 20 | replicas: 3 21 | template: 22 | spec: 23 | containers: 24 | - name: app 25 | securityContext: 26 | readOnlyRootFilesystem: true 27 | --- 28 | apiVersion: apps/v1 29 | kind: Deployment 30 | metadata: 31 | name: app1 32 | spec: 33 | replicas: 3 34 | template: 35 | spec: 36 | containers: 37 | - name: app 38 | securityContext: 39 | readOnlyRootFilesystem: false 40 | --- 41 | apiVersion: apps.openshift.io/v1 42 | kind: DeploymentConfig 43 | metadata: 44 | name: app2 45 | spec: 46 | replicas: 3 47 | template: 48 | spec: 49 | containers: 50 | - name: app 51 | securityContext: 52 | readOnlyRootFilesystem: false -------------------------------------------------------------------------------- /tests/checks/no-rolling-update-strategy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: app1 6 | spec: 7 | strategy: 8 | type: Other 9 | --- 10 | apiVersion: apps.openshift.io/v1 11 | kind: DeploymentConfig 12 | metadata: 13 | name: app2 14 | spec: 15 | strategy: 16 | type: Other -------------------------------------------------------------------------------- /tests/checks/non-existent-service-account.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: dont-fire 6 | --- 7 | apiVersion: apps/v1 8 | kind: Deployment 9 | metadata: 10 | name: dont-fire 11 | spec: 12 | template: 13 | spec: 14 | serviceAccount: dont-fire 15 | --- 16 | apiVersion: apps/v1 17 | kind: Deployment 18 | metadata: 19 | name: app 20 | spec: 21 | template: 22 | spec: 23 | serviceAccount: missing 24 | --- 25 | apiVersion: apps.openshift.io/v1 26 | kind: DeploymentConfig 27 | metadata: 28 | name: app 29 | spec: 30 | template: 31 | spec: 32 | serviceAccount: missing -------------------------------------------------------------------------------- /tests/checks/pdb-max-unavailable.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: policy/v1 3 | kind: PodDisruptionBudget 4 | metadata: 5 | name: baz 6 | namespace: bar 7 | spec: 8 | maxUnavailable: 1 9 | --- 10 | apiVersion: policy/v1 11 | kind: PodDisruptionBudget 12 | metadata: 13 | name: foo 14 | namespace: bar 15 | spec: 16 | maxUnavailable: 0 17 | -------------------------------------------------------------------------------- /tests/checks/pdb-unhealthy-pod-eviction-policy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: policy/v1 3 | kind: PodDisruptionBudget 4 | metadata: 5 | name: fire 6 | spec: 7 | selector: 8 | matchLabels: 9 | app: app1 10 | maxUnavailable: 1 11 | --- 12 | apiVersion: policy/v1 13 | kind: PodDisruptionBudget 14 | metadata: 15 | name: dont-fire-1 16 | spec: 17 | selector: 18 | matchLabels: 19 | app: app2 20 | maxUnavailable: 1 21 | unhealthyPodEvictionPolicy: AlwaysAllow 22 | --- 23 | apiVersion: policy/v1 24 | kind: PodDisruptionBudget 25 | metadata: 26 | name: dont-fire-2 27 | spec: 28 | selector: 29 | matchLabels: 30 | app: app2 31 | maxUnavailable: 1 32 | unhealthyPodEvictionPolicy: IfHealthyBudget 33 | -------------------------------------------------------------------------------- /tests/checks/privilege-escalation-container.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: dont-fire 6 | spec: 7 | replicas: 3 8 | template: 9 | spec: 10 | containers: 11 | - name: app 12 | securityContext: 13 | allowPrivilegeEscalation: false 14 | --- 15 | apiVersion: apps.openshift.io/v1 16 | kind: DeploymentConfig 17 | metadata: 18 | name: dont-fire 19 | spec: 20 | replicas: 3 21 | template: 22 | spec: 23 | containers: 24 | - name: app 25 | securityContext: 26 | allowPrivilegeEscalation: false 27 | --- 28 | apiVersion: apps/v1 29 | kind: Deployment 30 | metadata: 31 | name: app1 32 | spec: 33 | replicas: 3 34 | template: 35 | spec: 36 | containers: 37 | - name: app 38 | securityContext: 39 | allowPrivilegeEscalation: true 40 | --- 41 | apiVersion: apps.openshift.io/v1 42 | kind: DeploymentConfig 43 | metadata: 44 | name: app2 45 | spec: 46 | replicas: 3 47 | template: 48 | spec: 49 | containers: 50 | - name: app 51 | securityContext: 52 | allowPrivilegeEscalation: true -------------------------------------------------------------------------------- /tests/checks/privileged-container.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: dont-fire 6 | spec: 7 | replicas: 3 8 | template: 9 | spec: 10 | containers: 11 | - name: app 12 | securityContext: 13 | privileged: false 14 | --- 15 | apiVersion: apps.openshift.io/v1 16 | kind: DeploymentConfig 17 | metadata: 18 | name: dont-fire 19 | spec: 20 | replicas: 3 21 | template: 22 | spec: 23 | containers: 24 | - name: app 25 | securityContext: 26 | privileged: false 27 | --- 28 | apiVersion: apps/v1 29 | kind: Deployment 30 | metadata: 31 | name: app1 32 | spec: 33 | replicas: 3 34 | template: 35 | spec: 36 | containers: 37 | - name: app 38 | securityContext: 39 | privileged: true 40 | --- 41 | apiVersion: apps.openshift.io/v1 42 | kind: DeploymentConfig 43 | metadata: 44 | name: app2 45 | spec: 46 | replicas: 3 47 | template: 48 | spec: 49 | containers: 50 | - name: app 51 | securityContext: 52 | privileged: true -------------------------------------------------------------------------------- /tests/checks/privileged-ports.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: dont-fire 6 | spec: 7 | replicas: 3 8 | template: 9 | spec: 10 | containers: 11 | - name: app 12 | ports: 13 | - containerPort: 8080 14 | --- 15 | apiVersion: apps.openshift.io/v1 16 | kind: DeploymentConfig 17 | metadata: 18 | name: dont-fire 19 | spec: 20 | replicas: 3 21 | template: 22 | spec: 23 | containers: 24 | - name: app 25 | ports: 26 | - containerPort: 8080 27 | --- 28 | apiVersion: apps/v1 29 | kind: Deployment 30 | metadata: 31 | name: app1 32 | spec: 33 | replicas: 3 34 | template: 35 | spec: 36 | containers: 37 | - name: app 38 | ports: 39 | - containerPort: 80 40 | --- 41 | apiVersion: apps.openshift.io/v1 42 | kind: DeploymentConfig 43 | metadata: 44 | name: app2 45 | spec: 46 | replicas: 3 47 | template: 48 | spec: 49 | containers: 50 | - name: app 51 | ports: 52 | - containerPort: 80 -------------------------------------------------------------------------------- /tests/checks/read-secret-from-env-var.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: app1 6 | spec: 7 | replicas: 3 8 | template: 9 | spec: 10 | containers: 11 | - name: app 12 | env: 13 | - name: TOKEN 14 | valueFrom: 15 | secretKeyRef: 16 | name: my-secret 17 | key: token 18 | --- 19 | apiVersion: apps.openshift.io/v1 20 | kind: DeploymentConfig 21 | metadata: 22 | name: app2 23 | spec: 24 | replicas: 3 25 | template: 26 | spec: 27 | containers: 28 | - name: app 29 | env: 30 | - name: TOKEN 31 | valueFrom: 32 | secretKeyRef: 33 | name: my-secret 34 | key: token -------------------------------------------------------------------------------- /tests/checks/readiness-port.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | spec: 4 | template: 5 | spec: 6 | containers: 7 | - name: dont-fire-deployment-grpc 8 | ports: 9 | - containerPort: 8080 10 | name: http 11 | readinessProbe: 12 | grpc: 13 | port: 8080 14 | - name: dont-fire-deployment 15 | ports: 16 | - containerPort: 8080 17 | name: http 18 | protocol: TCP 19 | readinessProbe: 20 | httpGet: 21 | path: "/" 22 | port: 8080 23 | - name: fire-deployment-name 24 | readinessProbe: 25 | httpGet: 26 | path: "/" 27 | port: http 28 | - name: fire-deployment-int 29 | ports: 30 | - containerPort: 9999 31 | name: http 32 | protocol: TCP 33 | readinessProbe: 34 | httpGet: 35 | path: "/" 36 | port: 8080 37 | - name: fire-deployment-udp 38 | ports: 39 | - containerPort: 9999 40 | name: udp 41 | protocol: UDP 42 | readinessProbe: 43 | tcpSocket: 44 | port: udp 45 | - name: fire-deployment-grpc 46 | ports: 47 | - containerPort: 9999 48 | name: grpc 49 | protocol: TCP 50 | readinessProbe: 51 | grpc: 52 | port: 8080 53 | --- 54 | -------------------------------------------------------------------------------- /tests/checks/required-annotation-email.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: dont-fire 6 | annotations: 7 | email: kubelinter@stackrox.com 8 | --- 9 | apiVersion: apps.openshift.io/v1 10 | kind: DeploymentConfig 11 | metadata: 12 | name: dont-fire 13 | annotations: 14 | email: kubelinter@stackrox.com 15 | --- 16 | apiVersion: apps/v1 17 | kind: Deployment 18 | metadata: 19 | name: app1 20 | annotations: 21 | whois: kubelinter@stackrox.com 22 | --- 23 | apiVersion: apps.openshift.io/v1 24 | kind: DeploymentConfig 25 | metadata: 26 | name: app2 27 | annotations: 28 | whois: kubelinter@stackrox.com -------------------------------------------------------------------------------- /tests/checks/required-label-owner.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: dont-fire 6 | labels: 7 | owner: kubelinter@stackrox.com 8 | --- 9 | apiVersion: apps.openshift.io/v1 10 | kind: DeploymentConfig 11 | metadata: 12 | name: dont-fire 13 | labels: 14 | owner: kubelinter@stackrox.com 15 | --- 16 | apiVersion: apps/v1 17 | kind: Deployment 18 | metadata: 19 | name: app1 20 | labels: 21 | dev: kubelinter@stackrox.com 22 | --- 23 | apiVersion: apps.openshift.io/v1 24 | kind: DeploymentConfig 25 | metadata: 26 | name: app2 27 | labels: 28 | dev: kubelinter@stackrox.com -------------------------------------------------------------------------------- /tests/checks/run-as-non-root.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: dont-fire 6 | spec: 7 | selector: 8 | matchLabels: 9 | app.kubernetes.io/name: app1 10 | template: 11 | spec: 12 | containers: 13 | - name: app 14 | runAsUser: 1001 15 | securityContext: 16 | runAsNonRoot: true 17 | --- 18 | apiVersion: apps.openshift.io/v1 19 | kind: DeploymentConfig 20 | metadata: 21 | name: dont-fire 22 | spec: 23 | selector: 24 | app.kubernetes.io/name: app2 25 | template: 26 | spec: 27 | containers: 28 | - name: app2 29 | runAsUser: 1001 30 | securityContext: 31 | runAsNonRoot: true 32 | --- 33 | apiVersion: apps/v1 34 | kind: Deployment 35 | metadata: 36 | name: app1 37 | spec: 38 | selector: 39 | matchLabels: 40 | app.kubernetes.io/name: app1 41 | template: 42 | spec: 43 | containers: 44 | - name: app 45 | runAsUser: 0 46 | --- 47 | apiVersion: apps.openshift.io/v1 48 | kind: DeploymentConfig 49 | metadata: 50 | name: app2 51 | spec: 52 | selector: 53 | app.kubernetes.io/name: app2 54 | template: 55 | spec: 56 | containers: 57 | - name: app2 58 | runAsUser: 0 -------------------------------------------------------------------------------- /tests/checks/sensitive-host-mounts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: app1 6 | spec: 7 | selector: 8 | matchLabels: 9 | app.kubernetes.io/name: app1 10 | template: 11 | spec: 12 | containers: 13 | - name: app 14 | volumeMounts: 15 | - name: config 16 | mountPath: /etc 17 | volumes: 18 | - name: config 19 | hostPath: 20 | path: /etc 21 | --- 22 | apiVersion: apps.openshift.io/v1 23 | kind: DeploymentConfig 24 | metadata: 25 | name: app2 26 | spec: 27 | selector: 28 | app.kubernetes.io/name: app2 29 | template: 30 | spec: 31 | containers: 32 | - name: app2 33 | runAsUser: 0 34 | volumeMounts: 35 | - name: config 36 | mountPath: /etc 37 | volumes: 38 | - name: config 39 | hostPath: 40 | path: /etc -------------------------------------------------------------------------------- /tests/checks/ssh-port.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: dont-fire 6 | spec: 7 | replicas: 3 8 | template: 9 | spec: 10 | containers: 11 | - name: app 12 | ports: 13 | - containerPort: 2222 14 | protocol: TCP 15 | --- 16 | apiVersion: apps.openshift.io/v1 17 | kind: DeploymentConfig 18 | metadata: 19 | name: dont-fire 20 | spec: 21 | replicas: 3 22 | template: 23 | spec: 24 | containers: 25 | - name: app 26 | ports: 27 | - containerPort: 2222 28 | protocol: TCP 29 | --- 30 | apiVersion: apps/v1 31 | kind: Deployment 32 | metadata: 33 | name: app1 34 | spec: 35 | replicas: 3 36 | template: 37 | spec: 38 | containers: 39 | - name: app 40 | ports: 41 | - containerPort: 22 42 | protocol: TCP 43 | --- 44 | apiVersion: apps.openshift.io/v1 45 | kind: DeploymentConfig 46 | metadata: 47 | name: app2 48 | spec: 49 | replicas: 3 50 | template: 51 | spec: 52 | containers: 53 | - name: app 54 | ports: 55 | - containerPort: 22 56 | protocol: TCP 57 | --- 58 | apiVersion: apps.openshift.io/v1 59 | kind: DeploymentConfig 60 | metadata: 61 | name: app3 62 | spec: 63 | replicas: 3 64 | template: 65 | spec: 66 | containers: 67 | - name: app-no-protocol 68 | ports: 69 | - containerPort: 22 -------------------------------------------------------------------------------- /tests/checks/startup-port.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | spec: 4 | template: 5 | spec: 6 | containers: 7 | - name: dont-fire-deployment-grpc 8 | ports: 9 | - containerPort: 8080 10 | name: http 11 | startupProbe: 12 | grpc: 13 | port: 8080 14 | - name: dont-fire-deployment 15 | ports: 16 | - containerPort: 8080 17 | name: http 18 | protocol: TCP 19 | startupProbe: 20 | httpGet: 21 | path: "/" 22 | port: 8080 23 | - name: fire-deployment-name 24 | startupProbe: 25 | httpGet: 26 | path: "/" 27 | port: http 28 | - name: fire-deployment-int 29 | ports: 30 | - containerPort: 9999 31 | name: http 32 | protocol: TCP 33 | startupProbe: 34 | httpGet: 35 | path: "/" 36 | port: 8080 37 | - name: fire-deployment-udp 38 | ports: 39 | - containerPort: 9999 40 | name: udp 41 | protocol: UDP 42 | startupProbe: 43 | tcpSocket: 44 | port: udp 45 | - name: fire-deployment-grpc 46 | ports: 47 | - containerPort: 9999 48 | name: grpc 49 | protocol: TCP 50 | startupProbe: 51 | grpc: 52 | port: 8080 53 | --- 54 | -------------------------------------------------------------------------------- /tests/checks/unsafe-proc-mount.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: app1 6 | spec: 7 | replicas: 3 8 | template: 9 | spec: 10 | containers: 11 | - name: app 12 | securityContext: 13 | procMount: Unmasked 14 | --- 15 | apiVersion: apps.openshift.io/v1 16 | kind: DeploymentConfig 17 | metadata: 18 | name: app2 19 | spec: 20 | replicas: 3 21 | template: 22 | spec: 23 | containers: 24 | - name: app 25 | securityContext: 26 | procMount: Unmasked -------------------------------------------------------------------------------- /tests/checks/unsafe-sysctls.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: app1 6 | spec: 7 | replicas: 3 8 | template: 9 | spec: 10 | containers: 11 | - name: app 12 | securityContext: 13 | sysctls: 14 | - name: kernel.sem 15 | --- 16 | apiVersion: apps.openshift.io/v1 17 | kind: DeploymentConfig 18 | metadata: 19 | name: app2 20 | spec: 21 | replicas: 3 22 | template: 23 | spec: 24 | containers: 25 | - name: app 26 | securityContext: 27 | sysctls: 28 | - name: kernel.sem -------------------------------------------------------------------------------- /tests/checks/unset-cpu-requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: app1 6 | spec: 7 | replicas: 3 8 | template: 9 | spec: 10 | containers: 11 | - name: app 12 | requests: 13 | memory: 1Gi 14 | 15 | --- 16 | apiVersion: apps.openshift.io/v1 17 | kind: DeploymentConfig 18 | metadata: 19 | name: app2 20 | spec: 21 | replicas: 3 22 | template: 23 | spec: 24 | containers: 25 | - name: app 26 | requests: 27 | memory: 1Gi -------------------------------------------------------------------------------- /tests/checks/unset-memory-requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: app1 6 | spec: 7 | replicas: 3 8 | template: 9 | spec: 10 | containers: 11 | - name: app 12 | limit: 13 | cpu: 1 14 | --- 15 | apiVersion: apps.openshift.io/v1 16 | kind: DeploymentConfig 17 | metadata: 18 | name: app2 19 | spec: 20 | replicas: 3 21 | template: 22 | spec: 23 | containers: 24 | - name: app 25 | limit: 26 | cpu: 1 -------------------------------------------------------------------------------- /tests/checks/use-namespace.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: dont-fire 6 | namespace: namespace-dev 7 | --- 8 | apiVersion: apps/v1 9 | kind: Deployment 10 | metadata: 11 | name: app1 12 | namespace: default -------------------------------------------------------------------------------- /tests/checks/wildcard-in-rules.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: role1 6 | namespace: namespace-dev 7 | rules: 8 | - apiGroups: [""] 9 | resources: ["pods"] 10 | verbs: ["get"] 11 | --- 12 | apiVersion: rbac.authorization.k8s.io/v1 13 | kind: Role 14 | metadata: 15 | name: role1 16 | namespace: namespace-dev 17 | rules: 18 | - apiGroups: [""] 19 | resources: ["secrets"] 20 | verbs: ["*"] 21 | -------------------------------------------------------------------------------- /tests/checks/writable-host-mount.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: app1 6 | spec: 7 | selector: 8 | matchLabels: 9 | app.kubernetes.io/name: app1 10 | template: 11 | spec: 12 | containers: 13 | - name: app 14 | volumeMounts: 15 | - name: config 16 | mountPath: /config 17 | volumes: 18 | - name: config 19 | hostPath: 20 | path: /config 21 | --- 22 | apiVersion: apps.openshift.io/v1 23 | kind: DeploymentConfig 24 | metadata: 25 | name: app2 26 | spec: 27 | selector: 28 | app.kubernetes.io/name: app2 29 | template: 30 | spec: 31 | containers: 32 | - name: app2 33 | runAsUser: 0 34 | volumeMounts: 35 | - name: config 36 | mountPath: /config 37 | volumes: 38 | - name: config 39 | hostPath: 40 | path: /config -------------------------------------------------------------------------------- /tests/testdata/mychart-0.1.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackrox/kube-linter/e3a413f1435f6c1b4debd181c794ca0706e42660/tests/testdata/mychart-0.1.0.tgz -------------------------------------------------------------------------------- /tests/testdata/mychart/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /tests/testdata/mychart/Chart.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: subchart 3 | repository: file://../subchart 4 | version: 0.1.0 5 | digest: sha256:f8645a52640009a9d8905d929d3362e04720964c71e37af357b8b5e2852c365d 6 | generated: "2023-11-23T15:35:33.69184771+01:00" 7 | -------------------------------------------------------------------------------- /tests/testdata/mychart/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | appVersion: 1.16.0 3 | description: A Helm chart for Kubernetes 4 | name: mychart 5 | type: application 6 | version: 0.1.0 7 | dependencies: 8 | - name: subchart 9 | version: 0.1.0 10 | repository: file://../subchart 11 | -------------------------------------------------------------------------------- /tests/testdata/mychart/charts/subchart-0.1.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackrox/kube-linter/e3a413f1435f6c1b4debd181c794ca0706e42660/tests/testdata/mychart/charts/subchart-0.1.0.tgz -------------------------------------------------------------------------------- /tests/testdata/mychart/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "mychart.fullname" . }} 6 | labels: 7 | {{- include "mychart.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "mychart.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /tests/testdata/mychart/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $fullName := include "mychart.fullname" . -}} 3 | {{- $svcPort := .Values.service.port -}} 4 | {{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} 5 | apiVersion: networking.k8s.io/v1beta1 6 | {{- else -}} 7 | apiVersion: extensions/v1beta1 8 | {{- end }} 9 | kind: Ingress 10 | metadata: 11 | name: {{ $fullName }} 12 | labels: 13 | {{- include "mychart.labels" . | nindent 4 }} 14 | {{- with .Values.ingress.annotations }} 15 | annotations: 16 | {{- toYaml . | nindent 4 }} 17 | {{- end }} 18 | spec: 19 | {{- if .Values.ingress.tls }} 20 | tls: 21 | {{- range .Values.ingress.tls }} 22 | - hosts: 23 | {{- range .hosts }} 24 | - {{ . | quote }} 25 | {{- end }} 26 | secretName: {{ .secretName }} 27 | {{- end }} 28 | {{- end }} 29 | rules: 30 | {{- range .Values.ingress.hosts }} 31 | - host: {{ .host | quote }} 32 | http: 33 | paths: 34 | {{- range .paths }} 35 | - path: {{ . }} 36 | backend: 37 | serviceName: {{ $fullName }} 38 | servicePort: {{ $svcPort }} 39 | {{- end }} 40 | {{- end }} 41 | {{- end }} 42 | -------------------------------------------------------------------------------- /tests/testdata/mychart/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "mychart.fullname" . }} 5 | labels: 6 | {{- include "mychart.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "mychart.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /tests/testdata/mychart/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "mychart.serviceAccountName" . }} 6 | labels: 7 | {{- include "mychart.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /tests/testdata/mychart/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "mychart.fullname" . }}-test-connection" 5 | labels: 6 | {{- include "mychart.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "mychart.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /tests/testdata/splunk.yaml: -------------------------------------------------------------------------------- 1 | kind: Service 2 | apiVersion: v1 3 | metadata: 4 | name: splunk 5 | namespace: splunk-ns 6 | spec: 7 | selector: 8 | app: splunk 9 | ports: 10 | - name: http 11 | protocol: TCP 12 | port: 8000 13 | targetPort: 8000 14 | - name: https 15 | protocol: TCP 16 | port: 8088 17 | targetPort: 8088 18 | --- 19 | apiVersion: apps/v1 20 | kind: Deployment 21 | metadata: 22 | name: splunk 23 | namespace: splunk-ns 24 | labels: 25 | apps: splunk 26 | spec: 27 | replicas: 1 28 | selector: 29 | matchLabels: 30 | app: splunk 31 | template: 32 | metadata: 33 | labels: 34 | app: splunk 35 | spec: 36 | containers: 37 | - name: splunk 38 | image: splunk/splunk:8.1.2 39 | securityContext: 40 | privileged: true 41 | ports: 42 | - containerPort: 8000 43 | - containerPort: 8088 44 | env: 45 | - name: SPLUNK_START_ARGS 46 | value: --accept-license 47 | - name: SPLUNK_USER 48 | value: root 49 | - name: SPLUNK_PASSWORD 50 | value: helloworld 51 | -------------------------------------------------------------------------------- /tests/testdata/subchart/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /tests/testdata/subchart/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | appVersion: 1.16.0 3 | description: A Helm chart for Kubernetes 4 | name: subchart 5 | type: application 6 | version: 0.1.0 7 | -------------------------------------------------------------------------------- /tool-imports/empty.go: -------------------------------------------------------------------------------- 1 | package toolimports 2 | 3 | // Empty file to ensure that this directory has at least one Go file even without build tags. 4 | -------------------------------------------------------------------------------- /tool-imports/tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | package toolimports 5 | 6 | // This file declares dependencies on tool for `go mod` purposes. 7 | // See https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module 8 | // for an explanation of the approach. 9 | 10 | import ( 11 | _ "github.com/golangci/golangci-lint/v2/cmd/golangci-lint" 12 | _ "github.com/goreleaser/goreleaser" 13 | ) 14 | --------------------------------------------------------------------------------