From 31927afd7d7c12344d1f293d8e3387311705e9d0 Mon Sep 17 00:00:00 2001 From: Frank Jogeleit Date: Sun, 14 Jan 2024 11:32:53 +0100 Subject: [PATCH] init trivy plugin chart Signed-off-by: Frank Jogeleit --- .github/workflows/kyverno-chart.yaml | 2 +- .github/workflows/trivy-chart.yaml.yaml | 70 +++++++ charts/trivy-plugin/.helmignore | 24 +++ charts/trivy-plugin/Chart.yaml | 6 + charts/trivy-plugin/README.md | 69 +++++++ charts/trivy-plugin/README.md.gotmpl | 19 ++ charts/trivy-plugin/config.tmpl | 20 ++ charts/trivy-plugin/templates/NOTES.txt | 22 ++ charts/trivy-plugin/templates/_helpers.tpl | 74 +++++++ .../trivy-plugin/templates/config-secret.yaml | 9 + charts/trivy-plugin/templates/deployment.yaml | 90 +++++++++ charts/trivy-plugin/templates/ingress.yaml | 64 ++++++ .../trivy-plugin/templates/networkpolicy.yaml | 21 ++ .../templates/poddisruptionbudget.yaml | 17 ++ .../trivy-plugin/templates/secret-role.yaml | 14 ++ .../templates/secret-rolebinding.yaml | 16 ++ charts/trivy-plugin/templates/service.yaml | 22 ++ .../templates/serviceaccount.yaml | 13 ++ charts/trivy-plugin/values.yaml | 190 ++++++++++++++++++ plugins/trivy/pkg/config/config.go | 36 +--- plugins/trivy/pkg/config/load.go | 6 + plugins/trivy/pkg/config/resolver.go | 26 +-- .../trivy/pkg/kubernetes/secrets/client.go | 2 +- plugins/trivy/pkg/vulnr/service.go | 2 +- 24 files changed, 776 insertions(+), 58 deletions(-) create mode 100644 .github/workflows/trivy-chart.yaml.yaml create mode 100644 charts/trivy-plugin/.helmignore create mode 100644 charts/trivy-plugin/Chart.yaml create mode 100644 charts/trivy-plugin/README.md create mode 100644 charts/trivy-plugin/README.md.gotmpl create mode 100644 charts/trivy-plugin/config.tmpl create mode 100644 charts/trivy-plugin/templates/NOTES.txt create mode 100644 charts/trivy-plugin/templates/_helpers.tpl create mode 100644 charts/trivy-plugin/templates/config-secret.yaml create mode 100644 charts/trivy-plugin/templates/deployment.yaml create mode 100644 charts/trivy-plugin/templates/ingress.yaml create mode 100644 charts/trivy-plugin/templates/networkpolicy.yaml create mode 100644 charts/trivy-plugin/templates/poddisruptionbudget.yaml create mode 100644 charts/trivy-plugin/templates/secret-role.yaml create mode 100644 charts/trivy-plugin/templates/secret-rolebinding.yaml create mode 100644 charts/trivy-plugin/templates/service.yaml create mode 100644 charts/trivy-plugin/templates/serviceaccount.yaml create mode 100644 charts/trivy-plugin/values.yaml diff --git a/.github/workflows/kyverno-chart.yaml b/.github/workflows/kyverno-chart.yaml index ebf8879..77ebdfa 100644 --- a/.github/workflows/kyverno-chart.yaml +++ b/.github/workflows/kyverno-chart.yaml @@ -1,4 +1,4 @@ -name: kyverno-plugin-helm-release +name: Publish Kyverno Plugin OCI Chart permissions: {} diff --git a/.github/workflows/trivy-chart.yaml.yaml b/.github/workflows/trivy-chart.yaml.yaml new file mode 100644 index 0000000..3912623 --- /dev/null +++ b/.github/workflows/trivy-chart.yaml.yaml @@ -0,0 +1,70 @@ +name: Publish Trivy Plugin OCI Chart + +permissions: {} + +on: + push: + tags: + - 'trivy-plugin-chart-*' + +jobs: + helm-tests: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 + with: + python-version: 3.7 + + - name: Set up chart-testing + uses: helm/chart-testing-action@e6669bcd63d7cb57cb4380c33043eebe5d111992 # v2.6.1 + +# - name: Run chart-testing (lint) +# run: ct lint --target-branch=main --check-version-increment=false --validate-maintainers=false + + linter-artifacthub: + runs-on: ubuntu-latest + container: + image: artifacthub/ah + options: --user root + steps: + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Run ah lint + working-directory: ./charts/ + run: ah lint + + create-release: + runs-on: ubuntu-latest + needs: helm-tests + permissions: + contents: write + packages: write + id-token: write + steps: + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Install Helm + uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78 # v3.5 + with: + version: v3.10.3 + + - name: Install Cosign + uses: sigstore/cosign-installer@9614fae9e5c5eddabb09f90a270fcb487c9f7149 # v3.3.0 + + - name: Login to GitHub Container Registry + run: | + helm registry login --username ${GITHUB_ACTOR} --password ${{ secrets.GITHUB_TOKEN }} ghcr.io + + - name: Login to Cosign + run: | + cosign login --username ${GITHUB_ACTOR} --password ${{ secrets.GITHUB_TOKEN }} ghcr.io + + - name: Publish OCI Charts + run: | + helm package charts/trivy-plugin --destination .dist + helm push .dist/trivy-plugin-*.tgz oci://ghcr.io/${{ github.repository_owner }}/charts/policy-reporter |& tee .digest + cosign sign --yes ghcr.io/${{ github.repository_owner }}/charts/policy-reporter/trivy-plugin@$(cat .digest | awk -F "[, ]+" '/Digest/{print $NF}') \ No newline at end of file diff --git a/charts/trivy-plugin/.helmignore b/charts/trivy-plugin/.helmignore new file mode 100644 index 0000000..ced94a2 --- /dev/null +++ b/charts/trivy-plugin/.helmignore @@ -0,0 +1,24 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ +README.md.gotmpl diff --git a/charts/trivy-plugin/Chart.yaml b/charts/trivy-plugin/Chart.yaml new file mode 100644 index 0000000..0fbddbc --- /dev/null +++ b/charts/trivy-plugin/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: trivy-plugin +description: Trivy Plugin for Policy Reporter UI +type: application +version: 0.0.1 +appVersion: "0.0.2" diff --git a/charts/trivy-plugin/README.md b/charts/trivy-plugin/README.md new file mode 100644 index 0000000..5f1e478 --- /dev/null +++ b/charts/trivy-plugin/README.md @@ -0,0 +1,69 @@ +# trivy-plugin + +Trivy Plugin for Policy Reporter UI + +![Version: 0.0.1](https://img.shields.io/badge/Version-0.0.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.2](https://img.shields.io/badge/AppVersion-0.0.2-informational?style=flat-square) + +## Documentation + +You can find detailed Information and Screens about Features and Configurations in the [Documentation](https://kyverno.github.io/policy-reporter). + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| image.registry | string | `"ghcr.io"` | Image registry | +| image.repository | string | `"kyverno/policy-reporter/trivy-plugin"` | Image repository | +| image.pullPolicy | string | `"IfNotPresent"` | Image PullPolicy | +| image.tag | string | `""` | Image tag Defaults to `Chart.AppVersion` if omitted | +| replicaCount | int | `1` | Deployment replica count | +| logging.encoding | string | `"console"` | log encoding possible encodings are console and json | +| logging.logLevel | int | `0` | log level default info | +| server.port | int | `8080` | Application port | +| server.logging | bool | `false` | Enables Access logging | +| server.basicAuth.username | string | `""` | HTTP BasicAuth username | +| server.basicAuth.password | string | `""` | HTTP BasicAuth password | +| server.basicAuth.secretRef | string | `""` | Read HTTP BasicAuth credentials from secret | +| policyReporter.host | string | `""` | Host of the Policy Reporter Core App | +| policyReporter.skipTLS | bool | `false` | Skip TLS Verification | +| policyReporter.certificate | string | `""` | TLS Certificate | +| policyReporter.basicAuth.username | string | `""` | HTTP BasicAuth Username | +| policyReporter.basicAuth.password | string | `""` | HTTP BasicAuth Password | +| policyReporter.secretRef | string | `""` | Secret to read the API configuration from supports `host`, `certificate`, `skipTLS`, `username`, `password` key | +| imagePullSecrets | list | `[]` | Image pull secrets for image verification policies, this will define the `--imagePullSecrets` argument | +| nameOverride | string | `""` | Override the name of the chart | +| fullnameOverride | string | `""` | Override the expanded name of the chart | +| serviceAccount.create | bool | `true` | Create ServiceAccount | +| serviceAccount.automount | bool | `true` | Enable ServiceAccount automaount | +| serviceAccount.annotations | object | `{}` | Annotations for the ServiceAccount | +| serviceAccount.name | string | `""` | The ServiceAccount name | +| podAnnotations | object | `{}` | Additional annotations to add to each pod | +| podLabels | object | `{}` | Additional labels to add to each pod | +| updateStrategy | object | `{}` | Deployment update strategy. Ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy | +| revisionHistoryLimit | int | `10` | The number of revisions to keep | +| podSecurityContext | object | `{"runAsGroup":1234,"runAsUser":1234}` | Security context for the pod | +| envVars | list | `[]` | Allow additional env variables to be added | +| rbac.enabled | bool | `true` | Create RBAC resources | +| securityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"privileged":false,"readOnlyRootFilesystem":true,"runAsNonRoot":true,"runAsUser":1234,"seccompProfile":{"type":"RuntimeDefault"}}` | Container security context | +| service.type | string | `"ClusterIP"` | Service type. | +| service.port | int | `8080` | Service port. | +| service.annotations | object | `{}` | Service annotations. | +| service.labels | object | `{}` | Service labels. | +| ingress.enabled | bool | `false` | Create ingress resource. | +| ingress.className | string | `""` | Ingress class name. | +| ingress.labels | object | `{}` | Ingress labels. | +| ingress.annotations | object | `{}` | Ingress annotations. | +| ingress.hosts | list | `[]` | List of ingress host configurations. | +| ingress.tls | list | `[]` | List of ingress TLS configurations. | +| networkPolicy.enabled | bool | `false` | When true, use a NetworkPolicy to allow ingress to the webhook This is useful on clusters using Calico and/or native k8s network policies in a default-deny setup. | +| networkPolicy.egress | list | `[{"ports":[{"port":6443,"protocol":"TCP"}]}]` | A list of valid from selectors according to https://kubernetes.io/docs/concepts/services-networking/network-policies. Enables Kubernetes API Server by default | +| networkPolicy.ingress | list | `[]` | A list of valid from selectors according to https://kubernetes.io/docs/concepts/services-networking/network-policies. | +| resources | object | `{}` | | +| podDisruptionBudget.minAvailable | int | `1` | Configures the minimum available pods for kyvernoPlugin disruptions. Cannot be used if `maxUnavailable` is set. | +| podDisruptionBudget.maxUnavailable | string | `nil` | Configures the maximum unavailable pods for kyvernoPlugin disruptions. Cannot be used if `minAvailable` is set. | +| nodeSelector | object | `{}` | Node labels for pod assignment | +| tolerations | list | `[]` | List of node taints to tolerate | +| affinity | object | `{}` | Affinity constraints. | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.11.0](https://github.com/norwoodj/helm-docs/releases/v1.11.0) \ No newline at end of file diff --git a/charts/trivy-plugin/README.md.gotmpl b/charts/trivy-plugin/README.md.gotmpl new file mode 100644 index 0000000..da8c138 --- /dev/null +++ b/charts/trivy-plugin/README.md.gotmpl @@ -0,0 +1,19 @@ +{{ template "chart.header" . }} +{{ template "chart.deprecationWarning" . }} +{{ template "chart.description" . }} + +{{ template "chart.badgesSection" . }} + +## Documentation + +You can find detailed Information and Screens about Features and Configurations in the [Documentation](https://kyverno.github.io/policy-reporter). + +{{ template "chart.valuesSection" . }} + +{{ template "chart.sourcesSection" . }} + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.maintainersSection" . }} + +{{ template "helm-docs.versionFooter" . }} \ No newline at end of file diff --git a/charts/trivy-plugin/config.tmpl b/charts/trivy-plugin/config.tmpl new file mode 100644 index 0000000..9a36684 --- /dev/null +++ b/charts/trivy-plugin/config.tmpl @@ -0,0 +1,20 @@ +logging: + encoding: {{ .Values.logging.encoding }} + logLevel: {{ .Values.logging.logLevel }} + +server: + logging: {{ .Values.server.logging }} + basicAuth: + username: {{ .Values.server.basicAuth.username }} + password: {{ .Values.server.basicAuth.password }} + secretRef: {{ .Values.server.basicAuth.secretRef }} + +core: + host: {{ .Values.policyReporter.host }} + skipTLS: {{ .Values.policyReporter.skipTLS }} + certificate: {{ .Values.policyReporter.certificate }} + secretRef: {{ .Values.policyReporter.secretRef }} + basicAuth: + username: {{ .Values.policyReporter.basicAuth.username }} + password: {{ .Values.policyReporter.basicAuth.password }} + diff --git a/charts/trivy-plugin/templates/NOTES.txt b/charts/trivy-plugin/templates/NOTES.txt new file mode 100644 index 0000000..f95d8a1 --- /dev/null +++ b/charts/trivy-plugin/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "trivy-plugin.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "trivy-plugin.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "trivy-plugin.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "trivy-plugin.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/charts/trivy-plugin/templates/_helpers.tpl b/charts/trivy-plugin/templates/_helpers.tpl new file mode 100644 index 0000000..f1a133a --- /dev/null +++ b/charts/trivy-plugin/templates/_helpers.tpl @@ -0,0 +1,74 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "trivy-plugin.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 "trivy-plugin.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 "trivy-plugin.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "trivy-plugin.labels" -}} +helm.sh/chart: {{ include "trivy-plugin.chart" . }} +{{ include "trivy-plugin.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "trivy-plugin.selectorLabels" -}} +app.kubernetes.io/name: {{ include "trivy-plugin.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "trivy-plugin.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "trivy-plugin.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{- define "trivy-plugin.podDisruptionBudget" -}} +{{- if and .Values.podDisruptionBudget.minAvailable .Values.podDisruptionBudget.maxUnavailable }} +{{- fail "Cannot set both .Values.podDisruptionBudget.minAvailable and .Values.podDisruptionBudget.maxUnavailable" -}} +{{- end }} +{{- if not .Values.podDisruptionBudget.maxUnavailable }} +minAvailable: {{ default 1 .Values.podDisruptionBudget.minAvailable }} +{{- end }} +{{- if .Values.podDisruptionBudget.maxUnavailable }} +maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }} +{{- end }} +{{- end }} diff --git a/charts/trivy-plugin/templates/config-secret.yaml b/charts/trivy-plugin/templates/config-secret.yaml new file mode 100644 index 0000000..e291532 --- /dev/null +++ b/charts/trivy-plugin/templates/config-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "trivy-plugin.fullname" . }}-config + labels: + {{- include "trivy-plugin.labels" . | nindent 4 }} +type: Opaque +data: + config.yaml: {{ tpl (.Files.Get "config.tmpl") . | b64enc }} \ No newline at end of file diff --git a/charts/trivy-plugin/templates/deployment.yaml b/charts/trivy-plugin/templates/deployment.yaml new file mode 100644 index 0000000..31a740b --- /dev/null +++ b/charts/trivy-plugin/templates/deployment.yaml @@ -0,0 +1,90 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "trivy-plugin.fullname" . }} + labels: + {{- include "trivy-plugin.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + {{- with .Values.updateStrategy }} + strategy: + {{- toYaml . | nindent 4 }} + {{- end }} + selector: + matchLabels: + {{- include "trivy-plugin.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + checksum/secret: {{ include (print .Template.BasePath "/config-secret.yaml") . | sha256sum | quote }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "trivy-plugin.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "trivy-plugin.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + args: + - run + - --config=/app/config.yaml + - --port={{ .Values.server.port }} + ports: + - name: http + containerPort: {{ .Values.server.port }} + protocol: TCP + livenessProbe: + httpGet: + path: /api/vulnr/policies + port: http + readinessProbe: + httpGet: + path: /api/vulnr/policies + port: http + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + - name: config-file + mountPath: /app/config.yaml + subPath: config.yaml + readOnly: true + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- with .Values.envVars }} + {{- . | toYaml | trim | nindent 10 }} + {{- end }} + volumes: + - name: config-file + secret: + secretName: {{ include "trivy-plugin.fullname" . }}-config + optional: true + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/charts/trivy-plugin/templates/ingress.yaml b/charts/trivy-plugin/templates/ingress.yaml new file mode 100644 index 0000000..c72ba3b --- /dev/null +++ b/charts/trivy-plugin/templates/ingress.yaml @@ -0,0 +1,64 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "trivy-plugin.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "trivy-plugin.labels" . | nindent 4 }} + {{- with .Values.ingress.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/trivy-plugin/templates/networkpolicy.yaml b/charts/trivy-plugin/templates/networkpolicy.yaml new file mode 100644 index 0000000..1544ffa --- /dev/null +++ b/charts/trivy-plugin/templates/networkpolicy.yaml @@ -0,0 +1,21 @@ +{{- if .Values.networkPolicy.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: {{- include "trivy-plugin.labels" . | nindent 4 }} + name: {{ include "trivy-plugin.fullname" . }} +spec: + podSelector: + matchLabels: {{- include "trivy-plugin.selectorLabels" . | nindent 6 }} + policyTypes: + - Ingress + - Egress + {{- with .Values.networkPolicy.ingress }} + ingress: + {{- toYaml . | nindent 2 }} + {{- end }} + {{- with .Values.networkPolicy.egress }} + egress: + {{- toYaml . | nindent 2 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/trivy-plugin/templates/poddisruptionbudget.yaml b/charts/trivy-plugin/templates/poddisruptionbudget.yaml new file mode 100644 index 0000000..5a1b9bd --- /dev/null +++ b/charts/trivy-plugin/templates/poddisruptionbudget.yaml @@ -0,0 +1,17 @@ +{{- if (gt (int .Values.replicaCount) 1) }} +{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" }} +apiVersion: policy/v1 +{{- else }} +apiVersion: policy/v1beta1 +{{- end }} +kind: PodDisruptionBudget +metadata: + name: {{ include "trivy-plugin.fullname" . }} + labels: + {{- include "trivy-plugin.labels" . | nindent 4 }} +spec: +{{- include "trivy-plugin.podDisruptionBudget" . | indent 2 }} + selector: + matchLabels: + {{- include "trivy-plugin.selectorLabels" . | nindent 6 }} +{{- end }} \ No newline at end of file diff --git a/charts/trivy-plugin/templates/secret-role.yaml b/charts/trivy-plugin/templates/secret-role.yaml new file mode 100644 index 0000000..917ac17 --- /dev/null +++ b/charts/trivy-plugin/templates/secret-role.yaml @@ -0,0 +1,14 @@ +{{- if and .Values.serviceAccount.create .Values.rbac.enabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + {{- include "trivy-plugin.labels" . | nindent 4 }} + name: {{ include "trivy-plugin.fullname" . }}-secret-reader +rules: +- apiGroups: [''] + resources: + - secrets + verbs: + - get +{{- end -}} \ No newline at end of file diff --git a/charts/trivy-plugin/templates/secret-rolebinding.yaml b/charts/trivy-plugin/templates/secret-rolebinding.yaml new file mode 100644 index 0000000..7c4e8ad --- /dev/null +++ b/charts/trivy-plugin/templates/secret-rolebinding.yaml @@ -0,0 +1,16 @@ +{{- if and .Values.serviceAccount.create .Values.rbac.enabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "trivy-plugin.fullname" . }}-secret-reader + labels: + {{- include "trivy-plugin.labels" . | nindent 4 }} +roleRef: + kind: Role + name: {{ include "trivy-plugin.fullname" . }}-secret-reader + apiGroup: rbac.authorization.k8s.io +subjects: +- kind: "ServiceAccount" + name: {{ include "trivy-plugin.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{- end -}} \ No newline at end of file diff --git a/charts/trivy-plugin/templates/service.yaml b/charts/trivy-plugin/templates/service.yaml new file mode 100644 index 0000000..25a784c --- /dev/null +++ b/charts/trivy-plugin/templates/service.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "trivy-plugin.fullname" . }} + labels: + {{- include "trivy-plugin.labels" . | nindent 4 }} + {{- with .Values.service.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "trivy-plugin.selectorLabels" . | nindent 4 }} diff --git a/charts/trivy-plugin/templates/serviceaccount.yaml b/charts/trivy-plugin/templates/serviceaccount.yaml new file mode 100644 index 0000000..bac1ae3 --- /dev/null +++ b/charts/trivy-plugin/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "trivy-plugin.serviceAccountName" . }} + labels: + {{- include "trivy-plugin.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/charts/trivy-plugin/values.yaml b/charts/trivy-plugin/values.yaml new file mode 100644 index 0000000..1dfc12c --- /dev/null +++ b/charts/trivy-plugin/values.yaml @@ -0,0 +1,190 @@ +image: + # -- (string) Image registry + registry: ghcr.io + # -- (string) Image repository + repository: kyverno/policy-reporter/trivy-plugin + # -- (string) Image PullPolicy + pullPolicy: IfNotPresent + # -- (string) Image tag + # Defaults to `Chart.AppVersion` if omitted + tag: "" + +# -- Deployment replica count +replicaCount: 1 + +logging: + # -- log encoding + # possible encodings are console and json + encoding: console + # -- log level + # default info + logLevel: 0 + +server: + # -- Application port + port: 8080 + # -- Enables Access logging + logging: false + basicAuth: + # -- HTTP BasicAuth username + username: "" + # -- HTTP BasicAuth password + password: "" + # -- Read HTTP BasicAuth credentials from secret + secretRef: "" + +policyReporter: + # -- Host of the Policy Reporter Core App + host: "http://policy-reporter:8080" + # -- Skip TLS Verification + skipTLS: false + # -- TLS Certificate + certificate: "" + basicAuth: + # -- HTTP BasicAuth Username + username: "" + # -- HTTP BasicAuth Password + password: "" + # -- Secret to read the API configuration from + # supports `host`, `certificate`, `skipTLS`, `username`, `password` key + secretRef: "" + +# -- Image pull secrets for image verification policies, this will define the `--imagePullSecrets` argument +imagePullSecrets: [] + # regcred: + # registry: foo.example.com + # username: foobar + # password: secret + +# -- (string) Override the name of the chart +nameOverride: "" +# -- (string) Override the expanded name of the chart +fullnameOverride: "" + +serviceAccount: + # -- Create ServiceAccount + create: true + # -- Enable ServiceAccount automaount + automount: true + # -- Annotations for the ServiceAccount + annotations: {} + # -- The ServiceAccount name + name: "" + +# -- Additional annotations to add to each pod +podAnnotations: {} + +# -- Additional labels to add to each pod +podLabels: {} + +# -- Deployment update strategy. +# Ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy +updateStrategy: {} +# rollingUpdate: +# maxSurge: 1 +# maxUnavailable: 40% +# type: RollingUpdate + +# -- The number of revisions to keep +revisionHistoryLimit: 10 + +# -- Security context for the pod +podSecurityContext: + runAsUser: 1234 + runAsGroup: 1234 + +# -- Allow additional env variables to be added +envVars: [] + +rbac: + # -- Create RBAC resources + enabled: true + +# -- Container security context +securityContext: + runAsUser: 1234 + runAsNonRoot: true + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault + +service: + # -- Service type. + type: ClusterIP + # -- Service port. + port: 8080 + # -- Service annotations. + annotations: {} + # -- Service labels. + labels: {} + +ingress: + # -- Create ingress resource. + enabled: false + # -- Ingress class name. + className: "" + # -- Ingress labels. + labels: {} + # -- Ingress annotations. + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + # -- List of ingress host configurations. + hosts: [] + # - host: chart-example.local + # paths: + # - path: / + # pathType: ImplementationSpecific + # -- List of ingress TLS configurations. + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +networkPolicy: + # -- When true, use a NetworkPolicy to allow ingress to the webhook + # This is useful on clusters using Calico and/or native k8s network policies in a default-deny setup. + enabled: false + # -- A list of valid from selectors according to https://kubernetes.io/docs/concepts/services-networking/network-policies. + # Enables Kubernetes API Server by default + egress: + - ports: + - protocol: TCP + port: 6443 + # -- A list of valid from selectors according to https://kubernetes.io/docs/concepts/services-networking/network-policies. + ingress: [] + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +# enabled if replicaCount > 1 +podDisruptionBudget: + # -- Configures the minimum available pods for kyvernoPlugin disruptions. + # Cannot be used if `maxUnavailable` is set. + minAvailable: 1 + # -- Configures the maximum unavailable pods for kyvernoPlugin disruptions. + # Cannot be used if `minAvailable` is set. + maxUnavailable: + +# -- Node labels for pod assignment +nodeSelector: {} + +# -- List of node taints to tolerate +tolerations: [] + + # -- Affinity constraints. +affinity: {} diff --git a/plugins/trivy/pkg/config/config.go b/plugins/trivy/pkg/config/config.go index 0b28073..943a757 100644 --- a/plugins/trivy/pkg/config/config.go +++ b/plugins/trivy/pkg/config/config.go @@ -19,28 +19,6 @@ type Server struct { BasicAuth BasicAuth `mapstructure:"basicAuth"` } -type Results struct { - MaxPerReport int `mapstructure:"maxPerReport"` - KeepOnlyLatest bool `mapstructure:"keepOnlyLatest"` -} - -type LeaderElection struct { - LockName string `mapstructure:"lockName"` - PodName string `mapstructure:"podName"` - LeaseDuration int `mapstructure:"leaseDuration"` - RenewDeadline int `mapstructure:"renewDeadline"` - RetryPeriod int `mapstructure:"retryPeriod"` - ReleaseOnCancel bool `mapstructure:"releaseOnCancel"` - Enabled bool `mapstructure:"enabled"` -} - -type BlockReports struct { - Enabled bool `mapstructure:"enabled"` - Results Results `mapstructure:"results"` - Source string `mapstructure:"source"` - EventNamespace string `mapstructure:"eventNamespace"` -} - type CoreAPI struct { Host string `mapstructure:"host"` SkipTLS bool `mapstructure:"skipTLS"` @@ -70,12 +48,10 @@ func (a CoreAPI) FromValues(values secrets.Values) CoreAPI { } type Config struct { - KubeConfig clientcmd.ConfigOverrides - Namespace string `mapstructure:"namespace"` - Logging logging.Config `mapstructure:"logging"` - Server Server `mapstructure:"server"` - Local bool `mapstructure:"local"` - BlockReports BlockReports `mapstructure:"blockReports"` - LeaderElection LeaderElection `mapstructure:"leaderElection"` - CoreAPI CoreAPI `mapstructure:"core"` + KubeConfig clientcmd.ConfigOverrides + Namespace string `mapstructure:"namespace"` + Logging logging.Config `mapstructure:"logging"` + Server Server `mapstructure:"server"` + Local bool `mapstructure:"local"` + CoreAPI CoreAPI `mapstructure:"core"` } diff --git a/plugins/trivy/pkg/config/load.go b/plugins/trivy/pkg/config/load.go index 2518d77..fd78084 100644 --- a/plugins/trivy/pkg/config/load.go +++ b/plugins/trivy/pkg/config/load.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/spf13/viper" + "go.uber.org/zap" ) func Load(c *Config, cfgFile string) error { @@ -23,6 +24,11 @@ func Load(c *Config, cfgFile string) error { if err := v.ReadInConfig(); err != nil { fmt.Printf("[INFO] No configuration file found: %v\n", err) } + + if err := v.BindEnv("namespace", "POD_NAMESPACE"); err != nil { + zap.L().Warn("failed to bind env POD_NAMESPACE") + } + err := v.Unmarshal(c) return err diff --git a/plugins/trivy/pkg/config/resolver.go b/plugins/trivy/pkg/config/resolver.go index 1601b9c..79ec648 100644 --- a/plugins/trivy/pkg/config/resolver.go +++ b/plugins/trivy/pkg/config/resolver.go @@ -151,30 +151,6 @@ func (r *Resolver) Server(ctx context.Context, options []server.ServerOption) (* return serv, nil } -func (r *Resolver) LeaderElectionClient() (*leaderelection.Client, error) { - if r.leaderClient != nil { - return r.leaderClient, nil - } - - clientset, err := r.Clientset() - if err != nil { - return nil, err - } - - r.leaderClient = leaderelection.New( - clientset.CoordinationV1(), - r.config.LeaderElection.LockName, - r.config.Namespace, - r.config.LeaderElection.PodName, - time.Duration(r.config.LeaderElection.LeaseDuration)*time.Second, - time.Duration(r.config.LeaderElection.RenewDeadline)*time.Second, - time.Duration(r.config.LeaderElection.RetryPeriod)*time.Second, - r.config.LeaderElection.ReleaseOnCancel, - ) - - return r.leaderClient, nil -} - func (r *Resolver) LoadBasicAuth(ctx context.Context, secretRef string) (*BasicAuth, error) { values, err := r.SecretClient().Get(ctx, secretRef) if err != nil { @@ -245,7 +221,7 @@ func (r *Resolver) VulnrService() (*vulnr.Service, error) { return nil, err } - return vulnr.New(cve, r.GHClient(), gocache.New(gocache.NoExpiration, gocache.NoExpiration)), nil + return vulnr.New(cve, r.GHClient(), gocache.New(24*time.Hour, 1*time.Hour)), nil } func NewResolver(config *Config) Resolver { diff --git a/plugins/trivy/pkg/kubernetes/secrets/client.go b/plugins/trivy/pkg/kubernetes/secrets/client.go index 54bdd7c..8ad0df5 100644 --- a/plugins/trivy/pkg/kubernetes/secrets/client.go +++ b/plugins/trivy/pkg/kubernetes/secrets/client.go @@ -11,7 +11,7 @@ import ( ) type Values struct { - Host string `json:"api" mapstructure:"api"` + Host string `json:"host" mapstructure:"api"` Certificate string `json:"certificate" mapstructure:"certificate"` SkipTLS bool `json:"skipTLS" mapstructure:"skipTLS"` Username string `json:"username" mapstructure:"username"` diff --git a/plugins/trivy/pkg/vulnr/service.go b/plugins/trivy/pkg/vulnr/service.go index cb14a06..54b3f53 100644 --- a/plugins/trivy/pkg/vulnr/service.go +++ b/plugins/trivy/pkg/vulnr/service.go @@ -44,7 +44,7 @@ func (s *Service) Get(ctx context.Context, name string) (*Vulnerability, error) details = MapCVE(cve, trivyCVE) } - s.cache.Set(name, details, gocache.NoExpiration) + s.cache.Set(name, details, gocache.DefaultExpiration) return details, nil }