From 9944fc5389724cfd830c762280184ca6586560c9 Mon Sep 17 00:00:00 2001 From: andrewkreuzer Date: Fri, 10 May 2024 11:58:40 -0400 Subject: [PATCH] feat: checkov scans for helm charts --- Dockerfile | 6 ++ test/linters/checkov/bad/bad_chart/Chart.yaml | 24 +++++++ .../bad/bad_chart/templates/_helpers.tpl | 62 +++++++++++++++++++ .../bad/bad_chart/templates/deployment.yaml | 37 +++++++++++ .../linters/checkov/bad/bad_chart/values.yaml | 34 ++++++++++ .../checkov/good/good_chart/Chart.yaml | 24 +++++++ .../good/good_chart/templates/_helpers.tpl | 62 +++++++++++++++++++ .../good/good_chart/templates/deployment.yaml | 42 +++++++++++++ .../checkov/good/good_chart/values.yaml | 40 ++++++++++++ 9 files changed, 331 insertions(+) create mode 100644 test/linters/checkov/bad/bad_chart/Chart.yaml create mode 100644 test/linters/checkov/bad/bad_chart/templates/_helpers.tpl create mode 100644 test/linters/checkov/bad/bad_chart/templates/deployment.yaml create mode 100644 test/linters/checkov/bad/bad_chart/values.yaml create mode 100644 test/linters/checkov/good/good_chart/Chart.yaml create mode 100644 test/linters/checkov/good/good_chart/templates/_helpers.tpl create mode 100644 test/linters/checkov/good/good_chart/templates/deployment.yaml create mode 100644 test/linters/checkov/good/good_chart/values.yaml diff --git a/Dockerfile b/Dockerfile index f406b7e8445..494ec1693da 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,7 @@ FROM alpine/terragrunt:1.8.2 as terragrunt FROM dotenvlinter/dotenv-linter:3.3.0 as dotenv-linter FROM ghcr.io/terraform-linters/tflint:v0.50.3 as tflint FROM ghcr.io/yannh/kubeconform:v0.6.4 as kubeconfrm +FROM alpine/helm:3.14.4 as helm FROM golang:1.22.2-alpine as golang FROM golangci/golangci-lint:v1.57.2 as golangci-lint FROM goreleaser/goreleaser:v1.25.1 as goreleaser @@ -259,6 +260,11 @@ COPY dependencies/google-java-format /google-java-format RUN --mount=type=secret,id=GITHUB_TOKEN /install-google-java-format.sh \ && rm -rfv /install-google-java-format.sh /google-java-format +################ +# Install Helm # +################ +COPY --from=helm /usr/bin/helm /usr/bin/ + # Copy Node tools COPY --from=npm-builder /node_modules /node_modules diff --git a/test/linters/checkov/bad/bad_chart/Chart.yaml b/test/linters/checkov/bad/bad_chart/Chart.yaml new file mode 100644 index 00000000000..ce3f59c1101 --- /dev/null +++ b/test/linters/checkov/bad/bad_chart/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: bad_chart +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/test/linters/checkov/bad/bad_chart/templates/_helpers.tpl b/test/linters/checkov/bad/bad_chart/templates/_helpers.tpl new file mode 100644 index 00000000000..ae4809be5a1 --- /dev/null +++ b/test/linters/checkov/bad/bad_chart/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "bad_chart.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "bad_chart.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "bad_chart.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "bad_chart.labels" -}} +helm.sh/chart: {{ include "bad_chart.chart" . }} +{{ include "bad_chart.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "bad_chart.selectorLabels" -}} +app.kubernetes.io/name: {{ include "bad_chart.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "bad_chart.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "bad_chart.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/test/linters/checkov/bad/bad_chart/templates/deployment.yaml b/test/linters/checkov/bad/bad_chart/templates/deployment.yaml new file mode 100644 index 00000000000..e749919bf00 --- /dev/null +++ b/test/linters/checkov/bad/bad_chart/templates/deployment.yaml @@ -0,0 +1,37 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "bad_chart.fullname" . }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "bad_chart.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "bad_chart.labels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "bad_chart.serviceAccountName" . }} + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + livenessProbe: + httpGet: + path: / + port: http + readinessProbe: + httpGet: + path: / + port: http + resources: + {{- toYaml .Values.resources | nindent 12 }} diff --git a/test/linters/checkov/bad/bad_chart/values.yaml b/test/linters/checkov/bad/bad_chart/values.yaml new file mode 100644 index 00000000000..6a8a7c68d0f --- /dev/null +++ b/test/linters/checkov/bad/bad_chart/values.yaml @@ -0,0 +1,34 @@ +replicaCount: 1 + +image: + repository: nginx + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Automatically mount a ServiceAccount's API credentials? + automount: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} +podLabels: {} + +podSecurityContext: {} + +securityContext: {} + +service: + port: 80 + +resources: {} diff --git a/test/linters/checkov/good/good_chart/Chart.yaml b/test/linters/checkov/good/good_chart/Chart.yaml new file mode 100644 index 00000000000..460218a7adc --- /dev/null +++ b/test/linters/checkov/good/good_chart/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: good_chart +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/test/linters/checkov/good/good_chart/templates/_helpers.tpl b/test/linters/checkov/good/good_chart/templates/_helpers.tpl new file mode 100644 index 00000000000..a4d8fba8ff1 --- /dev/null +++ b/test/linters/checkov/good/good_chart/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "good_chart.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "good_chart.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "good_chart.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "good_chart.labels" -}} +helm.sh/chart: {{ include "good_chart.chart" . }} +{{ include "good_chart.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "good_chart.selectorLabels" -}} +app.kubernetes.io/name: {{ include "good_chart.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "good_chart.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "good_chart.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/test/linters/checkov/good/good_chart/templates/deployment.yaml b/test/linters/checkov/good/good_chart/templates/deployment.yaml new file mode 100644 index 00000000000..7836a0dc542 --- /dev/null +++ b/test/linters/checkov/good/good_chart/templates/deployment.yaml @@ -0,0 +1,42 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "good_chart.fullname" . }} + namespace: test +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "good_chart.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "good_chart.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ include "good_chart.serviceAccountName" . }} + automountServiceAccountToken: {{ .Values.serviceAccount.automount }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}@{{ .Values.image.digest }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + livenessProbe: + httpGet: + path: / + port: http + readinessProbe: + httpGet: + path: / + port: http + resources: + {{- toYaml .Values.resources | nindent 12 }} diff --git a/test/linters/checkov/good/good_chart/values.yaml b/test/linters/checkov/good/good_chart/values.yaml new file mode 100644 index 00000000000..f870e37e892 --- /dev/null +++ b/test/linters/checkov/good/good_chart/values.yaml @@ -0,0 +1,40 @@ +replicaCount: 1 + +image: + repository: nginx + pullPolicy: IfNotPresent + tag: "" + digest: "bXlzdXBlcnNlY3JldGhpZGRlbnN0cmluZwo" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + create: true + automount: false + name: "" + +podSecurityContext: + seccompProfile: + type: RuntimeDefault + +securityContext: + capabilities: + drop: + - ALL + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 10001 + +service: + port: 80 + +resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 100m + memory: 128Mi