diff --git a/.github/linters/.checkov.yaml b/.github/linters/.checkov.yaml index fe4590f..4031a76 100644 --- a/.github/linters/.checkov.yaml +++ b/.github/linters/.checkov.yaml @@ -5,8 +5,13 @@ directory: skip-path: - tests skip-check: - - CKV_K8S_49 # Minimize wildcard use in Roles and ClusterRoles - - CKV_K8S_155 # Minimize ClusterRoles that grant control over validating or mutating admission webhook configurations - - CKV_K8S_156 # Minimize ClusterRoles that grant permissions to approve CertificateSigningRequests - - CKV_K8S_157 # Minimize Roles and ClusterRoles that grant permissions to bind RoleBindings or ClusterRoleBindings - - CKV_K8S_158 # Minimize Roles and ClusterRoles that grant permissions to escalate Roles or ClusterRoles + # CKV_K8S_49: Minimize wildcard use in Roles and ClusterRoles + - CKV_K8S_49 + # CKV_K8S_155: ClusterRoles for admission webhook configurations + - CKV_K8S_155 + # CKV_K8S_156: ClusterRoles to approve CertificateSigningRequests + - CKV_K8S_156 + # CKV_K8S_157: Roles/ClusterRoles to bind RoleBindings or ClusterRoleBindings + - CKV_K8S_157 + # CKV_K8S_158: Roles/ClusterRoles to escalate Roles or ClusterRoles + - CKV_K8S_158 diff --git a/.github/workflows/superlinter.yml b/.github/workflows/superlinter.yml index bb67637..632f02e 100644 --- a/.github/workflows/superlinter.yml +++ b/.github/workflows/superlinter.yml @@ -14,3 +14,6 @@ jobs: with: sl_env: | VALIDATE_BIOME_FORMAT=false + # Exclude Helm templates ({{ }} not valid YAML for yamllint/kubeconform) + FILTER_REGEX_EXCLUDE=.*/templates/.* + VALIDATE_GITHUB_ACTIONS_ZIZMOR=false diff --git a/.helmignore b/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/.helmignore @@ -0,0 +1,23 @@ +# 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/ diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..00f0856 --- /dev/null +++ b/.yamllint @@ -0,0 +1,11 @@ +extends: default +ignore: + - templates/ + - "**/templates/**" +rules: + document-start: disable + line-length: + max: 120 + brackets: + min-spaces-inside: 0 + max-spaces-inside: 1 diff --git a/Chart.yaml b/Chart.yaml index 4fa33cc..01c3f61 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1,6 +1,9 @@ apiVersion: v2 -description: A Helm chart to serve as the Validated Patterns Template -keywords: - - pattern -name: vp-template -version: 0.0.1 +name: zero-trust-workload-identity-manager +description: Zero Trust Workload Identity Manager Helm Chart +type: application +version: 0.1.0 +home: https://github.com/validatedpatterns/ztwim-chart +maintainers: + - name: Validated Patterns Team + email: validatedpatterns@googlegroups.com diff --git a/Makefile b/Makefile index 319317a..f0ffb4c 100644 --- a/Makefile +++ b/Makefile @@ -36,8 +36,10 @@ test: helm-lint helm-unittest ## Runs helm lint and unit tests .PHONY: super-linter super-linter: ## Runs super linter locally rm -rf .mypy_cache - podman run -e RUN_LOCAL=true -e USE_FIND_ALGORITHM=true \ - -e VALIDATE_BIOME_FORMAT=false \ - -v $(PWD):/tmp/lint:rw,z \ - -w /tmp/lint \ - ghcr.io/super-linter/super-linter:slim-v8 + podman run -e RUN_LOCAL=true -e USE_FIND_ALGORITHM=true \ + -e VALIDATE_BIOME_FORMAT=false \ + -e "FILTER_REGEX_EXCLUDE=.*/templates/.*" \ + -e VALIDATE_GITHUB_ACTIONS_ZIZMOR=false \ + -v $(PWD):/tmp/lint:rw,z \ + -w /tmp/lint \ + ghcr.io/super-linter/super-linter:slim-v8 diff --git a/README.md b/README.md index b7aa861..145bf0f 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,73 @@ -# vp-template +# zero-trust-workload-identity-manager -![Version: 0.0.1](https://img.shields.io/badge/Version-0.0.1-informational?style=flat-square) + -A Helm chart to serve as the Validated Patterns Template +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) + + + + + +Zero Trust Workload Identity Manager Helm Chart + + This chart is used to serve as the template for Validated Patterns Charts ## Notable changes +**Homepage:** + +## Maintainers + +| Name | Email | Url | +| ----------------------- | ------------------------------------ | --- | +| Validated Patterns Team | | | + + + +## Values + +| Key | Type | Default | Description | +| -------------------------------------------------------------------------------- | ------ | -------------------------------------------------------------------------------- | ----------- | +| global.hubClusterDomain | string | `"hub.example.com"` | | +| global.localClusterDomain | string | `"local.example.com"` | | +| spiffe.csi.agentSocketPath | string | `"/run/spire/agent-sockets"` | | +| spire.agent.nodeAttestor.k8sPSATEnabled | string | `"true"` | | +| spire.agent.workloadAttestors.k8sEnabled | string | `"true"` | | +| spire.agent.workloadAttestors.workloadAttestorsVerification.hostCertBasePath | string | `"/var/lib/kubelet/pki"` | | +| spire.agent.workloadAttestors.workloadAttestorsVerification.hostCertFileName | string | `""` | | +| spire.agent.workloadAttestors.workloadAttestorsVerification.type | string | `"auto"` | | +| spire.bundleConfigMap | string | `"spire-bundle"` | | +| spire.clusterName | string | `"cluster"` | | +| spire.oidcDiscoveryProvider.ingress.annotations."route.openshift.io/termination" | string | `"reencrypt"` | | +| spire.oidcDiscoveryProvider.ingress.host | string | `"spire-spiffe-oidc-discovery-provider.{{ .Values.global.localClusterDomain }}"` | | +| spire.oidcDiscoveryProvider.ingress.operatorManaged | string | `"true"` | | +| spire.oidcDiscoveryProvider.service.name | string | `"spire-spiffe-oidc-discovery-provider"` | | +| spire.oidcDiscoveryProvider.service.port | int | `443` | | +| spire.server.ca.commonName | string | `"redhat.com"` | | +| spire.server.ca.country | string | `"US"` | | +| spire.server.ca.organization | string | `"Red Hat"` | | +| spire.server.datastore.connMaxLifetime | int | `0` | | +| spire.server.datastore.connectionString | string | `"/run/spire/data/datastore.sqlite3"` | | +| spire.server.datastore.databaseType | string | `"sqlite3"` | | +| spire.server.datastore.maxIdleConns | int | `10` | | +| spire.server.datastore.maxOpenConns | int | `100` | | +| spire.server.federation.bundleEndpoint.profile | string | `"https_spiffe"` | | +| spire.server.federation.enabled | string | `"false"` | | +| spire.server.federation.federatesWith | list | `[]` | | +| spire.server.federation.ingress.annotations."route.openshift.io/termination" | string | `"passthrough"` | | +| spire.server.federation.ingress.host | string | `"spire-server.{{ .Values.global.localClusterDomain }}"` | | +| spire.server.federation.ingress.operatorManaged | string | `"true"` | | +| spire.server.persistence.accessMode | string | `"ReadWriteOnce"` | | +| spire.server.persistence.size | string | `"5Gi"` | | +| spire.server.persistence.storageClass | string | `""` | | +| spire.server.service.name | string | `"spire-server"` | | +| spire.server.service.port | int | `443` | | +| spire.trustDomain | string | `"{{ .Values.global.localClusterDomain }}"` | | + + + --- Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/README.md.gotmpl b/README.md.gotmpl index bf3762f..c426f84 100644 --- a/README.md.gotmpl +++ b/README.md.gotmpl @@ -1,9 +1,13 @@ {{ template "chart.header" . }} {{ template "chart.deprecationWarning" . }} + {{ template "chart.badgesSection" . }} + + {{ template "chart.description" . }} + This chart is used to serve as the template for Validated Patterns Charts @@ -17,6 +21,8 @@ This chart is used to serve as the template for Validated Patterns Charts {{ template "chart.requirementsSection" . }} + {{ template "chart.valuesSection" . }} + {{ template "helm-docs.versionFooter" . }} diff --git a/templates/.keep b/templates/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/templates/SpiffeCSIDriver.yaml b/templates/SpiffeCSIDriver.yaml new file mode 100644 index 0000000..e6803c7 --- /dev/null +++ b/templates/SpiffeCSIDriver.yaml @@ -0,0 +1,6 @@ +apiVersion: operator.openshift.io/v1alpha1 +kind: SpiffeCSIDriver +metadata: + name: cluster +spec: + agentSocketPath: {{ .Values.spiffe.csi.agentSocketPath }} diff --git a/templates/SpireAgent.yaml b/templates/SpireAgent.yaml new file mode 100644 index 0000000..34851a1 --- /dev/null +++ b/templates/SpireAgent.yaml @@ -0,0 +1,11 @@ +apiVersion: operator.openshift.io/v1alpha1 +kind: SpireAgent +metadata: + name: cluster +spec: + nodeAttestor: + k8sPSATEnabled: {{ .Values.spire.agent.nodeAttestor.k8sPSATEnabled | quote }} + workloadAttestors: + k8sEnabled: {{ .Values.spire.agent.workloadAttestors.k8sEnabled | quote }} + workloadAttestorsVerification: + type: {{ .Values.spire.agent.workloadAttestors.workloadAttestorsVerification.type}} diff --git a/templates/SpireOIDCDiscoveryProvider-Ingress.yaml b/templates/SpireOIDCDiscoveryProvider-Ingress.yaml new file mode 100644 index 0000000..b0fd9c3 --- /dev/null +++ b/templates/SpireOIDCDiscoveryProvider-Ingress.yaml @@ -0,0 +1,22 @@ +{{- if not (eq .Values.spire.oidcDiscoveryProvider.ingress.operatorManaged "true") }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: spire-spiffe-oidc-discovery-provider + namespace: {{ .Release.Namespace }} + {{- if .Values.spire.server.ingress.annotations }} + annotations: + {{- tpl (toYaml .Values.spire.oidcDiscoveryProvider.ingress.annotations) . | nindent 4 }} + {{- end }} +spec: + rules: + - host: {{ tpl .Values.spire.oidcDiscoveryProvider.ingress.host $ }} + http: + paths: + - pathType: ImplementationSpecific + backend: + service: + name: {{ .Values.spire.oidcDiscoveryProvider.service.name }} + port: + number: {{ .Values.spire.oidcDiscoveryProvider.service.port }} +{{- end }} \ No newline at end of file diff --git a/templates/SpireOIDCDiscoveryProvider.yaml b/templates/SpireOIDCDiscoveryProvider.yaml new file mode 100644 index 0000000..f07849f --- /dev/null +++ b/templates/SpireOIDCDiscoveryProvider.yaml @@ -0,0 +1,7 @@ +apiVersion: operator.openshift.io/v1alpha1 +kind: SpireOIDCDiscoveryProvider +metadata: + name: cluster +spec: + jwtIssuer: {{ include "zero-trust-workload-identity-manager.jwtIssuer" . }} + managedRoute: {{ (.Values.spire.oidcDiscoveryProvider.ingress.operatorManaged | default true) | quote }} diff --git a/templates/SpireServer-Ingress.yaml b/templates/SpireServer-Ingress.yaml new file mode 100644 index 0000000..89e6cfe --- /dev/null +++ b/templates/SpireServer-Ingress.yaml @@ -0,0 +1,22 @@ +{{- if not (eq .Values.spire.server.federation.ingress.operatorManaged "true") }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: spire-server + namespace: {{ .Release.Namespace }} + {{- if .Values.spire.server.federation.ingress.annotations }} + annotations: + {{- tpl (toYaml .Values.spire.server.federation.ingress.annotations) . | nindent 4 }} + {{- end }} +spec: + rules: + - host: {{ tpl .Values.spire.server.federation.ingress.host $ }} + http: + paths: + - pathType: ImplementationSpecific + backend: + service: + name: {{ .Values.spire.server.service.name }} + port: + number: {{ .Values.spire.server.service.port }} +{{- end }} \ No newline at end of file diff --git a/templates/SpireServer.yaml b/templates/SpireServer.yaml new file mode 100644 index 0000000..7b551be --- /dev/null +++ b/templates/SpireServer.yaml @@ -0,0 +1,29 @@ +apiVersion: operator.openshift.io/v1alpha1 +kind: SpireServer +metadata: + name: cluster +spec: + caSubject: + commonName: {{ .Values.spire.server.ca.commonName }} + country: {{ .Values.spire.server.ca.country }} + organization: {{ .Values.spire.server.ca.organization }} + persistence: + size: {{ .Values.spire.server.persistence.size }} + accessMode: {{ .Values.spire.server.persistence.accessMode }} + datastore: + databaseType: {{ .Values.spire.server.datastore.databaseType }} + connectionString: {{ .Values.spire.server.datastore.connectionString }} + maxOpenConns: {{ .Values.spire.server.datastore.maxOpenConns }} + maxIdleConns: {{ .Values.spire.server.datastore.maxIdleConns }} + connMaxLifetime: {{ .Values.spire.server.datastore.connMaxLifetime }} + jwtIssuer: {{ include "zero-trust-workload-identity-manager.jwtIssuer" . }} +{{- if (eq .Values.spire.server.federation.ingress.operatorManaged "true") }} + federation: + bundleEndpoint: + profile: {{ .Values.spire.server.federation.bundleEndpoint.profile }} +{{- if .Values.spire.server.federation.federatesWith }} + federatesWith: + {{- toYaml .Values.spire.server.federation.federatesWith | nindent 6 }} +{{- end }} + managedRoute: {{ (.Values.spire.server.federation.ingress.operatorManaged | default false) | quote }} +{{- end }} diff --git a/templates/ZeroTrustWorkloadIdentityManager.yaml b/templates/ZeroTrustWorkloadIdentityManager.yaml new file mode 100644 index 0000000..6594b15 --- /dev/null +++ b/templates/ZeroTrustWorkloadIdentityManager.yaml @@ -0,0 +1,8 @@ +apiVersion: operator.openshift.io/v1alpha1 +kind: ZeroTrustWorkloadIdentityManager +metadata: + name: cluster +spec: + trustDomain: {{ tpl .Values.spire.trustDomain $ }} + clusterName: {{ .Values.spire.clusterName }} + bundleConfigMap: {{ .Values.spire.bundleConfigMap }} diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl new file mode 100644 index 0000000..abb53a2 --- /dev/null +++ b/templates/_helpers.tpl @@ -0,0 +1,69 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "zero-trust-workload-identity-manager.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 "zero-trust-workload-identity-manager.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 "zero-trust-workload-identity-manager.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "zero-trust-workload-identity-manager.labels" -}} +helm.sh/chart: {{ include "zero-trust-workload-identity-manager.chart" . }} +{{ include "zero-trust-workload-identity-manager.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "zero-trust-workload-identity-manager.selectorLabels" -}} +app.kubernetes.io/name: {{ include "zero-trust-workload-identity-manager.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "zero-trust-workload-identity-manager.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "zero-trust-workload-identity-manager.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "zero-trust-workload-identity-manager.jwtIssuer" -}} +{{- printf "https://%s" (tpl .Values.spire.oidcDiscoveryProvider.ingress.host $) }} +{{- end }} diff --git a/values.yaml b/values.yaml index ed97d53..e0901c4 100644 --- a/values.yaml +++ b/values.yaml @@ -1 +1,61 @@ ---- +global: + localClusterDomain: local.example.com + hubClusterDomain: hub.example.com + +spiffe: + csi: + agentSocketPath: "/run/spire/agent-sockets" + +spire: + trustDomain: "{{ .Values.global.localClusterDomain }}" + clusterName: "cluster" + bundleConfigMap: "spire-bundle" + + agent: + nodeAttestor: + k8sPSATEnabled: "true" + workloadAttestors: + k8sEnabled: "true" + workloadAttestorsVerification: + type: "auto" + hostCertBasePath: /var/lib/kubelet/pki + hostCertFileName: "" + + oidcDiscoveryProvider: + ingress: + operatorManaged: "true" + annotations: + route.openshift.io/termination: reencrypt + host: "spire-spiffe-oidc-discovery-provider.{{ .Values.global.localClusterDomain }}" + service: + name: spire-spiffe-oidc-discovery-provider + port: 443 + + server: + ca: + commonName: redhat.com + country: US + organization: Red Hat + service: + name: spire-server + port: 443 + persistence: + size: 5Gi + accessMode: ReadWriteOnce + storageClass: "" + datastore: + databaseType: sqlite3 + connectionString: /run/spire/data/datastore.sqlite3 + maxOpenConns: 100 + maxIdleConns: 10 + connMaxLifetime: 0 + federation: + enabled: "false" + federatesWith: [] + bundleEndpoint: + profile: "https_spiffe" + ingress: + operatorManaged: "true" + annotations: + route.openshift.io/termination: passthrough + host: "spire-server.{{ .Values.global.localClusterDomain }}"