From 13043647704b7ea996ebe887665e95f82952f048 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Apr 2024 19:27:38 -0700 Subject: [PATCH 01/37] Bump helm.sh/helm/v3 from 3.14.3 to 3.14.4 in /tests (#325) Bumps [helm.sh/helm/v3](https://github.com/helm/helm) from 3.14.3 to 3.14.4. - [Release notes](https://github.com/helm/helm/releases) - [Commits](https://github.com/helm/helm/compare/v3.14.3...v3.14.4) --- updated-dependencies: - dependency-name: helm.sh/helm/v3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: kfox1111 --- tests/go.mod | 2 +- tests/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/go.mod b/tests/go.mod index 463447c99..334b12e6c 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -7,7 +7,7 @@ toolchain go1.21.5 require ( github.com/onsi/ginkgo/v2 v2.17.1 github.com/onsi/gomega v1.32.0 - helm.sh/helm/v3 v3.14.3 + helm.sh/helm/v3 v3.14.4 ) require ( diff --git a/tests/go.sum b/tests/go.sum index 5f675dab7..81cacdb90 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -212,8 +212,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -helm.sh/helm/v3 v3.14.3 h1:HmvRJlwyyt9HjgmAuxHbHv3PhMz9ir/XNWHyXfmnOP4= -helm.sh/helm/v3 v3.14.3/go.mod h1:v6myVbyseSBJTzhmeE39UcPLNv6cQK6qss3dvgAySaE= +helm.sh/helm/v3 v3.14.4 h1:6FSpEfqyDalHq3kUr4gOMThhgY55kXUEjdQoyODYnrM= +helm.sh/helm/v3 v3.14.4/go.mod h1:Tje7LL4gprZpuBNTbG34d1Xn5NmRT3OWfBRwpOSer9I= k8s.io/api v0.29.0 h1:NiCdQMY1QOp1H8lfRyeEf8eOwV6+0xA6XEE44ohDX2A= k8s.io/api v0.29.0/go.mod h1:sdVmXoz2Bo/cb77Pxi71IPTSErEW32xa4aXwKH7gfBA= k8s.io/apiextensions-apiserver v0.29.0 h1:0VuspFG7Hj+SxyF/Z/2T0uFbI5gb5LRgEyUVE3Q4lV0= From 260b02f973bdf4787feeb75bc61a4c8c3eeb71c9 Mon Sep 17 00:00:00 2001 From: kfox1111 Date: Tue, 16 Apr 2024 08:35:07 -0700 Subject: [PATCH 02/37] Add an easy to use identity for child servers (#302) Signed-off-by: Kevin Fox Co-authored-by: Faisal Memon --- charts/spire/charts/spire-server/README.md | 3 +++ .../templates/controller-manager-cluster-ids.yaml | 9 ++++++--- charts/spire/charts/spire-server/values.yaml | 8 ++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index 5e503cc20..a7c50c701 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -270,6 +270,9 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `controllerManager.identities.clusterSPIFFEIDs.default.admin` | Indicates any pod matched by this identity will be an admin. Use this with extreme care. | `false` | | `controllerManager.identities.clusterSPIFFEIDs.default.downstream` | Set if this spire instance is a root server and the workloads are downstream servers. | `false` | | `controllerManager.identities.clusterSPIFFEIDs.default.autoPopulateDNSNames` | Auto populate DNS names from services attached to pods | `false` | +| `controllerManager.identities.clusterSPIFFEIDs.child-servers.enabled` | Enable this identity for controller manager | `false` | +| `controllerManager.identities.clusterSPIFFEIDs.child-servers.type` | The type of rule this is. | `child-servers` | +| `controllerManager.identities.clusterSPIFFEIDs.child-servers.downstream` | Set if this spire instance is a root server and the workloads are downstream servers. | `true` | | `controllerManager.identities.clusterSPIFFEIDs.oidc-discovery-provider.enabled` | Enable this identity for controller manager | `true` | | `controllerManager.identities.clusterSPIFFEIDs.oidc-discovery-provider.type` | The type of rule this is. | `oidc-discovery-provider` | | `controllerManager.identities.clusterSPIFFEIDs.oidc-discovery-provider.autoPopulateDNSNames` | Auto populate DNS names to the discovery provider | `true` | diff --git a/charts/spire/charts/spire-server/templates/controller-manager-cluster-ids.yaml b/charts/spire/charts/spire-server/templates/controller-manager-cluster-ids.yaml index cbd5400c5..7642d7af3 100644 --- a/charts/spire/charts/spire-server/templates/controller-manager-cluster-ids.yaml +++ b/charts/spire/charts/spire-server/templates/controller-manager-cluster-ids.yaml @@ -9,7 +9,10 @@ values: {{ .namespaces | toYaml | nindent 2 }} {{- end }} {{- define "spire-controller-manager-identity-podselector" }} -{{- if eq .type "oidc-discovery-provider" }} +{{- if eq .type "child-servers" }} +matchLabels: + component: server +{{- else if eq .type "oidc-discovery-provider" }} matchLabels: release: {{ .Release.Name }} release-namespace: {{ .Release.Namespace }} @@ -34,8 +37,8 @@ matchLabels: {{- if eq ($root.Values.controllerManager.enabled | toString) "true" }} {{- if or (not (hasKey $value "enabled")) (eq ($value.enabled | toString) "true") }} {{- $type := dig "type" "base" $value }} -{{- if not (has $type (list "base" "raw" "oidc-discovery-provider" "test-keys")) }} -{{- fail (printf "Type given: %s, must be one of [base, raw, oidc-discovery-provider, test-keys]" $type) }} +{{- if not (has $type (list "base" "raw" "child-servers" "oidc-discovery-provider" "test-keys")) }} +{{- fail (printf "Type given: %s, must be one of [base, raw, child-servers, oidc-discovery-provider, test-keys]" $type) }} {{- end }} {{- $namespaceSelector := deepCopy (dig "namespaceSelector" (dict) $value) }} {{- if ne $type "raw" }} diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index 1e38cbd6e..af0c48cdc 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -579,6 +579,14 @@ controllerManager: ## @param controllerManager.identities.clusterSPIFFEIDs.default.autoPopulateDNSNames Auto populate DNS names from services attached to pods autoPopulateDNSNames: false + child-servers: + ## @param controllerManager.identities.clusterSPIFFEIDs.child-servers.enabled Enable this identity for controller manager + enabled: false + ## @param controllerManager.identities.clusterSPIFFEIDs.child-servers.type The type of rule this is. + type: child-servers + ## @param controllerManager.identities.clusterSPIFFEIDs.child-servers.downstream Set if this spire instance is a root server and the workloads are downstream servers. + downstream: true + oidc-discovery-provider: ## @param controllerManager.identities.clusterSPIFFEIDs.oidc-discovery-provider.enabled Enable this identity for controller manager enabled: true From 08f699bdb08c51919d2b277b331db22a1f809072 Mon Sep 17 00:00:00 2001 From: kfox1111 Date: Thu, 18 Apr 2024 08:21:31 -0700 Subject: [PATCH 03/37] Add spire-lib chart (#289) * Add spire-lib chart Make spire-lib bits into its own library chart. Signed-off-by: Kevin Fox * Apply suggestions from code review Co-authored-by: Marco Franssen Signed-off-by: kfox1111 * Move notes back Signed-off-by: Kevin Fox * Fix NOTES Signed-off-by: Kevin Fox --------- Signed-off-by: Kevin Fox Signed-off-by: kfox1111 Co-authored-by: Marco Franssen --- charts/spire/Chart.yaml | 3 +++ charts/spire/charts/spire-lib/.helmignore | 23 ++++++++++++++++++ charts/spire/charts/spire-lib/Chart.yaml | 24 +++++++++++++++++++ charts/spire/charts/spire-lib/README.md | 14 +++++++++++ .../spire-lib/templates/_helpers.tpl} | 0 .../spire-lib/templates/_namespaces.yaml | 5 ++++ .../templates/_spire-server-namespace.yaml} | 6 +++-- .../templates/_spire-system-namespace.yaml} | 6 +++-- charts/spire/charts/spire-lib/values.yaml | 8 +++++++ charts/spire/templates/namespaces.yaml | 1 + 10 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 charts/spire/charts/spire-lib/.helmignore create mode 100644 charts/spire/charts/spire-lib/Chart.yaml create mode 100644 charts/spire/charts/spire-lib/README.md rename charts/spire/{templates/_spire-lib.tpl => charts/spire-lib/templates/_helpers.tpl} (100%) create mode 100644 charts/spire/charts/spire-lib/templates/_namespaces.yaml rename charts/spire/{templates/spire-server-namespace.yaml => charts/spire-lib/templates/_spire-server-namespace.yaml} (83%) rename charts/spire/{templates/spire-system-namespace.yaml => charts/spire-lib/templates/_spire-system-namespace.yaml} (81%) create mode 100644 charts/spire/charts/spire-lib/values.yaml create mode 100644 charts/spire/templates/namespaces.yaml diff --git a/charts/spire/Chart.yaml b/charts/spire/Chart.yaml index 3be046f13..3839ce265 100644 --- a/charts/spire/Chart.yaml +++ b/charts/spire/Chart.yaml @@ -22,6 +22,9 @@ maintainers: email: edwbuck@gmail.com kubeVersion: ">=1.21.0-0" dependencies: + - name: spire-lib + repository: file://./charts/spire-lib + version: 0.1.0 - name: spire-server condition: spire-server.enabled repository: file://./charts/spire-server diff --git a/charts/spire/charts/spire-lib/.helmignore b/charts/spire/charts/spire-lib/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/charts/spire/charts/spire-lib/.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/charts/spire/charts/spire-lib/Chart.yaml b/charts/spire/charts/spire-lib/Chart.yaml new file mode 100644 index 000000000..4f7bda246 --- /dev/null +++ b/charts/spire/charts/spire-lib/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: spire-lib +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: library + +# 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: "" diff --git a/charts/spire/charts/spire-lib/README.md b/charts/spire/charts/spire-lib/README.md new file mode 100644 index 000000000..0fd47f0a1 --- /dev/null +++ b/charts/spire/charts/spire-lib/README.md @@ -0,0 +1,14 @@ +# SPIRE Common Library Chart + +A [Helm Library Chart](https://helm.sh/docs/topics/library_charts/#helm) for grouping common logic between SPIRE charts. + +## TL;DR + +```yaml +dependencies: + - name: spire-lib + version: 0.1.0 + repository: https://spiffe.github.io/helm-charts-hardened/ +``` + +## Parameters diff --git a/charts/spire/templates/_spire-lib.tpl b/charts/spire/charts/spire-lib/templates/_helpers.tpl similarity index 100% rename from charts/spire/templates/_spire-lib.tpl rename to charts/spire/charts/spire-lib/templates/_helpers.tpl diff --git a/charts/spire/charts/spire-lib/templates/_namespaces.yaml b/charts/spire/charts/spire-lib/templates/_namespaces.yaml new file mode 100644 index 000000000..4071e2808 --- /dev/null +++ b/charts/spire/charts/spire-lib/templates/_namespaces.yaml @@ -0,0 +1,5 @@ +{{- define "spire-lib.namespaces" }} +{{- include "spire-lib.namespace.system" . }} +--- +{{- include "spire-lib.namespace.server" . }} +{{- end }} diff --git a/charts/spire/templates/spire-server-namespace.yaml b/charts/spire/charts/spire-lib/templates/_spire-server-namespace.yaml similarity index 83% rename from charts/spire/templates/spire-server-namespace.yaml rename to charts/spire/charts/spire-lib/templates/_spire-server-namespace.yaml index a0ac41b91..37a959b3f 100644 --- a/charts/spire/templates/spire-server-namespace.yaml +++ b/charts/spire/charts/spire-lib/templates/_spire-server-namespace.yaml @@ -1,12 +1,13 @@ -{{- define "spire.namespace.default_server_labels" }} +{{- define "spire-lib.namespace.default_server_labels" }} "pod-security.kubernetes.io/warn": restricted "pod-security.kubernetes.io/audit": restricted "pod-security.kubernetes.io/enforce": restricted {{- end }} +{{- define "spire-lib.namespace.server" }} {{- if or .Values.global.spire.namespaces.create .Values.global.spire.namespaces.server.create }} {{- $labels := dict }} {{- if and (dig "spire" "recommendations" "enabled" false .Values.global) (dig "spire" "recommendations" "namespacePSS" true .Values.global) }} -{{- $labels = mergeOverwrite $labels (include "spire.namespace.default_server_labels" . | fromYaml) }} +{{- $labels = mergeOverwrite $labels (include "spire-lib.namespace.default_server_labels" . | fromYaml) }} {{- if (dig "openshift" false .Values.global) }} {{- $_ := set $labels "security.openshift.io/scc.podSecurityLabelSync" "false" }} {{- if (index .Values "spiffe-oidc-discovery-provider").enabled }} @@ -28,3 +29,4 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} {{- end }} +{{- end }} diff --git a/charts/spire/templates/spire-system-namespace.yaml b/charts/spire/charts/spire-lib/templates/_spire-system-namespace.yaml similarity index 81% rename from charts/spire/templates/spire-system-namespace.yaml rename to charts/spire/charts/spire-lib/templates/_spire-system-namespace.yaml index 3ddf3c150..2e0c5c2e4 100644 --- a/charts/spire/templates/spire-system-namespace.yaml +++ b/charts/spire/charts/spire-lib/templates/_spire-system-namespace.yaml @@ -1,12 +1,13 @@ -{{- define "spire.namespace.default_system_labels" }} +{{- define "spire-lib.namespace.default_system_labels" }} "pod-security.kubernetes.io/warn": privileged "pod-security.kubernetes.io/audit": privileged "pod-security.kubernetes.io/enforce": privileged {{- end }} +{{- define "spire-lib.namespace.system" }} {{- if or .Values.global.spire.namespaces.create .Values.global.spire.namespaces.system.create }} {{- $labels := dict }} {{- if and (dig "spire" "recommendations" "enabled" false .Values.global) (dig "spire" "recommendations" "namespacePSS" true .Values.global) }} -{{- $labels = mergeOverwrite $labels (include "spire.namespace.default_system_labels" . | fromYaml) }} +{{- $labels = mergeOverwrite $labels (include "spire-lib.namespace.default_system_labels" . | fromYaml) }} {{- if (dig "openshift" false .Values.global) }} {{- $_ := set $labels "security.openshift.io/scc.podSecurityLabelSync" "false" }} {{- end }} @@ -25,3 +26,4 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} {{- end }} +{{- end }} diff --git a/charts/spire/charts/spire-lib/values.yaml b/charts/spire/charts/spire-lib/values.yaml new file mode 100644 index 000000000..ff0e175eb --- /dev/null +++ b/charts/spire/charts/spire-lib/values.yaml @@ -0,0 +1,8 @@ +# Default values for spire-lib. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +## It is required by CI/CD tools and processes. +## @skip exampleValue +## +exampleValue: spire-lib diff --git a/charts/spire/templates/namespaces.yaml b/charts/spire/templates/namespaces.yaml new file mode 100644 index 000000000..62d1a49f6 --- /dev/null +++ b/charts/spire/templates/namespaces.yaml @@ -0,0 +1 @@ +{{- include "spire-lib.namespaces" . }} From 184372690aa4ef397c662adb1e0c8fd9189aeb70 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Apr 2024 06:58:44 -0700 Subject: [PATCH 04/37] Bump github.com/onsi/gomega from 1.32.0 to 1.33.0 in /tests (#332) Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.32.0 to 1.33.0. - [Release notes](https://github.com/onsi/gomega/releases) - [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/gomega/compare/v1.32.0...v1.33.0) --- updated-dependencies: - dependency-name: github.com/onsi/gomega dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/go.mod | 10 +++++----- tests/go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/go.mod b/tests/go.mod index 334b12e6c..fc0d7a9b4 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -6,7 +6,7 @@ toolchain go1.21.5 require ( github.com/onsi/ginkgo/v2 v2.17.1 - github.com/onsi/gomega v1.32.0 + github.com/onsi/gomega v1.33.0 helm.sh/helm/v3 v3.14.4 ) @@ -47,11 +47,11 @@ require ( github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect - golang.org/x/crypto v0.18.0 // indirect - golang.org/x/net v0.20.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/net v0.23.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect - golang.org/x/sys v0.16.0 // indirect - golang.org/x/term v0.16.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.17.0 // indirect diff --git a/tests/go.sum b/tests/go.sum index 81cacdb90..6741ec22d 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -92,8 +92,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8= github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= -github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk= -github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg= +github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE= +github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -135,8 +135,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -148,8 +148,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -165,13 +165,13 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= From 3d2ac166b956a86b10df6af95457aba05ad89074 Mon Sep 17 00:00:00 2001 From: marcofranssen <694733+marcofranssen@users.noreply.github.com> Date: Mon, 15 Apr 2024 08:01:19 +0000 Subject: [PATCH 05/37] Bump test chart dependencies Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .github/tests/charts.json | 6 +++--- .../spire/charts/spiffe-oidc-discovery-provider/README.md | 4 ++-- .../spire/charts/spiffe-oidc-discovery-provider/values.yaml | 4 ++-- charts/spire/charts/spire-agent/README.md | 4 ++-- charts/spire/charts/spire-agent/values.yaml | 4 ++-- charts/spire/charts/spire-server/README.md | 2 +- charts/spire/charts/spire-server/values.yaml | 2 +- charts/spire/charts/tornjak-frontend/README.md | 2 +- charts/spire/charts/tornjak-frontend/values.yaml | 2 +- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/tests/charts.json b/.github/tests/charts.json index 5e3036960..3beee5371 100644 --- a/.github/tests/charts.json +++ b/.github/tests/charts.json @@ -2,7 +2,7 @@ { "name": "kube-prometheus-stack", "repo": "https://prometheus-community.github.io/helm-charts", - "version": "57.2.0" + "version": "58.1.1" }, { "name": "cert-manager", @@ -17,11 +17,11 @@ { "name": "mysql", "repo": "https://charts.bitnami.com/bitnami", - "version": "10.1.0" + "version": "10.1.1" }, { "name": "postgresql", "repo": "https://charts.bitnami.com/bitnami", - "version": "15.1.4" + "version": "15.2.5" } ] diff --git a/charts/spire/charts/spiffe-oidc-discovery-provider/README.md b/charts/spire/charts/spiffe-oidc-discovery-provider/README.md index 372409d1c..ee8264484 100644 --- a/charts/spire/charts/spiffe-oidc-discovery-provider/README.md +++ b/charts/spire/charts/spiffe-oidc-discovery-provider/README.md @@ -115,11 +115,11 @@ A Helm chart to install the SPIFFE OIDC discovery provider. | `tests.bash.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.bash.image.repository` | The repository within the registry | `chainguard/bash` | | `tests.bash.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:5921884408efe50b77796675dc109ad2126f54476fe7403c37d8898a5ceb2e76` | +| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:d99fd064eb2893d8fc50a81fad92cc49dceffac291974c64b2491a635a022d9c` | | `tests.toolkit.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.toolkit.image.repository` | The repository within the registry | `chainguard/min-toolkit-debug` | | `tests.toolkit.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.toolkit.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:f0369215d5a2c15e469a2a8d993e4db7e83fcda5142c8682f37c197d76f82faa` | +| `tests.toolkit.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:9ec0986b87c86bc62a7db3452bd547a3fc219afcad87a62dadf2aa0aff8d03a5` | | `tests.step.image.registry` | The OCI registry to pull the image from | `docker.io` | | `tests.step.image.repository` | The repository within the registry | `smallstep/step-cli` | | `tests.step.image.pullPolicy` | The image pull policy | `IfNotPresent` | diff --git a/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml b/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml index 1e46d498b..b88c6dd4a 100644 --- a/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml +++ b/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml @@ -328,7 +328,7 @@ tests: registry: cgr.dev repository: chainguard/bash pullPolicy: IfNotPresent - tag: latest@sha256:5921884408efe50b77796675dc109ad2126f54476fe7403c37d8898a5ceb2e76 + tag: latest@sha256:d99fd064eb2893d8fc50a81fad92cc49dceffac291974c64b2491a635a022d9c toolkit: ## @param tests.toolkit.image.registry The OCI registry to pull the image from @@ -340,7 +340,7 @@ tests: registry: cgr.dev repository: chainguard/min-toolkit-debug pullPolicy: IfNotPresent - tag: latest@sha256:f0369215d5a2c15e469a2a8d993e4db7e83fcda5142c8682f37c197d76f82faa + tag: latest@sha256:9ec0986b87c86bc62a7db3452bd547a3fc219afcad87a62dadf2aa0aff8d03a5 step: ## @param tests.step.image.registry The OCI registry to pull the image from diff --git a/charts/spire/charts/spire-agent/README.md b/charts/spire/charts/spire-agent/README.md index 85d87a264..de1c527c8 100644 --- a/charts/spire/charts/spire-agent/README.md +++ b/charts/spire/charts/spire-agent/README.md @@ -70,7 +70,7 @@ A Helm chart to install the SPIRE agent. | `fsGroupFix.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `fsGroupFix.image.repository` | The repository within the registry | `chainguard/bash` | | `fsGroupFix.image.pullPolicy` | The image pull policy | `Always` | -| `fsGroupFix.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:5921884408efe50b77796675dc109ad2126f54476fe7403c37d8898a5ceb2e76` | +| `fsGroupFix.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:d99fd064eb2893d8fc50a81fad92cc49dceffac291974c64b2491a635a022d9c` | | `fsGroupFix.resources` | Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | `{}` | | `keyManager.memory.enabled` | Enable the memory based Key Manager | `true` | | `nodeAttestor.k8sPsat.enabled` | Enable Psat k8s Node Attestor | `true` | @@ -106,7 +106,7 @@ A Helm chart to install the SPIRE agent. | `socketAlternate.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `socketAlternate.image.repository` | The repository within the registry | `chainguard/bash` | | `socketAlternate.image.pullPolicy` | The image pull policy | `Always` | -| `socketAlternate.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:5921884408efe50b77796675dc109ad2126f54476fe7403c37d8898a5ceb2e76` | +| `socketAlternate.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:d99fd064eb2893d8fc50a81fad92cc49dceffac291974c64b2491a635a022d9c` | | `socketAlternate.resources` | Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | `{}` | | `priorityClassName` | Priority class assigned to daemonset pods. Can be auto set with global.recommendations.priorityClassName. | `""` | | `extraEnvVars` | Extra environment variables to be added to the Spire Agent container | `[]` | diff --git a/charts/spire/charts/spire-agent/values.yaml b/charts/spire/charts/spire-agent/values.yaml index be1b1d975..732f63caf 100644 --- a/charts/spire/charts/spire-agent/values.yaml +++ b/charts/spire/charts/spire-agent/values.yaml @@ -154,7 +154,7 @@ fsGroupFix: registry: cgr.dev repository: chainguard/bash pullPolicy: Always - tag: latest@sha256:5921884408efe50b77796675dc109ad2126f54476fe7403c37d8898a5ceb2e76 + tag: latest@sha256:d99fd064eb2893d8fc50a81fad92cc49dceffac291974c64b2491a635a022d9c ## @param fsGroupFix.resources Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ resources: {} @@ -262,7 +262,7 @@ socketAlternate: registry: cgr.dev repository: chainguard/bash pullPolicy: Always - tag: latest@sha256:5921884408efe50b77796675dc109ad2126f54476fe7403c37d8898a5ceb2e76 + tag: latest@sha256:d99fd064eb2893d8fc50a81fad92cc49dceffac291974c64b2491a635a022d9c ## @param socketAlternate.resources Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ resources: {} diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index a7c50c701..5eef5653c 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -383,5 +383,5 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `tests.bash.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.bash.image.repository` | The repository within the registry | `chainguard/bash` | | `tests.bash.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:3b5e8046bc76624f645b97684845c3aef3c27d001db0f3f71d1ffbdf885edea7` | +| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:d99fd064eb2893d8fc50a81fad92cc49dceffac291974c64b2491a635a022d9c` | | `kubeConfigs` | Manage additional kubeconfig files to talk to external Kubernetes clusters | `{}` | diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index af0c48cdc..ef388828c 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -955,7 +955,7 @@ tests: registry: cgr.dev repository: chainguard/bash pullPolicy: IfNotPresent - tag: latest@sha256:3b5e8046bc76624f645b97684845c3aef3c27d001db0f3f71d1ffbdf885edea7 + tag: latest@sha256:d99fd064eb2893d8fc50a81fad92cc49dceffac291974c64b2491a635a022d9c ## @param kubeConfigs [object] Manage additional kubeconfig files to talk to external Kubernetes clusters kubeConfigs: {} diff --git a/charts/spire/charts/tornjak-frontend/README.md b/charts/spire/charts/tornjak-frontend/README.md index 68dc1af58..f24c6fa2b 100644 --- a/charts/spire/charts/tornjak-frontend/README.md +++ b/charts/spire/charts/tornjak-frontend/README.md @@ -98,4 +98,4 @@ port forwarding. See the chart NOTES output for more details. | `tests.bash.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.bash.image.repository` | The repository within the registry | `chainguard/bash` | | `tests.bash.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:5921884408efe50b77796675dc109ad2126f54476fe7403c37d8898a5ceb2e76` | +| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:d99fd064eb2893d8fc50a81fad92cc49dceffac291974c64b2491a635a022d9c` | diff --git a/charts/spire/charts/tornjak-frontend/values.yaml b/charts/spire/charts/tornjak-frontend/values.yaml index f58dd0843..d4f87f4dd 100644 --- a/charts/spire/charts/tornjak-frontend/values.yaml +++ b/charts/spire/charts/tornjak-frontend/values.yaml @@ -154,4 +154,4 @@ tests: registry: cgr.dev repository: chainguard/bash pullPolicy: IfNotPresent - tag: latest@sha256:5921884408efe50b77796675dc109ad2126f54476fe7403c37d8898a5ceb2e76 + tag: latest@sha256:d99fd064eb2893d8fc50a81fad92cc49dceffac291974c64b2491a635a022d9c From 51492566711ebb75262874956a90f75f59ffcbf0 Mon Sep 17 00:00:00 2001 From: Kevin Fox Date: Fri, 19 Apr 2024 07:05:39 -0700 Subject: [PATCH 06/37] Work around curl change Signed-off-by: Kevin Fox --- .../charts/spire-server/templates/tests/test-connection.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/spire/charts/spire-server/templates/tests/test-connection.yaml b/charts/spire/charts/spire-server/templates/tests/test-connection.yaml index 9c48b620e..3ff41c647 100644 --- a/charts/spire/charts/spire-server/templates/tests/test-connection.yaml +++ b/charts/spire/charts/spire-server/templates/tests/test-connection.yaml @@ -24,8 +24,8 @@ spec: curl -k -f -s 'https://{{ include "spire-server.fullname" . }}:{{ .Values.service.port }}' IGNORECA=$? echo $NOCA $IGNORECA - if [ $NOCA -eq 60 -a $IGNORECA -eq 22 ]; then - # We were able to connect to the server but didn't recognize the ca (60) and the page not found (22) because we're not using grpc + if [ $NOCA -eq 60 -a $IGNORECA -eq 56 ]; then + # We were able to connect to the server but didn't recognize the ca (60) and the page not found (56) because we're not using grpc exit 0 fi exit 1 From f679a0dab698a0e527e3d9cebd41067e70f0fd54 Mon Sep 17 00:00:00 2001 From: "spire-helm-version-checker[bot]" <161522935+spire-helm-version-checker[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 06:34:39 -0700 Subject: [PATCH 07/37] Bump test chart dependencies (#333) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: marcofranssen <694733+marcofranssen@users.noreply.github.com> --- .github/tests/charts.json | 2 +- .../spire/charts/spiffe-oidc-discovery-provider/README.md | 6 +++--- .../spire/charts/spiffe-oidc-discovery-provider/values.yaml | 6 +++--- charts/spire/charts/spire-agent/README.md | 4 ++-- charts/spire/charts/spire-agent/values.yaml | 4 ++-- charts/spire/charts/spire-server/README.md | 2 +- charts/spire/charts/spire-server/values.yaml | 2 +- charts/spire/charts/tornjak-frontend/README.md | 2 +- charts/spire/charts/tornjak-frontend/values.yaml | 2 +- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/tests/charts.json b/.github/tests/charts.json index 3beee5371..1d53e08a0 100644 --- a/.github/tests/charts.json +++ b/.github/tests/charts.json @@ -2,7 +2,7 @@ { "name": "kube-prometheus-stack", "repo": "https://prometheus-community.github.io/helm-charts", - "version": "58.1.1" + "version": "58.2.1" }, { "name": "cert-manager", diff --git a/charts/spire/charts/spiffe-oidc-discovery-provider/README.md b/charts/spire/charts/spiffe-oidc-discovery-provider/README.md index ee8264484..c320c311d 100644 --- a/charts/spire/charts/spiffe-oidc-discovery-provider/README.md +++ b/charts/spire/charts/spiffe-oidc-discovery-provider/README.md @@ -69,7 +69,7 @@ A Helm chart to install the SPIFFE OIDC discovery provider. | `insecureScheme.nginx.image.registry` | The OCI registry to pull the image from. Only used when TLS is disabled. | `docker.io` | | `insecureScheme.nginx.image.repository` | The repository within the registry. Only used when TLS is disabled. | `nginxinc/nginx-unprivileged` | | `insecureScheme.nginx.image.pullPolicy` | The image pull policy. Only used when TLS is disabled. | `IfNotPresent` | -| `insecureScheme.nginx.image.tag` | Overrides the image tag whose default is the chart appVersion. Only used when TLS is disabled. | `1.25.4-alpine` | +| `insecureScheme.nginx.image.tag` | Overrides the image tag whose default is the chart appVersion. Only used when TLS is disabled. | `1.25.5-alpine` | | `insecureScheme.nginx.resources` | Resource requests and limits | `{}` | | `jwtIssuer` | Path to JWT issuer. Defaults to oidc-discovery.$trustDomain if unset | `""` | | `config.logLevel` | The log level, valid values are "debug", "info", "warn", and "error" | `info` | @@ -115,11 +115,11 @@ A Helm chart to install the SPIFFE OIDC discovery provider. | `tests.bash.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.bash.image.repository` | The repository within the registry | `chainguard/bash` | | `tests.bash.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:d99fd064eb2893d8fc50a81fad92cc49dceffac291974c64b2491a635a022d9c` | +| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1` | | `tests.toolkit.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.toolkit.image.repository` | The repository within the registry | `chainguard/min-toolkit-debug` | | `tests.toolkit.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.toolkit.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:9ec0986b87c86bc62a7db3452bd547a3fc219afcad87a62dadf2aa0aff8d03a5` | +| `tests.toolkit.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:cbcc74f3a614ebb5455ce4a1c3cf311ffb2ce857833a827e3d0dc07f77760383` | | `tests.step.image.registry` | The OCI registry to pull the image from | `docker.io` | | `tests.step.image.repository` | The repository within the registry | `smallstep/step-cli` | | `tests.step.image.pullPolicy` | The image pull policy | `IfNotPresent` | diff --git a/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml b/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml index b88c6dd4a..049d963e2 100644 --- a/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml +++ b/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml @@ -164,7 +164,7 @@ insecureScheme: registry: docker.io repository: nginxinc/nginx-unprivileged pullPolicy: IfNotPresent - tag: 1.25.4-alpine + tag: 1.25.5-alpine ## @param insecureScheme.nginx.resources Resource requests and limits resources: {} # We usually recommend not to specify default resources and to leave this as a conscious @@ -328,7 +328,7 @@ tests: registry: cgr.dev repository: chainguard/bash pullPolicy: IfNotPresent - tag: latest@sha256:d99fd064eb2893d8fc50a81fad92cc49dceffac291974c64b2491a635a022d9c + tag: latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1 toolkit: ## @param tests.toolkit.image.registry The OCI registry to pull the image from @@ -340,7 +340,7 @@ tests: registry: cgr.dev repository: chainguard/min-toolkit-debug pullPolicy: IfNotPresent - tag: latest@sha256:9ec0986b87c86bc62a7db3452bd547a3fc219afcad87a62dadf2aa0aff8d03a5 + tag: latest@sha256:cbcc74f3a614ebb5455ce4a1c3cf311ffb2ce857833a827e3d0dc07f77760383 step: ## @param tests.step.image.registry The OCI registry to pull the image from diff --git a/charts/spire/charts/spire-agent/README.md b/charts/spire/charts/spire-agent/README.md index de1c527c8..29eefbe35 100644 --- a/charts/spire/charts/spire-agent/README.md +++ b/charts/spire/charts/spire-agent/README.md @@ -70,7 +70,7 @@ A Helm chart to install the SPIRE agent. | `fsGroupFix.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `fsGroupFix.image.repository` | The repository within the registry | `chainguard/bash` | | `fsGroupFix.image.pullPolicy` | The image pull policy | `Always` | -| `fsGroupFix.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:d99fd064eb2893d8fc50a81fad92cc49dceffac291974c64b2491a635a022d9c` | +| `fsGroupFix.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1` | | `fsGroupFix.resources` | Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | `{}` | | `keyManager.memory.enabled` | Enable the memory based Key Manager | `true` | | `nodeAttestor.k8sPsat.enabled` | Enable Psat k8s Node Attestor | `true` | @@ -106,7 +106,7 @@ A Helm chart to install the SPIRE agent. | `socketAlternate.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `socketAlternate.image.repository` | The repository within the registry | `chainguard/bash` | | `socketAlternate.image.pullPolicy` | The image pull policy | `Always` | -| `socketAlternate.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:d99fd064eb2893d8fc50a81fad92cc49dceffac291974c64b2491a635a022d9c` | +| `socketAlternate.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1` | | `socketAlternate.resources` | Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | `{}` | | `priorityClassName` | Priority class assigned to daemonset pods. Can be auto set with global.recommendations.priorityClassName. | `""` | | `extraEnvVars` | Extra environment variables to be added to the Spire Agent container | `[]` | diff --git a/charts/spire/charts/spire-agent/values.yaml b/charts/spire/charts/spire-agent/values.yaml index 732f63caf..dc220e200 100644 --- a/charts/spire/charts/spire-agent/values.yaml +++ b/charts/spire/charts/spire-agent/values.yaml @@ -154,7 +154,7 @@ fsGroupFix: registry: cgr.dev repository: chainguard/bash pullPolicy: Always - tag: latest@sha256:d99fd064eb2893d8fc50a81fad92cc49dceffac291974c64b2491a635a022d9c + tag: latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1 ## @param fsGroupFix.resources Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ resources: {} @@ -262,7 +262,7 @@ socketAlternate: registry: cgr.dev repository: chainguard/bash pullPolicy: Always - tag: latest@sha256:d99fd064eb2893d8fc50a81fad92cc49dceffac291974c64b2491a635a022d9c + tag: latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1 ## @param socketAlternate.resources Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ resources: {} diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index 5eef5653c..7fe6ccf42 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -383,5 +383,5 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `tests.bash.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.bash.image.repository` | The repository within the registry | `chainguard/bash` | | `tests.bash.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:d99fd064eb2893d8fc50a81fad92cc49dceffac291974c64b2491a635a022d9c` | +| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1` | | `kubeConfigs` | Manage additional kubeconfig files to talk to external Kubernetes clusters | `{}` | diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index ef388828c..3417651ce 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -955,7 +955,7 @@ tests: registry: cgr.dev repository: chainguard/bash pullPolicy: IfNotPresent - tag: latest@sha256:d99fd064eb2893d8fc50a81fad92cc49dceffac291974c64b2491a635a022d9c + tag: latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1 ## @param kubeConfigs [object] Manage additional kubeconfig files to talk to external Kubernetes clusters kubeConfigs: {} diff --git a/charts/spire/charts/tornjak-frontend/README.md b/charts/spire/charts/tornjak-frontend/README.md index f24c6fa2b..31d5db158 100644 --- a/charts/spire/charts/tornjak-frontend/README.md +++ b/charts/spire/charts/tornjak-frontend/README.md @@ -98,4 +98,4 @@ port forwarding. See the chart NOTES output for more details. | `tests.bash.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.bash.image.repository` | The repository within the registry | `chainguard/bash` | | `tests.bash.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:d99fd064eb2893d8fc50a81fad92cc49dceffac291974c64b2491a635a022d9c` | +| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1` | diff --git a/charts/spire/charts/tornjak-frontend/values.yaml b/charts/spire/charts/tornjak-frontend/values.yaml index d4f87f4dd..bba2b5168 100644 --- a/charts/spire/charts/tornjak-frontend/values.yaml +++ b/charts/spire/charts/tornjak-frontend/values.yaml @@ -154,4 +154,4 @@ tests: registry: cgr.dev repository: chainguard/bash pullPolicy: IfNotPresent - tag: latest@sha256:d99fd064eb2893d8fc50a81fad92cc49dceffac291974c64b2491a635a022d9c + tag: latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1 From a2494ee45ecf09dacda6733fa701109a0b674bd2 Mon Sep 17 00:00:00 2001 From: Mohammed Abdi Date: Thu, 25 Apr 2024 18:49:20 -0400 Subject: [PATCH 08/37] Add auth option for Tornjak (#259) * Added auth option, specifically keycloak for tornjak production use Signed-off-by: Mohammed Abdi * Added auth values for tornjak Signed-off-by: Mohammed Abdi * Update charts/spire/charts/tornjak-frontend/values.yaml Co-authored-by: Faisal Memon Signed-off-by: Mohammed Abdi * Update examples/tornjak/keycloak/README.md Co-authored-by: Faisal Memon Signed-off-by: Mohammed Abdi * Update examples/tornjak/keycloak/README.md Co-authored-by: Faisal Memon Signed-off-by: Mohammed Abdi * Update examples/tornjak/keycloak/README.md Co-authored-by: Faisal Memon Signed-off-by: Mohammed Abdi * Update examples/tornjak/keycloak/README.md Co-authored-by: Faisal Memon Signed-off-by: Mohammed Abdi * Update examples/tornjak/keycloak/README.md Co-authored-by: Faisal Memon Signed-off-by: Mohammed Abdi * Update examples/tornjak/keycloak/README.md Co-authored-by: Faisal Memon Signed-off-by: Mohammed Abdi * nit Signed-off-by: Mohammed Abdi * nit Signed-off-by: Mohammed Abdi * nit Signed-off-by: Mohammed Abdi * Update examples/tornjak/keycloak/README.md Co-authored-by: Mariusz Sabath Signed-off-by: Mohammed Abdi * nit Signed-off-by: Mohammed Abdi * install keycloak first Signed-off-by: Mohammed Abdi * add logs volume back Signed-off-by: Mohammed Abdi * Fixed NPM init error Signed-off-by: Mariusz Sabath * Fixed the values documentation errors Signed-off-by: Mariusz Sabath * Post-review suggestion fixes Signed-off-by: Mariusz Sabath * Fixed typo Signed-off-by: Mariusz Sabath * Updating Keyclaok examples README Signed-off-by: Mariusz Sabath * Fixed the parameter reference Signed-off-by: Mariusz Sabath * Fix typo Signed-off-by: Mariusz Sabath * use keycloak-config-cli to simplify tornjak realm import Signed-off-by: MohammedAbdi * edit client id Signed-off-by: MohammedAbdi * reverse client id Signed-off-by: MohammedAbdi * fix the doc Signed-off-by: Mariusz Sabath * update tornjak version and backend auth Signed-off-by: MohammedAbdi * update client id Signed-off-by: MohammedAbdi * updates values yaml Signed-off-by: MohammedAbdi * update documentation Signed-off-by: MohammedAbdi * nit Signed-off-by: MohammedAbdi * update doc Signed-off-by: MohammedAbdi * add audience check tornjak Signed-off-by: MohammedAbdi * remove unused file Signed-off-by: MohammedAbdi * update doc Signed-off-by: MohammedAbdi * nit and add auth not enabled warning back Signed-off-by: MohammedAbdi * adjust liveness probe until tornjak handles liveendpoint for auth and direct connection to discovery Signed-off-by: MohammedAbdi * update doc and add keycloak proxy Signed-off-by: MohammedAbdi --------- Signed-off-by: Mohammed Abdi Signed-off-by: Mohammed Abdi Signed-off-by: Mariusz Sabath Signed-off-by: MohammedAbdi Co-authored-by: Mohammed Abdi Co-authored-by: Faisal Memon Co-authored-by: Mariusz Sabath --- charts/spire/charts/spire-server/README.md | 5 +- .../charts/spire-server/templates/NOTES.txt | 8 +- .../templates/server-resource.yaml | 2 +- .../templates/tornjak-config.yaml | 8 ++ charts/spire/charts/spire-server/values.yaml | 10 +- .../spire/charts/tornjak-frontend/Chart.yaml | 2 +- .../spire/charts/tornjak-frontend/README.md | 91 +++++++------ .../tornjak-frontend/templates/NOTES.txt | 10 +- .../templates/deployment.yaml | 8 +- .../spire/charts/tornjak-frontend/values.yaml | 24 ++-- examples/tornjak/README.md | 8 +- examples/tornjak/keycloak/README.md | 114 ++++++++++++++++ examples/tornjak/keycloak/values.yaml | 128 ++++++++++++++++++ examples/tornjak/values-auth.yaml | 12 ++ examples/tornjak/values.yaml | 1 + 15 files changed, 360 insertions(+), 71 deletions(-) create mode 100644 examples/tornjak/keycloak/README.md create mode 100644 examples/tornjak/keycloak/values.yaml create mode 100644 examples/tornjak/values-auth.yaml diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index 7fe6ccf42..9270736be 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -340,7 +340,7 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `tornjak.image.repository` | The repository within the registry | `spiffe/tornjak-backend` | | `tornjak.image.pullPolicy` | The image pull policy | `IfNotPresent` | | `tornjak.image.tag` | Overrides the image tag to be whatever you need it to be. It will always be the flag you set without modifications | `""` | -| `tornjak.image.defaultTag` | Sets the default image to use when image.tag is not set. It will automatically be updated with a ubi- prefix if on OpenShift. | `v1.4.2` | +| `tornjak.image.defaultTag` | Sets the default image to use when image.tag is not set. It will automatically be updated with a ubi- prefix if on OpenShift. | `v1.6.0` | | `tornjak.service.type` | Type of service resource | `ClusterIP` | | `tornjak.service.ports.http` | Insecure port for tornjak service | `10000` | | `tornjak.service.ports.https` | Secure port for tornjak service | `10443` | @@ -361,6 +361,9 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `tornjak.config.dataStore` | Persistent DB for storing Tornjak specific information | | | `tornjak.config.dataStore.driver` | Database driver name | `sqlite3` | | `tornjak.config.dataStore.file` | File path for sqlite3 file | `/run/spire/data/tornjak.sqlite3` | +| `tornjak.config.userManagement` | UserManagement config | | +| `tornjak.config.userManagement.issuer` | UserManagement issuer URL | `""` | +| `tornjak.config.userManagement.audience` | UserManagement audience check | `""` | | `tornjak.config.tlsSecret` | Name of the secret containing server side key and certificate for TLS verification (required for `tls` or `mtls` connectionType) | `tornjak-tls-secret` | | `tornjak.config.clientCA.type` | Type of delivery for the user CA for TLS client verification. Options are `Secret` or `ConfigMap` (required for `mtls` connectionType) | `Secret` | | `tornjak.config.clientCA.name` | Name of the resource secret or configMap with user CA for TLS | `tornjak-client-ca` | diff --git a/charts/spire/charts/spire-server/templates/NOTES.txt b/charts/spire/charts/spire-server/templates/NOTES.txt index 1b3053b61..f4d368fa4 100644 --- a/charts/spire/charts/spire-server/templates/NOTES.txt +++ b/charts/spire/charts/spire-server/templates/NOTES.txt @@ -9,10 +9,12 @@ Installed {{ .Chart.Name }}… Installed {{ include "spire-tornjak.fullname" . }}… -### WARNING ### +{{- if eq .Values.tornjak.config.userManagement.issuer "" }} + ### WARNING ### -Tornjak runs without authentication and is therefore NOT suitable to run in production environments. -Only use in test environments! + Tornjak is configured to run without authentication and is therefore NOT suitable to run in production environments. + Only use in test environments! +{{- end }} Access Tornjak: kubectl -n {{ include "spire-server.namespace" . }} port-forward service/{{ include "spire-tornjak.servicename" . }} {{ .Values.tornjak.service.ports.http }}:10000 diff --git a/charts/spire/charts/spire-server/templates/server-resource.yaml b/charts/spire/charts/spire-server/templates/server-resource.yaml index 4793db97c..a661a3b53 100644 --- a/charts/spire/charts/spire-server/templates/server-resource.yaml +++ b/charts/spire/charts/spire-server/templates/server-resource.yaml @@ -319,7 +319,7 @@ spec: startupProbe: httpGet: scheme: HTTP - path: /api/tornjak/serverinfo + path: / port: 10000 {{- toYaml .Values.tornjak.startupProbe | nindent 12 }} {{- end }} diff --git a/charts/spire/charts/spire-server/templates/tornjak-config.yaml b/charts/spire/charts/spire-server/templates/tornjak-config.yaml index f187e47ba..ec7b2e6e3 100644 --- a/charts/spire/charts/spire-server/templates/tornjak-config.yaml +++ b/charts/spire/charts/spire-server/templates/tornjak-config.yaml @@ -42,5 +42,13 @@ data: } } {{- end }} + {{- if ne .Values.tornjak.config.userManagement.issuer "" }} + UserManagement "KeycloakAuth" { + plugin_data { + issuer = "{{ .Values.tornjak.config.userManagement.issuer }}" + audience = "{{ .Values.tornjak.config.userManagement.audience }}" + } + } + {{- end }} } {{- end }} diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index 3417651ce..4369b9cb5 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -805,7 +805,7 @@ tornjak: repository: spiffe/tornjak-backend pullPolicy: IfNotPresent tag: "" - defaultTag: "v1.4.2" + defaultTag: "v1.6.0" service: ## @param tornjak.service.type Type of service resource @@ -868,7 +868,13 @@ tornjak: driver: "sqlite3" ## @param tornjak.config.dataStore.file File path for sqlite3 file file: "/run/spire/data/tornjak.sqlite3" - + ## @extra tornjak.config.userManagement [object] UserManagement config + userManagement: + ## @param tornjak.config.userManagement.issuer UserManagement issuer URL + issuer: "" + ## @param tornjak.config.userManagement.audience UserManagement audience check + audience: "" + # Tornjak supports 3 connection types: `http`, `tls`, and `mtls`. # The connections are determined based on provided configuration # When `tlsSecret` is created in this chart namespace, the TLS connection is started diff --git a/charts/spire/charts/tornjak-frontend/Chart.yaml b/charts/spire/charts/tornjak-frontend/Chart.yaml index d8f94c80d..2d67db39f 100644 --- a/charts/spire/charts/tornjak-frontend/Chart.yaml +++ b/charts/spire/charts/tornjak-frontend/Chart.yaml @@ -3,7 +3,7 @@ name: tornjak-frontend description: A Helm chart to deploy Tornjak frontend type: application version: 0.1.0 -appVersion: "v1.4.2" +appVersion: "v1.6.0" home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire sources: - https://github.com/spiffe/tornjak diff --git a/charts/spire/charts/tornjak-frontend/README.md b/charts/spire/charts/tornjak-frontend/README.md index 31d5db158..0216f4523 100644 --- a/charts/spire/charts/tornjak-frontend/README.md +++ b/charts/spire/charts/tornjak-frontend/README.md @@ -55,47 +55,50 @@ port forwarding. See the chart NOTES output for more details. ### Chart parameters -| Name | Description | Value | -| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -| `image.registry` | The OCI registry to pull the image from | `ghcr.io` | -| `image.repository` | The repository within the registry | `spiffe/tornjak-frontend` | -| `image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `image.tag` | Overrides the image tag whose default is the chart appVersion | `""` | -| `imagePullSecrets` | Pull secrets for images | `[]` | -| `nameOverride` | Name override | `""` | -| `namespaceOverride` | Namespace override | `""` | -| `fullnameOverride` | Fullname override | `""` | -| `serviceAccount.create` | Specifies whether a service account should be created | `true` | -| `serviceAccount.annotations` | Annotations to add to the service account | `{}` | -| `serviceAccount.name` | The name of the service account to use. If not set and create is true, a name is generated. | `""` | -| `labels` | Labels for tornjak frontend pods | `{}` | -| `podSecurityContext` | Pod security context | `{}` | -| `securityContext` | Security context | `{}` | -| `service.type` | Service type | `ClusterIP` | -| `service.port` | Service port | `3000` | -| `service.annotations` | Annotations for service resource | `{}` | -| `nodeSelector` | Select specific nodes to run on (currently only amd64 is supported by Tornjak) | | -| `affinity` | Affinity rules | `{}` | -| `tolerations` | List of tolerations | `[]` | -| `topologySpreadConstraints` | List of topology spread constraints for resilience | `[]` | -| `apiServerURL` | URL of the Tornjak APIs (backend). Since Tornjak Frontend runs in the browser, this URL must be accessible from the machine running a browser. If unset, autodetection is atempted. | `""` | -| `spireHealthCheck.enabled` | Enables the SPIRE Healthchecker indicator | `true` | -| `startupProbe.enabled` | Enable startupProbe on Tornjak frontend container | `true` | -| `startupProbe.initialDelaySeconds` | Initial delay seconds for startupProbe | `5` | -| `startupProbe.periodSeconds` | Period seconds for startupProbe | `10` | -| `startupProbe.timeoutSeconds` | Timeout seconds for startupProbe | `5` | -| `startupProbe.failureThreshold` | Failure threshold count for startupProbe | `6` | -| `startupProbe.successThreshold` | Success threshold count for startupProbe | `1` | -| `workingDir` | Set to override the default path containing the Tornjak frontend within the image | `""` | -| `ingress.enabled` | Flag to enable ingress for Tornjak frontend service | `false` | -| `ingress.className` | Ingress class name for Tornjak frontend service | `""` | -| `ingress.controllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | -| `ingress.annotations` | Annotations for Tornjak frontend service | `{}` | -| `ingress.host` | Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The rest of the rules will be autogenerated. For more customizability, use hosts[] instead. | `tornjak-frontend` | -| `ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` | -| `ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` | -| `ingress.tls` | Secrets containing TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` | -| `tests.bash.image.registry` | The OCI registry to pull the image from | `cgr.dev` | -| `tests.bash.image.repository` | The repository within the registry | `chainguard/bash` | -| `tests.bash.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1` | +| Name | Description | Value | +| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | +| `image.registry` | The OCI registry to pull the image from | `ghcr.io` | +| `image.repository` | The repository within the registry | `spiffe/tornjak-frontend` | +| `image.pullPolicy` | The image pull policy | `IfNotPresent` | +| `image.tag` | Overrides the image tag whose default is the chart appVersion | `""` | +| `imagePullSecrets` | Pull secrets for images | `[]` | +| `nameOverride` | Name override | `""` | +| `namespaceOverride` | Namespace override | `""` | +| `fullnameOverride` | Fullname override | `""` | +| `serviceAccount.create` | Specifies whether a service account should be created | `true` | +| `serviceAccount.annotations` | Annotations to add to the service account | `{}` | +| `serviceAccount.name` | The name of the service account to use. If not set and create is true, a name is generated. | `""` | +| `labels` | Labels for tornjak frontend pods | `{}` | +| `podSecurityContext` | Pod security context | `{}` | +| `securityContext` | Security context | `{}` | +| `service.type` | Service type | `ClusterIP` | +| `service.port` | Service port | `3000` | +| `service.annotations` | Annotations for service resource | `{}` | +| `nodeSelector` | (Optional) Select specific nodes to run on. Tornjak currently supports amd64 and arm64 architectures | `{}` | +| `affinity` | Affinity rules | `{}` | +| `tolerations` | List of tolerations | `[]` | +| `topologySpreadConstraints` | List of topology spread constraints for resilience | `[]` | +| `apiServerURL` | URL of the Tornjak APIs (backend). Since Tornjak Frontend runs in the browser, this URL must be accessible from the machine running a browser. If not provided, auto-detection is attempted. | `""` | +| `spireHealthCheck.enabled` | Enables the SPIRE Healthchecker indicator | `true` | +| `auth.enabled` | Enables auth for Tornjak | `false` | +| `auth.serverURL` | URL of the Auth service. Tornjak Frontend will redirect to this URL to authenticate the user | `""` | +| `startupProbe.enabled` | Enable startupProbe on Tornjak frontend container | `true` | +| `startupProbe.initialDelaySeconds` | Initial delay seconds for startupProbe | `5` | +| `startupProbe.periodSeconds` | Period seconds for startupProbe | `10` | +| `startupProbe.timeoutSeconds` | Timeout seconds for startupProbe | `5` | +| `startupProbe.failureThreshold` | Failure threshold count for startupProbe | `6` | +| `startupProbe.successThreshold` | Success threshold count for startupProbe | `1` | +| `workingDir` | Set to override the default path containing the Tornjak frontend within the image | `""` | +| `logsDir` | Directory path for NPM logs | `/home/node/` | +| `ingress.enabled` | Flag to enable ingress for Tornjak frontend service | `false` | +| `ingress.className` | Ingress class name for Tornjak frontend service | `""` | +| `ingress.controllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, auto-detection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | +| `ingress.annotations` | Annotations for Tornjak frontend service | `{}` | +| `ingress.host` | Host name for the ingress. If no '.' in host, trustDomain is automatically appended. The rest of the rules will be autogenerated. For more customizability, use hosts[] instead. | `tornjak-frontend` | +| `ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` | +| `ingress.hosts` | Host paths for ingress object. If empty, rules will be built based on the host var. | `[]` | +| `ingress.tls` | Secrets containing TLS certs to enable https on ingress. If empty, rules will be built based on the host and tlsSecret vars. | `[]` | +| `tests.bash.image.registry` | The OCI registry to pull the image from | `cgr.dev` | +| `tests.bash.image.repository` | The repository within the registry | `chainguard/bash` | +| `tests.bash.image.pullPolicy` | The image pull policy | `IfNotPresent` | +| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1` | diff --git a/charts/spire/charts/tornjak-frontend/templates/NOTES.txt b/charts/spire/charts/tornjak-frontend/templates/NOTES.txt index 85a568e41..ad5c90fdb 100644 --- a/charts/spire/charts/tornjak-frontend/templates/NOTES.txt +++ b/charts/spire/charts/tornjak-frontend/templates/NOTES.txt @@ -10,11 +10,15 @@ Tornjak UI (Frontend) pull policy: {{ .Values.image.pullPolicy }} Tornjak API (Backend): {{ include "tornjak-frontend.apiURL" . }} SPIRE health check enabled: "{{ .Values.spireHealthCheck.enabled }}" + User Managemenet enabled: "{{ .Values.auth.enabled }}" + User Managemenet API: "{{ .Values.auth.serverURL }}" -### WARNING ### +{{- if not .Values.auth.enabled }} + ### WARNING ### -Tornjak runs without authentication and is therefore NOT suitable to run in production environments. -Only use in test environments! + Tornjak is configured to run without authentication and is therefore NOT suitable to run in production environments. + Only use in test environments! +{{- end }} Access Tornjak: diff --git a/charts/spire/charts/tornjak-frontend/templates/deployment.yaml b/charts/spire/charts/tornjak-frontend/templates/deployment.yaml index 84e152ba4..97d187a09 100644 --- a/charts/spire/charts/tornjak-frontend/templates/deployment.yaml +++ b/charts/spire/charts/tornjak-frontend/templates/deployment.yaml @@ -35,10 +35,14 @@ spec: env: - name: REACT_APP_API_SERVER_URI value: {{ include "tornjak-frontend.apiURL" . }} - {{- if eq (.Values.spireHealthCheck.enabled | toString) "true" }} + {{- if .Values.spireHealthCheck.enabled }} - name: REACT_APP_SPIRE_HEALTH_CHECK_ENABLE value: "{{ .Values.spireHealthCheck.enabled }}" {{- end }} + {{- if .Values.auth.enabled }} + - name: REACT_APP_AUTH_SERVER_URI + value: "{{ .Values.auth.serverURL }}" + {{- end }} {{- if .Values.startupProbe.enabled }} startupProbe: httpGet: @@ -57,7 +61,7 @@ spec: - name: env mountPath: {{ include "tornjak-frontend.workingDir" . }}/build/tmp - name: logs - mountPath: /opt/app-root/src/.npm/ + mountPath: {{ .Values.logsDir }} {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} diff --git a/charts/spire/charts/tornjak-frontend/values.yaml b/charts/spire/charts/tornjak-frontend/values.yaml index bba2b5168..44ae3ec74 100644 --- a/charts/spire/charts/tornjak-frontend/values.yaml +++ b/charts/spire/charts/tornjak-frontend/values.yaml @@ -63,10 +63,8 @@ service: port: 3000 annotations: {} -## @extra nodeSelector Select specific nodes to run on (currently only amd64 is supported by Tornjak) -nodeSelector: - ## @skip nodeSelector.kubernetes.io/arch - kubernetes.io/arch: amd64 +## @param nodeSelector (Optional) Select specific nodes to run on. Tornjak currently supports amd64 and arm64 architectures +nodeSelector: {} ## @param affinity [object] Affinity rules affinity: {} @@ -86,7 +84,7 @@ topologySpreadConstraints: [] # cpu: 100m # memory: 512Mi -## @param apiServerURL URL of the Tornjak APIs (backend). Since Tornjak Frontend runs in the browser, this URL must be accessible from the machine running a browser. If unset, autodetection is atempted. +## @param apiServerURL URL of the Tornjak APIs (backend). Since Tornjak Frontend runs in the browser, this URL must be accessible from the machine running a browser. If not provided, auto-detection is attempted. apiServerURL: "" # SPIRE Healthchecker indicator @@ -94,6 +92,13 @@ spireHealthCheck: ## @param spireHealthCheck.enabled Enables the SPIRE Healthchecker indicator enabled: true +# User Management +auth: + ## @param auth.enabled Enables auth for Tornjak + enabled: false + ## @param auth.serverURL URL of the Auth service. Tornjak Frontend will redirect to this URL to authenticate the user + serverURL: "" + ## Configure extra options for Tornjak frontend container's startup probe ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-startup-probes ## @param startupProbe.enabled Enable startupProbe on Tornjak frontend container @@ -114,9 +119,12 @@ startupProbe: ## @param workingDir Set to override the default path containing the Tornjak frontend within the image workingDir: "" +## @param logsDir Directory path for NPM logs +logsDir: "/home/node/" + ## @param ingress.enabled Flag to enable ingress for Tornjak frontend service ## @param ingress.className Ingress class name for Tornjak frontend service -## @param ingress.controllerType Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. +## @param ingress.controllerType Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, auto-detection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. ## @param ingress.annotations [object] Annotations for Tornjak frontend service ingress: enabled: false @@ -130,14 +138,14 @@ ingress: ## @param ingress.tlsSecret Secret that has the certs. If blank will use default certs. Used with host var. tlsSecret: "" - ## @param ingress.hosts [array] Host paths for ingress object. If emtpy, rules will be built based on the host var. + ## @param ingress.hosts [array] Host paths for ingress object. If empty, rules will be built based on the host var. hosts: [] # - host: tornjak-frontend.example.org # paths: # - path: / # pathType: Prefix - ## @param ingress.tls [array] Secrets containing TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. + ## @param ingress.tls [array] Secrets containing TLS certs to enable https on ingress. If empty, rules will be built based on the host and tlsSecret vars. tls: [] # - secretName: chart-example-tls # hosts: diff --git a/examples/tornjak/README.md b/examples/tornjak/README.md index 51bdc4da0..cf876ca34 100644 --- a/examples/tornjak/README.md +++ b/examples/tornjak/README.md @@ -1,8 +1,5 @@ # Recommended setup to deploy Tornjak -> [!Warning] -> The current version of Tornjak in this chart is deployed without authentication. Therefore it is not suitable to run this version in production. - To install Spire with the least privileges possible we deploy spire across 2 namespaces. ```shell @@ -13,7 +10,7 @@ kubectl label namespace "spire-server" pod-security.kubernetes.io/enforce=restri # deploy SPIRE with Tornjak enabled helm upgrade --install --namespace spire-server spire charts/spire \ ---values examples/production/values.yaml \ +--values tests/integration/psat/values.yaml \ --values examples/tornjak/values.yaml \ --render-subchart-notes @@ -48,11 +45,10 @@ Update examples/production/example-your-values.yaml with your information, most ```shell helm upgrade --install --namespace spire-server spire charts/spire \ ---values examples/production/values.yaml \ +--values tests/integration/psat/values.yaml \ --values examples/tornjak/values.yaml \ --values examples/tornjak/values-ingress.yaml \ --set global.spire.ingressControllerType=ingress-nginx \ ---values examples/production/example-your-values.yaml \ --render-subchart-notes --debug ``` diff --git a/examples/tornjak/keycloak/README.md b/examples/tornjak/keycloak/README.md new file mode 100644 index 000000000..46cc2c6af --- /dev/null +++ b/examples/tornjak/keycloak/README.md @@ -0,0 +1,114 @@ +# Deploy Tornjak with Authentication Enabled + +This example demonstrates Tornjak's capability to control access to the Frontend Application using +User Management via [Keycloak](https://www.keycloak.org/). + +Tested on: +- Keycloak Application Version - 24.0.3 +- Keycloak Chart Version - 21.0.3 + +For more information regarding Tornjak User Management, please refer to the following documentation: + +* [Tornjak User Management](https://github.com/spiffe/tornjak/blob/main/docs/keycloak-configuration.md) +* [Keycloak Configuration for Tornjak](https://github.com/spiffe/tornjak/blob/main/docs/keycloak-configuration.md) +* [Detailed Blogs on Tornjak User Management](https://github.com/spiffe/tornjak/blob/main/docs/blogs.md) + +**NOTE:** This example works only with the Vanilla version of Kubernetes; it does not yet support Openshift. + +As part of the exercise, an instance of Keycloak is deployed to illustrate how to manage users' access to Tornjak. +Once enabled, the Tornjak UI will redirect all authentication calls to the Keycloak instance to obtain the +correct credentials. Authorization is based on these credentials and occurs at the Tornjak application level. + +## Deploy Keycloak Instance (Authentication Service) + +We will deploy the instance of Keycloak in the same namespace as the SPIRE Server + +```shell +# Create a namespace to deploy Keycloak and SPIRE-server +kubectl create namespace spire-server +``` + +```shell +# Deploy Keycloak as an authentication service +helm upgrade --install -n spire-server keycloak --values examples/tornjak/keycloak/values.yaml oci://registry-1.docker.io/bitnamicharts/keycloak --render-subchart-notes +``` + +* It's important to start the service before configuring Tornjak with auth. + +```shell +# Start an auth Service [Keycloak] (Terminal 3) +kubectl -n spire-server port-forward service/keycloak 8080:80 +``` +## Deploy SPIRE with Tornjak User Management Enabled + +Please follow the instructions for deploying Tornjak as specified in Tornjak Example [here](../README.md) +with addition of the User Management values `--values examples/tornjak/values-auth.yaml`. + +For example: + +```shell +# Install SPIRE CRDs +helm upgrade --install --create-namespace -n spire-mgmt spire-crds charts/spire-crds +``` + +```shell +# Standard SPIRE and Tornjak deployment with Authentication enabled +helm upgrade --install \ +--set global.spire.namespaces.system.create=true \ +--values tests/integration/psat/values.yaml \ +--values examples/tornjak/values.yaml \ +--values examples/tornjak/values-auth.yaml \ +--render-subchart-notes spire charts/spire +``` + +To test the deployment, you can run the SPIRE test: + +```shell +# Test the Tornjak deployment +helm test spire +``` + +## Access Tornjak + +To access Tornjak use port-forwarding or check the ingress option below. + +Run following commands from your shell, if you run with different values your namespace might differ. Consult the install notes printed when running above `helm upgrade` command in that case. + +Since `port-forward` is a blocking command, execute them in three different consoles (one for backend, one for frontend and one for auth that you already started in the previous step): + +```shell +# Start a backend Service (Terminal 1) +kubectl -n spire-server port-forward service/spire-tornjak-backend 10000:10000 +``` + +```shell +# Start a frontend Service (Terminal 2) +kubectl -n spire-server port-forward service/spire-tornjak-frontend 3000:3000 +``` + +* You can now access Tornjak at [localhost:3000](http://localhost:3000). + +* This will redirect to the auth service for authentication to [localhost:8080](http://localhost:8080) + +See [values.yaml](./values.yaml) for more details on the chart configurations to customize authentication config. + +## Deploy SPIRE with Tornjak User Management Enabled using Ingress + +When deployment uses Ingress, the access to Tornjak application and Keycloak will be different from above. +Please follow the deployment and configuration instructions as described [here](../README.md) +and make sure to add the `--values examples/tornjak/values-auth.yaml` parameter that is referencing Tornjak Authentication values. + +And update your `examples/production/example-your-values.yaml` most importantly, `trustDomain`, accordingly. + +E.g: + +```shell +helm upgrade --install \ +--set global.spire.namespaces.create=true \ +--set global.spire.ingressControllerType=ingress-nginx \ +--values tests/integration/psat/values.yaml \ +--values examples/tornjak/values.yaml \ +--values examples/tornjak/values-auth.yaml \ +--values examples/tornjak/values-ingress.yaml \ +--render-subchart-notes spire charts/spire +``` diff --git a/examples/tornjak/keycloak/values.yaml b/examples/tornjak/keycloak/values.yaml new file mode 100644 index 000000000..ccbba06ec --- /dev/null +++ b/examples/tornjak/keycloak/values.yaml @@ -0,0 +1,128 @@ +auth: + ## @param keycloak.auth.realm Realm name in which to create users## @param auth.adminUser Keycloak administrator user + ## + adminUser: admin + ## @param auth.adminPassword Keycloak administrator password for the new user + ## + adminPassword: admin +proxy: edge # for https proxy reverse mode +keycloakConfigCli: + enabled: true + configuration: # tornjak realm configuration + tornjak.json: | + { + "realm": "tornjak", + "enabled": true, + "roles" : { + "realm" : [ { + "name" : "tornjak-viewer-realm-role" + }, { + "name" : "tornjak-admin-realm-role" + } ], + "client" : { + "tornjak" : [ { + "name" : "viewer", + "composite" : true, + "composites" : { + "realm" : [ "tornjak-viewer-realm-role" ] + }, + "clientRole" : true + }, { + "name" : "admin", + "composite" : true, + "composites" : { + "realm" : [ "tornjak-admin-realm-role" ] + }, + "clientRole" : true + } ] + } + }, + "groups" : [ { + "name" : "admin", + "path" : "/admin", + "realmRoles" : [ "tornjak-admin-realm-role" ] + }, { + "name" : "viewer", + "path" : "/viewer", + "realmRoles" : [ "tornjak-viewer-realm-role" ] + } ], + "users" : [ { + "username" : "admin", + "enabled" : true, + "firstName" : "Admin", + "lastName" : "User", + "credentials" : [ { + "type" : "password", + "userLabel" : "My password", + "secretData" : "{\"value\":\"Y1Kcmx/XxLWtnRLyMy/zn6wWbfu2fSKdaefrXM50cva3P+kA2BqBDvTZDswGP6JZ+IWrJaitm8RKV0L9LiwaFQ==\",\"salt\":\"Mh5g1EgTo26xhzoj67bovA==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "groups" : [ "/admin" ] + }, { + "username" : "viewer", + "enabled" : true, + "firstName" : "Viewer", + "lastName" : "User", + "credentials" : [ { + "type" : "password", + "userLabel" : "My password", + "secretData" : "{\"value\":\"1ow3LfLDvpBRLfRbr2LtFRqje8NsKouHMw95Wwpsg5NP2Pga4ZBL7+T62bCDV6dOvy3U9xEEU4CRkhSWFaeDLg==\",\"salt\":\"qML2gBVSG7xYRZcaffW68A==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "groups" : [ "/viewer" ] + } ], + "clients" : [ { + "clientId" : "tornjak", + "name" : "Tornjak", + "enabled" : true, + "alwaysDisplayInConsole" : true, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "http://localhost:3000/*" ], + "webOrigins" : [ "*" ], + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : true, + "protocol" : "openid-connect", + "attributes" : { + "post.logout.redirect.uris" : "http://localhost:3000/*" + }, + "fullScopeAllowed" : true, + "defaultClientScopes": [ + "role_list", + "profile", + "email", + "roles", + "web-origins", + "acr", + "tornjak-backend", + ] + }], + "clientScopes": [{ + "name": "tornjak-backend", + "description": "tornjak backend audience check", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false", + "gui.order": "", + "consent.screen.text": "" + }, + "protocolMappers": [{ + "name": "tornjak-backend", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "included.custom.audience": "tornjak-backend", + "userinfo.token.claim": "false", + "id.token.claim": "false", + "lightweight.claim": "false", + "access.token.claim": "true" + } + }] + }] + } \ No newline at end of file diff --git a/examples/tornjak/values-auth.yaml b/examples/tornjak/values-auth.yaml new file mode 100644 index 000000000..4f7e2d92d --- /dev/null +++ b/examples/tornjak/values-auth.yaml @@ -0,0 +1,12 @@ +spire-server: + tornjak: + config: + ## @extra tornjak.config.userManagement [object] UserManagement config + userManagement: + issuer: "http://keycloak:80/realms/tornjak" + audience: "tornjak-backend" + +tornjak-frontend: + auth: + enabled: true + serverURL: http://localhost:8080/ # enable auth by providing url diff --git a/examples/tornjak/values.yaml b/examples/tornjak/values.yaml index 6ba001503..688c2b0e2 100644 --- a/examples/tornjak/values.yaml +++ b/examples/tornjak/values.yaml @@ -4,6 +4,7 @@ spire-server: tornjak-frontend: enabled: true + apiServerURL: "http://localhost:10000" service: type: ClusterIP port: 3000 From c132cc481e1477cf570f7b883a95279d2e2cadac Mon Sep 17 00:00:00 2001 From: kfox1111 Date: Fri, 26 Apr 2024 12:19:01 -0700 Subject: [PATCH 09/37] Add support for externalServer=true (#303) --- charts/spire/charts/spire-server/README.md | 1 + .../charts/spire-server/templates/_helpers.tpl | 13 +++++++++++++ .../charts/spire-server/templates/configmap.yaml | 2 ++ .../templates/controller-manager-configmap.yaml | 2 ++ .../templates/controller-manager-roles.yaml | 10 ++-------- .../templates/controller-manager-service.yaml | 2 ++ .../templates/controller-manager-webhook.yaml | 2 ++ .../spire/charts/spire-server/templates/hpa.yaml | 2 ++ .../spire-server/templates/podmonitor.yaml | 2 ++ .../templates/post-install-hook.yaml | 2 ++ .../templates/post-upgrade-hook.yaml | 2 ++ .../spire-server/templates/pre-delete-hook.yaml | 2 ++ .../spire-server/templates/pre-upgrade-hook.yaml | 2 ++ .../charts/spire-server/templates/roles.yaml | 16 ++++------------ .../spire-server/templates/server-resource.yaml | 2 ++ .../charts/spire-server/templates/service.yaml | 2 ++ .../spire-server/templates/serviceaccount.yaml | 2 ++ .../templates/tests/test-connection.yaml | 2 ++ charts/spire/charts/spire-server/values.yaml | 3 +++ 19 files changed, 51 insertions(+), 20 deletions(-) diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index 9270736be..d138be53c 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -87,6 +87,7 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `image.pullPolicy` | The image pull policy | `IfNotPresent` | | `image.tag` | Overrides the image tag whose default is the chart appVersion | `""` | | `kind` | Define SPIRE server deployment type. Can be statefulset/deployment. Defaults to statefulset if not set. This feature is experimental. | `statefulset` | +| `externalServer` | Deploy only the bundle ConfigMap, RBAC rules, and identity documents but not the server. Use in a nested setup where the server is external. | `false` | | `imagePullSecrets` | Pull secrets for images | `[]` | | `nameOverride` | Name override | `""` | | `crNameOverride` | Name override for any custom resources | `""` | diff --git a/charts/spire/charts/spire-server/templates/_helpers.tpl b/charts/spire/charts/spire-server/templates/_helpers.tpl index 9f6e011be..acb469708 100644 --- a/charts/spire/charts/spire-server/templates/_helpers.tpl +++ b/charts/spire/charts/spire-server/templates/_helpers.tpl @@ -301,3 +301,16 @@ The code below determines what connection type should be used. {{- $g := dig "spire" "caSubject" "commonName" "" .Values.global }} {{- default .Values.ca_subject.common_name $g }} {{- end }} + +{{- define "spire-server.subject" }} +subjects: +{{- if .Values.externalServer }} +- apiGroup: rbac.authorization.k8s.io + kind: User + name: spire-root +{{- else }} +- kind: ServiceAccount + name: {{ include "spire-server.serviceAccountName" . }} + namespace: {{ include "spire-server.namespace" . }} +{{- end }} +{{- end }} diff --git a/charts/spire/charts/spire-server/templates/configmap.yaml b/charts/spire/charts/spire-server/templates/configmap.yaml index 2c6e34cc0..a4e07a5e1 100644 --- a/charts/spire/charts/spire-server/templates/configmap.yaml +++ b/charts/spire/charts/spire-server/templates/configmap.yaml @@ -361,6 +361,7 @@ telemetry: port: 9988 {{- end }} {{- end }} +{{- if not .Values.externalServer }} apiVersion: v1 kind: ConfigMap metadata: @@ -373,3 +374,4 @@ metadata: data: server.conf: | {{- include "spire-lib.reformat-and-yaml2json" (dict "config" (include "spire-server.yaml-config" .) "root" .) | nindent 4 }} +{{- end }} diff --git a/charts/spire/charts/spire-server/templates/controller-manager-configmap.yaml b/charts/spire/charts/spire-server/templates/controller-manager-configmap.yaml index f019f56d4..52e86caa5 100644 --- a/charts/spire/charts/spire-server/templates/controller-manager-configmap.yaml +++ b/charts/spire/charts/spire-server/templates/controller-manager-configmap.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.externalServer }} {{- if eq (.Values.controllerManager.enabled | toString) "true" }} apiVersion: v1 kind: ConfigMap @@ -51,3 +52,4 @@ data: {{- toYaml . | nindent 6 }} {{- end }} {{- end }} +{{- end }} diff --git a/charts/spire/charts/spire-server/templates/controller-manager-roles.yaml b/charts/spire/charts/spire-server/templates/controller-manager-roles.yaml index d2f9e9b85..8b18b3def 100644 --- a/charts/spire/charts/spire-server/templates/controller-manager-roles.yaml +++ b/charts/spire/charts/spire-server/templates/controller-manager-roles.yaml @@ -24,10 +24,7 @@ roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: {{ include "spire-controller-manager.fullname" . }}-leader-election -subjects: -- kind: ServiceAccount - name: {{ include "spire-server.serviceAccountName" . }} - namespace: {{ include "spire-server.namespace" . }} +{{ include "spire-server.subject" . }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -87,8 +84,5 @@ roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: {{ .Release.Namespace}}-{{ include "spire-controller-manager.fullname" . }} -subjects: -- kind: ServiceAccount - name: {{ include "spire-server.serviceAccountName" . }} - namespace: {{ include "spire-server.namespace" . }} +{{ include "spire-server.subject" . }} {{- end }} diff --git a/charts/spire/charts/spire-server/templates/controller-manager-service.yaml b/charts/spire/charts/spire-server/templates/controller-manager-service.yaml index 5d3b0a767..864c20472 100644 --- a/charts/spire/charts/spire-server/templates/controller-manager-service.yaml +++ b/charts/spire/charts/spire-server/templates/controller-manager-service.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.externalServer }} {{- if eq (.Values.controllerManager.enabled | toString) "true" }} apiVersion: v1 kind: Service @@ -20,3 +21,4 @@ spec: selector: {{- include "spire-server.selectorLabels" . | nindent 4 }} {{- end }} +{{- end }} diff --git a/charts/spire/charts/spire-server/templates/controller-manager-webhook.yaml b/charts/spire/charts/spire-server/templates/controller-manager-webhook.yaml index 7f91f1090..660c2f86c 100644 --- a/charts/spire/charts/spire-server/templates/controller-manager-webhook.yaml +++ b/charts/spire/charts/spire-server/templates/controller-manager-webhook.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.externalServer }} {{- if and (eq (.Values.controllerManager.enabled | toString) "true") .Values.controllerManager.validatingWebhookConfiguration.enabled }} apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration @@ -37,3 +38,4 @@ webhooks: resources: ["clusterspiffeids"] sideEffects: None {{- end }} +{{- end }} diff --git a/charts/spire/charts/spire-server/templates/hpa.yaml b/charts/spire/charts/spire-server/templates/hpa.yaml index 65b195e52..7fd457a31 100644 --- a/charts/spire/charts/spire-server/templates/hpa.yaml +++ b/charts/spire/charts/spire-server/templates/hpa.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.externalServer }} {{- if .Values.autoscaling.enabled }} apiVersion: {{ include "spire-lib.autoscalingVersion" . }} kind: HorizontalPodAutoscaler @@ -38,3 +39,4 @@ spec: averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} {{- end }} {{- end }} +{{- end }} diff --git a/charts/spire/charts/spire-server/templates/podmonitor.yaml b/charts/spire/charts/spire-server/templates/podmonitor.yaml index 878a4bafc..3e736df9a 100644 --- a/charts/spire/charts/spire-server/templates/podmonitor.yaml +++ b/charts/spire/charts/spire-server/templates/podmonitor.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.externalServer }} {{- if (dig "telemetry" "prometheus" "podMonitor" "enabled" .Values.telemetry.prometheus.podMonitor.enabled .Values.global) }} {{- $namespace := include "spire-server.podMonitor.namespace" . }} {{- $podNamespace := ( include "spire-server.namespace" . ) }} @@ -25,3 +26,4 @@ spec: kubernetes.io/metadata.name: {{ $podNamespace }} {{- end }} {{- end }} +{{- end }} diff --git a/charts/spire/charts/spire-server/templates/post-install-hook.yaml b/charts/spire/charts/spire-server/templates/post-install-hook.yaml index 6b4568edc..3b9bf1b03 100644 --- a/charts/spire/charts/spire-server/templates/post-install-hook.yaml +++ b/charts/spire/charts/spire-server/templates/post-install-hook.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.externalServer }} {{- if eq ((dig "installAndUpgradeHooks" "enabled" .Values.controllerManager.installAndUpgradeHook.enabled .Values.global) | toString) "true" }} {{- if and (eq (.Values.controllerManager.enabled | toString) "true") .Values.controllerManager.validatingWebhookConfiguration.enabled }} {{- if eq .Values.controllerManager.validatingWebhookConfiguration.failurePolicy "Fail" }} @@ -87,3 +88,4 @@ spec: {{- end }} {{- end }} {{- end }} +{{- end }} diff --git a/charts/spire/charts/spire-server/templates/post-upgrade-hook.yaml b/charts/spire/charts/spire-server/templates/post-upgrade-hook.yaml index 45f952550..683ba91db 100644 --- a/charts/spire/charts/spire-server/templates/post-upgrade-hook.yaml +++ b/charts/spire/charts/spire-server/templates/post-upgrade-hook.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.externalServer }} {{- if eq ((dig "installAndUpgradeHooks" "enabled" .Values.controllerManager.installAndUpgradeHook.enabled .Values.global) | toString) "true" }} {{- if and (eq (.Values.controllerManager.enabled | toString) "true") .Values.controllerManager.validatingWebhookConfiguration.enabled }} {{- if eq .Values.controllerManager.validatingWebhookConfiguration.failurePolicy "Fail" }} @@ -87,3 +88,4 @@ spec: {{- end }} {{- end }} {{- end }} +{{- end }} diff --git a/charts/spire/charts/spire-server/templates/pre-delete-hook.yaml b/charts/spire/charts/spire-server/templates/pre-delete-hook.yaml index 2a14be910..7c4c0e80b 100644 --- a/charts/spire/charts/spire-server/templates/pre-delete-hook.yaml +++ b/charts/spire/charts/spire-server/templates/pre-delete-hook.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.externalServer }} {{- if eq ((dig "deleteHooks" "enabled" .Values.controllerManager.deleteHook.enabled .Values.global) | toString) "true" }} {{- if .Values.upstreamAuthority.spire.enabled }} apiVersion: v1 @@ -90,3 +91,4 @@ spec: - {{ include "spire-server.namespace" . }} {{- end }} {{- end }} +{{- end }} diff --git a/charts/spire/charts/spire-server/templates/pre-upgrade-hook.yaml b/charts/spire/charts/spire-server/templates/pre-upgrade-hook.yaml index b3f852520..a3a86b6f4 100644 --- a/charts/spire/charts/spire-server/templates/pre-upgrade-hook.yaml +++ b/charts/spire/charts/spire-server/templates/pre-upgrade-hook.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.externalServer }} {{- if eq ((dig "installAndUpgradeHooks" "enabled" .Values.controllerManager.installAndUpgradeHook.enabled .Values.global) | toString) "true" }} {{- if and (eq (.Values.controllerManager.enabled | toString) "true") .Values.controllerManager.validatingWebhookConfiguration.enabled }} {{- if eq .Values.controllerManager.validatingWebhookConfiguration.failurePolicy "Fail" }} @@ -87,3 +88,4 @@ spec: {{- end }} {{- end }} {{- end }} +{{- end }} diff --git a/charts/spire/charts/spire-server/templates/roles.yaml b/charts/spire/charts/spire-server/templates/roles.yaml index c09bedecd..3949967a5 100644 --- a/charts/spire/charts/spire-server/templates/roles.yaml +++ b/charts/spire/charts/spire-server/templates/roles.yaml @@ -1,3 +1,4 @@ +{{- $subject := include "spire-server.subject" . }} {{- $namespace := include "spire-server.namespace" . }} {{- $bundleNamespace := include "spire-server.bundle-namespace" . }} # Role to be able to push certificate bundles to a configmap @@ -39,10 +40,7 @@ apiVersion: rbac.authorization.k8s.io/v1 metadata: name: {{ include "spire-server.fullname" . }}-cm namespace: {{ $namespace }} -subjects: - - kind: ServiceAccount - name: {{ include "spire-server.serviceAccountName" . }} - namespace: {{ $namespace }} +{{ $subject }} roleRef: kind: Role name: {{ include "spire-server.fullname" . }}-cm @@ -54,10 +52,7 @@ apiVersion: rbac.authorization.k8s.io/v1 metadata: name: {{ include "spire-lib.bundle-configmap" . }} namespace: {{ $bundleNamespace }} -subjects: - - kind: ServiceAccount - name: {{ include "spire-server.serviceAccountName" . }} - namespace: {{ $namespace }} +{{ $subject }} roleRef: kind: Role name: {{ include "spire-lib.bundle-configmap" . }} @@ -89,10 +84,7 @@ kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: {{ .Release.Namespace}}-{{ include "spire-server.fullname" . }} -subjects: - - kind: ServiceAccount - name: {{ include "spire-server.serviceAccountName" . }} - namespace: {{ $namespace }} +{{ $subject }} roleRef: kind: ClusterRole name: {{ .Release.Namespace}}-{{ include "spire-server.fullname" . }} diff --git a/charts/spire/charts/spire-server/templates/server-resource.yaml b/charts/spire/charts/spire-server/templates/server-resource.yaml index a661a3b53..a90dba2bd 100644 --- a/charts/spire/charts/spire-server/templates/server-resource.yaml +++ b/charts/spire/charts/spire-server/templates/server-resource.yaml @@ -21,6 +21,7 @@ {{- else }} {{- fail "Unsupported kind." }} {{- end }} +{{- if not .Values.externalServer }} apiVersion: apps/v1 {{- if eq .Values.kind "statefulset" }} kind: StatefulSet @@ -514,3 +515,4 @@ spec: storageClassName: {{ $storageClass }} {{- end }} {{- end }} +{{- end }} diff --git a/charts/spire/charts/spire-server/templates/service.yaml b/charts/spire/charts/spire-server/templates/service.yaml index 1e57a0707..b9726efe6 100644 --- a/charts/spire/charts/spire-server/templates/service.yaml +++ b/charts/spire/charts/spire-server/templates/service.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.externalServer }} apiVersion: v1 kind: Service metadata: @@ -26,3 +27,4 @@ spec: {{- end }} selector: {{- include "spire-server.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/charts/spire/charts/spire-server/templates/serviceaccount.yaml b/charts/spire/charts/spire-server/templates/serviceaccount.yaml index cd717b588..0b793d244 100644 --- a/charts/spire/charts/spire-server/templates/serviceaccount.yaml +++ b/charts/spire/charts/spire-server/templates/serviceaccount.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.externalServer }} {{- if .Values.serviceAccount.create -}} apiVersion: v1 kind: ServiceAccount @@ -11,3 +12,4 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} {{- end }} +{{- end }} diff --git a/charts/spire/charts/spire-server/templates/tests/test-connection.yaml b/charts/spire/charts/spire-server/templates/tests/test-connection.yaml index 3ff41c647..e4f33b0ef 100644 --- a/charts/spire/charts/spire-server/templates/tests/test-connection.yaml +++ b/charts/spire/charts/spire-server/templates/tests/test-connection.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.externalServer }} {{ $values := merge .Values }} apiVersion: v1 kind: Pod @@ -64,3 +65,4 @@ spec: secretName: {{ .Values.tests.tls.customCA }} {{- end }} restartPolicy: Never +{{- end }} diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index 4369b9cb5..ef9e65231 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -23,6 +23,9 @@ image: ## @param kind Define SPIRE server deployment type. Can be statefulset/deployment. Defaults to statefulset if not set. This feature is experimental. kind: statefulset +## @param externalServer Deploy only the bundle ConfigMap, RBAC rules, and identity documents but not the server. Use in a nested setup where the server is external. +externalServer: false + ## @param imagePullSecrets [array] Pull secrets for images imagePullSecrets: [] From 6de23d3303ade50609fc5239120f8819220a3bbb Mon Sep 17 00:00:00 2001 From: kfox1111 Date: Fri, 26 Apr 2024 14:21:16 -0700 Subject: [PATCH 10/37] Don't create role/binding when bundle disabled (#336) When the bundle notifier is disabled, there is no need to create a role and role binding for it. Signed-off-by: Kevin Fox --- charts/spire/charts/spire-server/templates/roles.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/charts/spire/charts/spire-server/templates/roles.yaml b/charts/spire/charts/spire-server/templates/roles.yaml index 3949967a5..fdf2ac517 100644 --- a/charts/spire/charts/spire-server/templates/roles.yaml +++ b/charts/spire/charts/spire-server/templates/roles.yaml @@ -1,6 +1,7 @@ {{- $subject := include "spire-server.subject" . }} {{- $namespace := include "spire-server.namespace" . }} {{- $bundleNamespace := include "spire-server.bundle-namespace" . }} +{{- if .Values.notifier.k8sbundle.enabled }} # Role to be able to push certificate bundles to a configmap kind: Role apiVersion: rbac.authorization.k8s.io/v1 @@ -14,6 +15,7 @@ rules: verbs: - get - patch +{{- end }} {{- if and .Values.upstreamAuthority.certManager.enabled .Values.upstreamAuthority.certManager.rbac.create }} --- # Role to be able to manage cert requests with Cert-Manager @@ -46,6 +48,7 @@ roleRef: name: {{ include "spire-server.fullname" . }}-cm apiGroup: rbac.authorization.k8s.io {{- end }} +{{- if .Values.notifier.k8sbundle.enabled }} --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 @@ -57,7 +60,7 @@ roleRef: kind: Role name: {{ include "spire-lib.bundle-configmap" . }} apiGroup: rbac.authorization.k8s.io - +{{- end }} {{- if and .Values.nodeAttestor.k8sPsat.enabled }} --- # ClusterRole to allow spire-server node attestor to query Token Review API From d92e8b04977289e868087778ac9f47d72c334565 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 05:15:11 -0700 Subject: [PATCH 11/37] Bump github.com/onsi/ginkgo/v2 from 2.17.1 to 2.17.2 in /tests (#337) Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.17.1 to 2.17.2. - [Release notes](https://github.com/onsi/ginkgo/releases) - [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/ginkgo/compare/v2.17.1...v2.17.2) --- updated-dependencies: - dependency-name: github.com/onsi/ginkgo/v2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/go.mod | 16 ++++++++-------- tests/go.sum | 38 ++++++++++++++++---------------------- 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/tests/go.mod b/tests/go.mod index fc0d7a9b4..cca96f4ad 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -5,7 +5,7 @@ go 1.21 toolchain go1.21.5 require ( - github.com/onsi/ginkgo/v2 v2.17.1 + github.com/onsi/ginkgo/v2 v2.17.2 github.com/onsi/gomega v1.33.0 helm.sh/helm/v3 v3.14.4 ) @@ -22,14 +22,14 @@ require ( github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.3 // indirect - github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect + github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect github.com/google/uuid v1.3.0 // indirect github.com/huandu/xstrings v1.4.0 // indirect github.com/imdario/mergo v0.3.13 // indirect @@ -47,14 +47,14 @@ require ( github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/net v0.23.0 // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/net v0.24.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/term v0.18.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.17.0 // indirect + golang.org/x/tools v0.20.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/tests/go.sum b/tests/go.sum index 6741ec22d..633b23bfc 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -7,9 +7,6 @@ github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0 github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= @@ -31,8 +28,8 @@ github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2Kv github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -50,15 +47,14 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= @@ -90,8 +86,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8= -github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= +github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g= +github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE= github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -114,7 +110,6 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= @@ -135,8 +130,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -148,8 +143,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -158,20 +153,19 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -186,8 +180,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= -golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= +golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 2d9866ada272dcc7b4c8be5a02750450ec743fa3 Mon Sep 17 00:00:00 2001 From: "spire-helm-version-checker[bot]" <161522935+spire-helm-version-checker[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 06:10:16 -0700 Subject: [PATCH 12/37] Bump test chart dependencies (#338) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: marcofranssen <694733+marcofranssen@users.noreply.github.com> Co-authored-by: kfox1111 --- .github/tests/charts.json | 6 +++--- .../spire/charts/spiffe-oidc-discovery-provider/README.md | 6 +++--- .../spire/charts/spiffe-oidc-discovery-provider/values.yaml | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/tests/charts.json b/.github/tests/charts.json index 1d53e08a0..82f843f57 100644 --- a/.github/tests/charts.json +++ b/.github/tests/charts.json @@ -2,17 +2,17 @@ { "name": "kube-prometheus-stack", "repo": "https://prometheus-community.github.io/helm-charts", - "version": "58.2.1" + "version": "58.2.2" }, { "name": "cert-manager", "repo": "https://charts.jetstack.io", - "version": "v1.14.4" + "version": "v1.14.5" }, { "name": "ingress-nginx", "repo": "https://kubernetes.github.io/ingress-nginx", - "version": "4.10.0" + "version": "4.10.1" }, { "name": "mysql", diff --git a/charts/spire/charts/spiffe-oidc-discovery-provider/README.md b/charts/spire/charts/spiffe-oidc-discovery-provider/README.md index c320c311d..da0fc2b39 100644 --- a/charts/spire/charts/spiffe-oidc-discovery-provider/README.md +++ b/charts/spire/charts/spiffe-oidc-discovery-provider/README.md @@ -69,7 +69,7 @@ A Helm chart to install the SPIFFE OIDC discovery provider. | `insecureScheme.nginx.image.registry` | The OCI registry to pull the image from. Only used when TLS is disabled. | `docker.io` | | `insecureScheme.nginx.image.repository` | The repository within the registry. Only used when TLS is disabled. | `nginxinc/nginx-unprivileged` | | `insecureScheme.nginx.image.pullPolicy` | The image pull policy. Only used when TLS is disabled. | `IfNotPresent` | -| `insecureScheme.nginx.image.tag` | Overrides the image tag whose default is the chart appVersion. Only used when TLS is disabled. | `1.25.5-alpine` | +| `insecureScheme.nginx.image.tag` | Overrides the image tag whose default is the chart appVersion. Only used when TLS is disabled. | `1.26.0-alpine` | | `insecureScheme.nginx.resources` | Resource requests and limits | `{}` | | `jwtIssuer` | Path to JWT issuer. Defaults to oidc-discovery.$trustDomain if unset | `""` | | `config.logLevel` | The log level, valid values are "debug", "info", "warn", and "error" | `info` | @@ -119,11 +119,11 @@ A Helm chart to install the SPIFFE OIDC discovery provider. | `tests.toolkit.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.toolkit.image.repository` | The repository within the registry | `chainguard/min-toolkit-debug` | | `tests.toolkit.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.toolkit.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:cbcc74f3a614ebb5455ce4a1c3cf311ffb2ce857833a827e3d0dc07f77760383` | +| `tests.toolkit.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:773ae9342541de6efc71425dbeeb863fc6cf9856c0dac89266ae3fd8d14a4528` | | `tests.step.image.registry` | The OCI registry to pull the image from | `docker.io` | | `tests.step.image.repository` | The repository within the registry | `smallstep/step-cli` | | `tests.step.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.step.image.tag` | Overrides the image tag whose default is the chart appVersion | `0.26.0` | +| `tests.step.image.tag` | Overrides the image tag whose default is the chart appVersion | `0.26.1` | | `tests.busybox.image.registry` | The OCI registry to pull the image from | `""` | | `tests.busybox.image.repository` | The repository within the registry | `busybox` | | `tests.busybox.image.pullPolicy` | The image pull policy | `IfNotPresent` | diff --git a/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml b/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml index 049d963e2..1f5483a2b 100644 --- a/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml +++ b/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml @@ -164,7 +164,7 @@ insecureScheme: registry: docker.io repository: nginxinc/nginx-unprivileged pullPolicy: IfNotPresent - tag: 1.25.5-alpine + tag: 1.26.0-alpine ## @param insecureScheme.nginx.resources Resource requests and limits resources: {} # We usually recommend not to specify default resources and to leave this as a conscious @@ -340,7 +340,7 @@ tests: registry: cgr.dev repository: chainguard/min-toolkit-debug pullPolicy: IfNotPresent - tag: latest@sha256:cbcc74f3a614ebb5455ce4a1c3cf311ffb2ce857833a827e3d0dc07f77760383 + tag: latest@sha256:773ae9342541de6efc71425dbeeb863fc6cf9856c0dac89266ae3fd8d14a4528 step: ## @param tests.step.image.registry The OCI registry to pull the image from @@ -352,7 +352,7 @@ tests: registry: "docker.io" repository: smallstep/step-cli pullPolicy: IfNotPresent - tag: 0.26.0 + tag: 0.26.1 busybox: ## @param tests.busybox.image.registry The OCI registry to pull the image from From f8fd46a28d30d341e1501ab01748b8a5f5d4cf89 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Apr 2024 08:10:29 -0700 Subject: [PATCH 13/37] Bump github.com/onsi/gomega from 1.33.0 to 1.33.1 in /tests (#340) Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.33.0 to 1.33.1. - [Release notes](https://github.com/onsi/gomega/releases) - [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/gomega/compare/v1.33.0...v1.33.1) --- updated-dependencies: - dependency-name: github.com/onsi/gomega dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/go.mod | 2 +- tests/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/go.mod b/tests/go.mod index cca96f4ad..4bb724eb9 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -6,7 +6,7 @@ toolchain go1.21.5 require ( github.com/onsi/ginkgo/v2 v2.17.2 - github.com/onsi/gomega v1.33.0 + github.com/onsi/gomega v1.33.1 helm.sh/helm/v3 v3.14.4 ) diff --git a/tests/go.sum b/tests/go.sum index 633b23bfc..7c5108932 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -88,8 +88,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g= github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= -github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE= -github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY= +github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= +github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= From ee124042c2e433e4f2e5e15a6d5c0471c2abf039 Mon Sep 17 00:00:00 2001 From: Drew Wells Date: Thu, 2 May 2024 15:24:51 -0500 Subject: [PATCH 14/37] set refresh hint to 1/3 of default CA TTL value fixes #335 (#343) Signed-off-by: Drew Wells --- charts/spire/charts/spire-server/README.md | 1 + charts/spire/charts/spire-server/values.yaml | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index d138be53c..7d94afd93 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -147,6 +147,7 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `federation.enabled` | Flag to enable federation | `false` | | `federation.bundleEndpoint.port` | Port value for trust bundle federation | `8443` | | `federation.bundleEndpoint.address` | Address for trust bundle federation | `0.0.0.0` | +| `federation.bundleEndpoint.refresh_hint` | Hint used by federated servers on how often to refresh the bundle. CA TTL must be 3-5x the duration of this value to ensure public keys are loaded on federated servers prior to private key rotation on remote server. | `5m` | | `federation.tls.spire.enabled` | Use spire to secure the federation bundle endpoint | `true` | | `federation.tls.externalSecret.enabled` | Provide your own certificate/key via tls style Kubernetes Secret | `false` | | `federation.tls.externalSecret.secretName` | Specify which Secret to use | `""` | diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index ef9e65231..9149176cd 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -210,6 +210,8 @@ federation: port: 8443 ## @param federation.bundleEndpoint.address Address for trust bundle federation address: "0.0.0.0" + ## @param federation.bundleEndpoint.refresh_hint Hint used by federated servers on how often to refresh the bundle. CA TTL must be 3-5x the duration of this value to ensure public keys are loaded on federated servers prior to private key rotation on remote server. + refresh_hint: "5m" tls: spire: @@ -877,7 +879,7 @@ tornjak: issuer: "" ## @param tornjak.config.userManagement.audience UserManagement audience check audience: "" - + # Tornjak supports 3 connection types: `http`, `tls`, and `mtls`. # The connections are determined based on provided configuration # When `tlsSecret` is created in this chart namespace, the TLS connection is started From 8fef1bd050bc99a9bb50f0fb0de3c6b86429093a Mon Sep 17 00:00:00 2001 From: kfox1111 Date: Thu, 2 May 2024 14:08:31 -0700 Subject: [PATCH 15/37] Add external spire-controller-managers (#284) * Complete Server K8S PSAT support Add all the SPIRE Server supported options for the K8S PSAT attestor. This retains the ease of use for configuring local cluster support while adding the ability to configure multiple/external clusters as well. Kubeconfig support is added in its own config block as it will be used/shared with spire-controller-manager support in the future. Signed-off-by: Kevin Fox * Fix merge conflict Signed-off-by: Kevin Fox * Add support for integration tests in the tests/integration dir Signed-off-by: Kevin Fox * Fix split issue and typo Signed-off-by: Kevin Fox * Add basic psat test Signed-off-by: Kevin Fox * Fix linter Signed-off-by: Kevin Fox * Fix up test Signed-off-by: Kevin Fox * Add missing file Signed-off-by: Kevin Fox * Better encode config Signed-off-by: Kevin Fox * Update charts/spire/charts/spire-server/values.yaml Co-authored-by: Faisal Memon Signed-off-by: kfox1111 * Update docs Signed-off-by: Kevin Fox * Apply suggestions from code review Co-authored-by: Faisal Memon Signed-off-by: kfox1111 * Fix docs Signed-off-by: Kevin Fox * Update charts/spire/charts/spire-server/values.yaml Co-authored-by: Faisal Memon Signed-off-by: kfox1111 * Fix docs Signed-off-by: Kevin Fox * Add external k8s bundler Adds support to sync the CA bundle to configmaps in external Kubernetes clusters Signed-off-by: Kevin Fox * Update default Signed-off-by: Kevin Fox * Fix config file layout. Incorperate feedback. Signed-off-by: Kevin Fox * Incorperate feedback Signed-off-by: Kevin Fox * Update based on parent pr feedback Signed-off-by: Kevin Fox * Reformat config file Signed-off-by: Kevin Fox * Fix some things Signed-off-by: Kevin Fox * Reconfigure kind Signed-off-by: Kevin Fox * More debugging Signed-off-by: Kevin Fox * Fix up kind Signed-off-by: Kevin Fox * Incorperate feedback Signed-off-by: Kevin Fox * Add external spire-controller-managers Only one external controller manager is supported at a time until https://github.com/spiffe/spire/issues/4898 is resolved. Signed-off-by: Kevin Fox * Fix tests Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Upgrade to spire-controller-manager 0.5.0 Signed-off-by: Kevin Fox * Update for released 0.5.0 Signed-off-by: Kevin Fox * Merge in some of the id prefix pr Signed-off-by: Kevin Fox * Entry ID Prefix (#287) * Add Entry ID Prefix support Signed-off-by: Kevin Fox * Mulitcluster test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Implement cleanup setting too Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix docs Signed-off-by: Kevin Fox * Bump up test container Signed-off-by: Kevin Fox * Swith to testing with nightly Signed-off-by: Kevin Fox * Fix value name Signed-off-by: Kevin Fox * Fix docs Signed-off-by: Kevin Fox --------- Signed-off-by: Kevin Fox Signed-off-by: kfox1111 * Fix up doc formatting Signed-off-by: Kevin Fox * Fix merge conflict Signed-off-by: Kevin Fox * Update charts/spire/charts/spire-server/values.yaml Co-authored-by: Faisal Memon Signed-off-by: kfox1111 --------- Signed-off-by: Kevin Fox Signed-off-by: kfox1111 Co-authored-by: Faisal Memon --- .github/tests/common.sh | 2 +- .github/workflows/helm-chart-ci.yaml | 3 +- charts/spire/README.md | 13 +- charts/spire/charts/spire-server/README.md | 16 +++ .../_controller-manager-container.tpl | 125 ++++++++++++++++++ .../spire-server/templates/_helpers.tpl | 8 +- .../controller-manager-configmap.yaml | 118 +++++++++++------ .../templates/server-resource.yaml | 54 +------- charts/spire/charts/spire-server/values.yaml | 47 +++++++ charts/spire/values.yaml | 3 + ...ind-config.yaml => child-kind-config.yaml} | 2 +- tests/integration/psat/child-values.yaml | 20 +++ tests/integration/psat/other-kind-config.yaml | 7 + tests/integration/psat/run-tests.sh | 65 ++++++--- .../psat/sodp-clusterspiffeid.yaml | 23 ++++ tests/integration/psat/values.yaml | 26 +++- 16 files changed, 400 insertions(+), 132 deletions(-) create mode 100644 charts/spire/charts/spire-server/templates/_controller-manager-container.tpl rename tests/integration/psat/{kind-config.yaml => child-kind-config.yaml} (81%) create mode 100644 tests/integration/psat/child-values.yaml create mode 100644 tests/integration/psat/other-kind-config.yaml create mode 100644 tests/integration/psat/sodp-clusterspiffeid.yaml diff --git a/.github/tests/common.sh b/.github/tests/common.sh index 7d5323b2e..0566665d9 100755 --- a/.github/tests/common.sh +++ b/.github/tests/common.sh @@ -21,7 +21,7 @@ $(kubectl --request-timeout=30s describe pods --namespace "$1") #### Logs \`\`\`shell -$(kubectl get pods -o name -n "$1" | while read -r line; do echo logs for "${line}"; kubectl logs -n "$1" "${line}" --all-containers=true --ignore-errors=true; done) +$(kubectl get pods -o name -n "$1" | while read -r line; do echo logs for "${line}"; kubectl logs -n "$1" "${line}" --prefix --all-containers=true --ignore-errors=true; done) $( ([[ -n "$2" ]] && kubectl get pods -o name -n "$2") | while read -r line; do echo logs for "${line}"; kubectl logs -n "$2" "${line}" --all-containers=true --ignore-errors=true; done) \`\`\` diff --git a/.github/workflows/helm-chart-ci.yaml b/.github/workflows/helm-chart-ci.yaml index 08758b471..c664f9ed5 100644 --- a/.github/workflows/helm-chart-ci.yaml +++ b/.github/workflows/helm-chart-ci.yaml @@ -300,8 +300,7 @@ jobs: - name: Install and test integration run: | - kubectl create namespace spire-server - helm install -n spire-server spire-crds charts/spire-crds + helm install --create-namespace -n spire-mgmt spire-crds charts/spire-crds ${{ matrix.integrationtest }}/run-tests.sh upgrade-test: diff --git a/charts/spire/README.md b/charts/spire/README.md index d3267de4a..d9bc66ad3 100644 --- a/charts/spire/README.md +++ b/charts/spire/README.md @@ -255,12 +255,13 @@ Now you can interact with the Spire agent socket from your own application. The ### Spire server parameters -| Name | Description | Value | -| ---------------------------------------- | ------------------------------------------------------------------------- | ------------- | -| `spire-server.enabled` | Flag to enable Spire server | `true` | -| `spire-server.nameOverride` | Overrides the name of Spire server pods | `server` | -| `spire-server.kind` | Run spire server as deployment/statefulset. This feature is experimental. | `statefulset` | -| `spire-server.controllerManager.enabled` | Enable controller manager and provision CRD's | `true` | +| Name | Description | Value | +| ------------------------------------------------- | ------------------------------------------------------------------------- | ------------- | +| `spire-server.enabled` | Flag to enable Spire server | `true` | +| `spire-server.nameOverride` | Overrides the name of Spire server pods | `server` | +| `spire-server.kind` | Run spire server as deployment/statefulset. This feature is experimental. | `statefulset` | +| `spire-server.controllerManager.enabled` | Enable controller manager and provision CRD's | `true` | +| `spire-server.externalControllerManagers.enabled` | Enable external controller manager support | `true` | ### Spire agent parameters diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index 7d94afd93..c0c9b4904 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -286,6 +286,22 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `controllerManager.validatingWebhookConfiguration.enabled` | Disable only when you have another chart instance on the k8s cluster with webhooks enabled. | `true` | | `controllerManager.validatingWebhookConfiguration.failurePolicy` | Action when identity is not issued | `Fail` | | `controllerManager.cacheNamespaces` | If specified restricts the manager's cache to watch objects in the desired namespaces. Defaults to all namespaces. | `{}` | +| `externalControllerManagers.enabled` | Flag to enable external controller managers | `false` | +| `externalControllerManagers.defaults.reconcile.clusterSPIFFEIDs` | Enable reconciliation of clusterSPIFFEIDs from K8s to the SPIRE server | `true` | +| `externalControllerManagers.defaults.reconcile.clusterStaticEntries` | Enable reconciliation of clusterStaticEntries from K8s to the SPIRE server | `false` | +| `externalControllerManagers.defaults.reconcile.clusterFederatedTrustDomains` | Enable reconciliation of clusterFederatedTrustDomains from K8s to the SPIRE server | `false` | +| `externalControllerManagers.defaults.className` | specify to use an explicit class name. If empty, it will be automatically set to Release.Namespace-Release.Name to not conflict with other installs, enabling parallel installs. | `""` | +| `externalControllerManagers.defaults.watchClassless` | specify to process custom resources without class name specified. Useful to slowly migrate to class names from classless installs. Do not have two installs on the same k8s cluster both set to true. | `false` | +| `externalControllerManagers.defaults.entryIDPrefixCleanup` | consult the spiffe.io docs about this option before changing. Its unlikely you will need to ever change it. | `false` | +| `externalControllerManagers.defaults.parentIDTemplate` | The template that is used to register workloads. | `spiffe://{{ .TrustDomain }}/spire/agent/k8s_psat/{{ .ClusterName }}/{{ .NodeMeta.UID }}` | +| `externalControllerManagers.defaults.expandEnv` | Set to true to enable environment variable substitution of config file options | `false` | +| `externalControllerManagers.defaults.extraEnv` | Extra environment variables to add to the controller manager | `[]` | +| `externalControllerManagers.defaults.resources` | Resource requests and limits for controller manager | `{}` | +| `externalControllerManagers.defaults.securityContext` | Security context | `{}` | +| `externalControllerManagers.defaults.configMap.annotations` | Annotations to add to the Controller Manager ConfigMap | `{}` | +| `externalControllerManagers.defaults.ignoreNamespaces` | These namespaces are ignored by controller manager | `[]` | +| `externalControllerManagers.defaults.cacheNamespaces` | If specified restricts the manager's cache to watch objects in the desired namespaces. Defaults to all namespaces. | `{}` | +| `externalControllerManagers.clusters` | A dictionary of clusters to add with optional overrides. If empty, all clusters defined in kubeConfigs will be used. | `{}` | | `tools.kubectl.image.registry` | The OCI registry to pull the image from | `docker.io` | | `tools.kubectl.image.repository` | The repository within the registry | `rancher/kubectl` | | `tools.kubectl.image.pullPolicy` | The image pull policy | `IfNotPresent` | diff --git a/charts/spire/charts/spire-server/templates/_controller-manager-container.tpl b/charts/spire/charts/spire-server/templates/_controller-manager-container.tpl new file mode 100644 index 000000000..4c0b4e9c3 --- /dev/null +++ b/charts/spire/charts/spire-server/templates/_controller-manager-container.tpl @@ -0,0 +1,125 @@ +{{- define "spire-controller-manager.containers" }} +{{- $root := . }} +{{- $settings := dict }} +{{- $defaults := .Values.controllerManager }} +{{- $webhooksEnabled := .Values.controllerManager.validatingWebhookConfiguration.enabled }} +{{- $startPort := 8082 }} +{{- $reconcileFederation := 0 }} +{{- $reconcileEntries := 0 }} +{{- if eq (.Values.controllerManager.enabled | toString) "true" }} +{{- if .Values.controllerManager.reconcile.clusterFederatedTrustDomains }} +{{- $reconcileFederation = add $reconcileFederation 1 }} +{{- end }} +{{- if or .Values.controllerManager.reconcile.clusterSPIFFEIDs .Values.controllerManager.reconcile.clusterStaticEntries }} +{{- $reconcileEntries = add $reconcileEntries 1 }} +{{- end }} +{{- include "spire-controller-manager.container" (dict "Values" .Values "Chart" .Chart "startPort" $startPort "suffix" "" "settings" $settings "defaults" $defaults "webhooksEnabled" $webhooksEnabled) }} +{{- end }} +{{- if .Values.externalControllerManagers.enabled }} +{{- $clusters := default .Values.kubeConfigs .Values.externalControllerManagers.clusters }} +{{- $clusterDefaults := .Values.externalControllerManagers.defaults }} +{{- range $name, $_ := $clusters }} +{{- $clusterSettings := dict }} +{{- if hasKey $root.Values.externalControllerManagers.clusters $name }} +{{- $clusterSettings = index $root.Values.externalControllerManagers.clusters $name }} +{{- end }} +{{- $suffix := printf "-%s" $name }} +{{- $startPort = add $startPort 2 }} +{{- $kubeConfig := $name }} +{{- if hasKey $clusterSettings "kubeConfigName" }} +{{- $kubeConfig = $clusterSettings.kubeConfigName }} +{{- end }} +{{- $reconcile := dict }} +{{- if hasKey $clusterSettings "reconcile" }} +{{- $reconcile = $clusterSettings.reconcile }} +{{- end }} +{{- if and (hasKey $reconcile "clusterFederatedTrustDomains") $reconcile.clusterFederatedTrustDomains }} +{{- $reconcileFederation = add $reconcileFederation 1 }} +{{- else if $clusterDefaults.reconcile.clusterFederatedTrustDomains }} +{{- $reconcileFederation = add $reconcileFederation 1 }} +{{- end }} +{{- if gt $reconcileFederation 1 }} +{{- fail "You can only have one controller-manager with reconcile.clusterFederatedTrustDomains set to true" }} +{{- end }} +{{- include "spire-controller-manager.container" (dict "Values" $root.Values "Chart" $root.Chart "startPort" $startPort "suffix" $suffix "settings" $clusterSettings "defaults" $clusterDefaults "webhooksEnabled" false "kubeConfig" $kubeConfig ) }} +{{- end }} +{{- end }} +{{- end }} +{{- define "spire-controller-manager.container" }} +{{- $promPort := .startPort }} +{{- $healthPort := add .startPort 1 }} +{{- $extraEnv := .defaults.extraEnv }} +{{- if hasKey .settings "extraEnv" }} +{{- $extraEnv = .settings.extraEnv }} +{{- end }} +{{- $expandEnv := .defaults.expandEnv }} +{{- if hasKey .settings "expandEnv" }} +{{- $extraEnv = .settings.expandEnv }} +{{- end }} +{{- $securityContext := .defaults.securityContext }} +{{- if hasKey .settings "securityContext" }} +{{- $securityContext = mergeOverwrite .defaults.securityContext .settings.securityContext }} +{{- end }} +- name: spire-controller-manager{{ .suffix }} + securityContext: + {{- include "spire-lib.securitycontext-extended" (dict "root" . "securityContext" $securityContext) | nindent 4 }} + image: {{ template "spire-lib.image" (dict "appVersion" .Chart.AppVersion "image" .Values.controllerManager.image "global" .Values.global) }} + imagePullPolicy: {{ .Values.controllerManager.image.pullPolicy }} + args: + {{- if hasKey . "kubeConfig" }} + - --kubeconfig=/kubeconfigs/{{ .kubeConfig }} + {{- end }} + - --config=controller-manager-config{{ .suffix }}.yaml + {{- if $expandEnv }} + - --expand-env + {{- end }} + env: + - name: ENABLE_WEBHOOKS + value: {{ .webhooksEnabled | toString | quote }} + {{- if gt (len $extraEnv) 0 }} + {{- $extraEnv | toYaml | nindent 4 }} + {{- end }} + ports: + {{- if .webhooksEnabled }} + - name: https + containerPort: 9443 + protocol: TCP + {{- end }} + - containerPort: {{ $healthPort }} + name: healthz + {{- if or (dig "telemetry" "prometheus" "enabled" .Values.telemetry.prometheus.enabled .Values.global) (and (dig "spire" "recommendations" "enabled" false .Values.global) (dig "spire" "recommendations" "prometheus" true .Values.global)) }} + - containerPort: {{ $promPort }} + name: prom-cm{{ .suffix }} + {{- end }} + livenessProbe: + httpGet: + path: /healthz + port: healthz + readinessProbe: + httpGet: + path: /readyz + port: healthz + resources: + {{- toYaml .Values.controllerManager.resources | nindent 4 }} + volumeMounts: + - name: spire-server-socket + mountPath: /tmp/spire-server/private + readOnly: true + - name: controller-manager-config + mountPath: /controller-manager-config{{ .suffix }}.yaml + subPath: controller-manager-config{{ .suffix }}.yaml + readOnly: true + {{- with .kubeConfig }} + - name: kubeconfigs + mountPath: /kubeconfigs/{{ . }} + subPath: {{ . }} + readOnly: true + {{- end }} + - name: spire-controller-manager-tmp + mountPath: /tmp + subPath: {{ printf "spire-controller-manager%s" .suffix }} + readOnly: false + {{- if gt (len .Values.extraVolumeMounts) 0 }} + {{- toYaml .Values.extraVolumeMounts | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/spire/charts/spire-server/templates/_helpers.tpl b/charts/spire/charts/spire-server/templates/_helpers.tpl index acb469708..0b3fb0fc6 100644 --- a/charts/spire/charts/spire-server/templates/_helpers.tpl +++ b/charts/spire/charts/spire-server/templates/_helpers.tpl @@ -280,8 +280,12 @@ The code below determines what connection type should be used. {{- end -}} {{- define "spire-server.controller-manager-class-name" -}} -{{- if .Values.controllerManager.className }} -{{- .Values.controllerManager.className }} +{{- if and (hasKey . "settings") (hasKey .settings "className") }} +{{- .settings.className }} +{{- else if and (hasKey . "defaults") .defaults.className }} +{{- .defaults.className }} +{{- else if .Values.controllerManager.className }} +{{- .Values.controllerManager.className }} {{- else }} {{- .Release.Namespace }}-{{ default .Release.Name .Values.crNameOverride }} {{- end -}} diff --git a/charts/spire/charts/spire-server/templates/controller-manager-configmap.yaml b/charts/spire/charts/spire-server/templates/controller-manager-configmap.yaml index 52e86caa5..bcfa7ffbf 100644 --- a/charts/spire/charts/spire-server/templates/controller-manager-configmap.yaml +++ b/charts/spire/charts/spire-server/templates/controller-manager-configmap.yaml @@ -1,5 +1,9 @@ +{{- $root := . }} +{{- $startPort := 8082 }} +{{- $clusters := default .Values.kubeConfigs .Values.externalControllerManagers.clusters }} +{{- $clusterDefaults := .Values.externalControllerManagers.defaults }} {{- if not .Values.externalServer }} -{{- if eq (.Values.controllerManager.enabled | toString) "true" }} +{{- if or (eq (.Values.controllerManager.enabled | toString) "true") (and .Values.externalControllerManagers.enabled (gt (len $clusters) 0)) }} apiVersion: v1 kind: ConfigMap metadata: @@ -10,46 +14,78 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} data: +{{- if eq (.Values.controllerManager.enabled | toString) "true" }} +{{- $clusterName := include "spire-lib.cluster-name" . }} controller-manager-config.yaml: | - apiVersion: spire.spiffe.io/v1alpha1 - kind: ControllerManagerConfig - metadata: - name: {{ include "spire-controller-manager.fullname" . }} - namespace: {{ include "spire-server.namespace" . }} - labels: - {{- include "spire-server.labels" . | nindent 8 }} - metrics: - bindAddress: 0.0.0.0:8082 - health: - healthProbeBindAddress: 0.0.0.0:8083 - leaderElection: - leaderElect: true - resourceName: {{ printf "%s-%s" .Release.Namespace (default .Release.Name .Values.crNameOverride) | sha256sum | trunc 8 }}.spiffe.io - resourceNamespace: {{ include "spire-server.namespace" . }} - {{- with .Values.controllerManager.cacheNamespaces }} - cacheNamespaces: - {{- toYaml . | nindent 6 }} - {{- end }} - {{- if .Values.controllerManager.validatingWebhookConfiguration.enabled }} - validatingWebhookConfigurationName: {{ .Release.Namespace }}-{{ include "spire-controller-manager.fullname" . }}-webhook - {{- end }} - {{- if typeIs "string" .Values.controllerManager.entryIDPrefixCleanup }} - entryIDPrefixCleanup: {{ .Values.controllerManager.entryIDPrefixCleanup | quote }} - {{- end }} - entryIDPrefix: {{ include "spire-lib.cluster-name" . }} - clusterName: {{ include "spire-lib.cluster-name" . }} - trustDomain: {{ include "spire-lib.trust-domain" . }} - ignoreNamespaces: - {{- with .Values.controllerManager.ignoreNamespaces }} - {{- toYaml . | nindent 6 }} - {{- end }} - spireServerSocketPath: "/tmp/spire-server/private/api.sock" - className: {{ include "spire-server.controller-manager-class-name" . | quote}} - watchClassless: {{ .Values.controllerManager.watchClassless | toYaml }} - parentIDTemplate: {{ .Values.controllerManager.parentIDTemplate | quote }} - {{- with .Values.reconcile }} - reconcile: - {{- toYaml . | nindent 6 }} - {{- end }} + {{- include "spire-controller-manager.config" (dict "Values" .Values "Chart" .Chart "Release" .Release "startPort" $startPort "suffix" "" "settings" (dict) "defaults" .Values.controllerManager "webhookEnabled" .Values.controllerManager.validatingWebhookConfiguration.enabled "clusterName" $clusterName) | nindent 4 }} +{{- end }} +{{- if .Values.externalControllerManagers.enabled }} +{{- range $name, $_ := $clusters }} +{{- $clusterSettings := dict }} +{{- if hasKey $root.Values.externalControllerManagers.clusters $name }} +{{- $clusterSettings = index $root.Values.externalControllerManagers.clusters $name }} +{{- end }} +{{- $suffix := printf "-%s" $name }} +{{- $startPort = add $startPort 2 }} + controller-manager-config{{ $suffix }}.yaml: | + {{- include "spire-controller-manager.config" (dict "Values" $root.Values "Chart" $root.Chart "Release" $root.Release "startPort" $startPort "suffix" $suffix "settings" $clusterSettings "defaults" $clusterDefaults "webhookEnabled" false "clusterName" $name) | nindent 4 }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- define "spire-controller-manager.config" }} +{{- $promPort := .startPort }} +{{- $healthPort := add $promPort 1 }} +apiVersion: spire.spiffe.io/v1alpha1 +kind: ControllerManagerConfig +metadata: + name: {{ include "spire-controller-manager.fullname" . }}{{ .suffix }} + namespace: {{ include "spire-server.namespace" . }} + labels: + {{- include "spire-server.labels" . | nindent 4 }} +metrics: + bindAddress: 0.0.0.0:{{ $promPort }} +health: + healthProbeBindAddress: 0.0.0.0:{{ $healthPort }} +leaderElection: + leaderElect: true + resourceName: {{ printf "%s-%s%s" .Release.Namespace (default .Release.Name .Values.crNameOverride) .suffix | sha256sum | trunc 8 }}.spiffe.io + resourceNamespace: {{ include "spire-server.namespace" . }} +{{- with .settings.cacheNamespaces }} +cacheNamespaces: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- if .webhookEnabled }} +validatingWebhookConfigurationName: {{ .Release.Namespace }}-{{ include "spire-controller-manager.fullname" . }}-webhook +{{- end }} +{{- $entryIDPrefixCleanup := .defaults.entryIDPrefixCleanup }} +{{- if hasKey .settings "entryIDPrefixCleanup" }} +{{- $entryIDPrefixCleanup = .settings.entryIDPrefixCleanup }} +{{- end }} +{{- if typeIs "string" $entryIDPrefixCleanup }} +entryIDPrefixCleanup: {{ $entryIDPrefixCleanup | quote }} +{{- end }} +entryIDPrefix: {{ .clusterName }} +clusterName: {{ .clusterName }} +trustDomain: {{ include "spire-lib.trust-domain" . }} +{{- $ignoreNamespaces := .defaults.ignoreNamespaces }} +{{- if hasKey .settings "ignoreNamespaces" }} +{{- $ignoreNamespaces = .settings.ingoreNamespaces }} +{{- end }} +{{- with $ignoreNamespaces }} +ignoreNamespaces: + {{- toYaml . | nindent 2 }} +{{- end }} +spireServerSocketPath: "/tmp/spire-server/private/api.sock" +className: {{ include "spire-server.controller-manager-class-name" . | quote}} +watchClassless: {{ if hasKey .settings "watchClassless" }}{{ .settings.watchClassless | toYaml }}{{ else }}{{ .defaults.watchClassless | toYaml }}{{ end }} +parentIDTemplate: {{ if hasKey .settings "parentIDTemplate" }}{{ .settings.parentIDTemplate | quote }}{{ else }}{{ .defaults.parentIDTemplate | quote }}{{ end }} +{{- $reconcile := dict }} +{{- if hasKey .settings "reconcile" }} +{{- $reconcile = .settings.reconcile }} {{- end }} +reconcile: + clusterSPIFFEIDs: {{ if hasKey $reconcile "clusterSPIFFEIDs" }}{{ toYaml $reconcile.clusterSPIFFEIDs }}{{ else }}{{ toYaml .defaults.reconcile.clusterSPIFFEIDs }}{{ end }} + clusterStaticEntries: {{ if hasKey $reconcile "clusterStaticEntries" }}{{ toYaml $reconcile.clusterStaticEntries }}{{ else }}{{ toYaml .defaults.reconcile.clusterStaticEntries }}{{ end }} + clusterFederatedTrustDomains: {{ if hasKey $reconcile "clusterFederatedTrustDomains" }}{{ toYaml $reconcile.clusterFederatedTrustDomains }}{{ else }}{{ toYaml .defaults.reconcile.clusterFederatedTrustDomains }}{{ end }} {{- end }} diff --git a/charts/spire/charts/spire-server/templates/server-resource.yaml b/charts/spire/charts/spire-server/templates/server-resource.yaml index a90dba2bd..d30910995 100644 --- a/charts/spire/charts/spire-server/templates/server-resource.yaml +++ b/charts/spire/charts/spire-server/templates/server-resource.yaml @@ -257,59 +257,7 @@ spec: - name: server-tmp mountPath: /tmp readOnly: false - {{- if eq (.Values.controllerManager.enabled | toString) "true" }} - - name: spire-controller-manager - securityContext: - {{- include "spire-lib.securitycontext-extended" (dict "root" . "securityContext" .Values.controllerManager.securityContext) | nindent 12 }} - image: {{ template "spire-lib.image" (dict "appVersion" $.Chart.AppVersion "image" .Values.controllerManager.image "global" .Values.global) }} - imagePullPolicy: {{ .Values.controllerManager.image.pullPolicy }} - args: - - --config=controller-manager-config.yaml - {{- if .Values.controllerManager.expandEnv }} - - --expand-env - {{- end }} - env: - - name: ENABLE_WEBHOOKS - value: {{ .Values.controllerManager.validatingWebhookConfiguration.enabled | toString | quote }} - {{- if gt (len .Values.controllerManager.extraEnv) 0 }} - {{- .Values.controllerManager.extraEnv | toYaml | nindent 12 }} - {{- end }} - ports: - - name: https - containerPort: 9443 - protocol: TCP - - containerPort: 8083 - name: healthz - {{- if or (dig "telemetry" "prometheus" "enabled" .Values.telemetry.prometheus.enabled .Values.global) (and (dig "spire" "recommendations" "enabled" false .Values.global) (dig "spire" "recommendations" "prometheus" true .Values.global)) }} - - containerPort: 8082 - name: prom2 - {{- end }} - livenessProbe: - httpGet: - path: /healthz - port: healthz - readinessProbe: - httpGet: - path: /readyz - port: healthz - resources: - {{- toYaml .Values.controllerManager.resources | nindent 12 }} - volumeMounts: - - name: spire-server-socket - mountPath: /tmp/spire-server/private - readOnly: true - - name: controller-manager-config - mountPath: /controller-manager-config.yaml - subPath: controller-manager-config.yaml - readOnly: true - - name: spire-controller-manager-tmp - mountPath: /tmp - readOnly: false - {{- if gt (len .Values.extraVolumeMounts) 0 }} - {{- toYaml .Values.extraVolumeMounts | nindent 12 }} - {{- end }} - {{- end }} - + {{- include "spire-controller-manager.containers" . | nindent 8 }} {{- if eq (.Values.tornjak.enabled | toString) "true" }} - name: tornjak securityContext: diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index 9149176cd..570454cbd 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -658,6 +658,53 @@ controllerManager: # fieldSelectors: # fName: f1 +externalControllerManagers: + ## @param externalControllerManagers.enabled Flag to enable external controller managers + enabled: false + defaults: + ## @param externalControllerManagers.defaults.reconcile.clusterSPIFFEIDs Enable reconciliation of clusterSPIFFEIDs from K8s to the SPIRE server + ## @param externalControllerManagers.defaults.reconcile.clusterStaticEntries Enable reconciliation of clusterStaticEntries from K8s to the SPIRE server + ## @param externalControllerManagers.defaults.reconcile.clusterFederatedTrustDomains Enable reconciliation of clusterFederatedTrustDomains from K8s to the SPIRE server + reconcile: + clusterSPIFFEIDs: true + clusterStaticEntries: false + clusterFederatedTrustDomains: false + ## @param externalControllerManagers.defaults.className specify to use an explicit class name. If empty, it will be automatically set to Release.Namespace-Release.Name to not conflict with other installs, enabling parallel installs. + className: "" + ## @param externalControllerManagers.defaults.watchClassless specify to process custom resources without class name specified. Useful to slowly migrate to class names from classless installs. Do not have two installs on the same k8s cluster both set to true. + watchClassless: false + ## @param externalControllerManagers.defaults.entryIDPrefixCleanup consult the spiffe.io docs about this option before changing. Its unlikely you will need to ever change it. + entryIDPrefixCleanup: false + ## @param externalControllerManagers.defaults.parentIDTemplate The template that is used to register workloads. + parentIDTemplate: "spiffe://{{ .TrustDomain }}/spire/agent/k8s_psat/{{ .ClusterName }}/{{ .NodeMeta.UID }}" + ## @param externalControllerManagers.defaults.expandEnv Set to true to enable environment variable substitution of config file options + expandEnv: false + ## @param externalControllerManagers.defaults.extraEnv [array] Extra environment variables to add to the controller manager + extraEnv: [] + ## @param externalControllerManagers.defaults.resources [object] Resource requests and limits for controller manager + resources: {} + ## @param externalControllerManagers.defaults.securityContext [object] Security context + securityContext: {} + configMap: + ## @param externalControllerManagers.defaults.configMap.annotations [object] Annotations to add to the Controller Manager ConfigMap + annotations: {} + ## @param externalControllerManagers.defaults.ignoreNamespaces [array] These namespaces are ignored by controller manager + ignoreNamespaces: + - kube-system + - kube-public + - local-path-storage + ## @param externalControllerManagers.defaults.cacheNamespaces [object] If specified restricts the manager's cache to watch objects in the desired namespaces. Defaults to all namespaces. + cacheNamespaces: {} + + ## @param externalControllerManagers.clusters [object] A dictionary of clusters to add with optional overrides. If empty, all clusters defined in kubeConfigs will be used. + clusters: {} + # clustera: + # Should match the name of the config in the kubeConfigs section + # kubeConfigName: foo + # reconcile: + # clusterStaticEntries: true + # other: {} + tools: kubectl: ## @param tools.kubectl.image.registry The OCI registry to pull the image from diff --git a/charts/spire/values.yaml b/charts/spire/values.yaml index c77eb696c..ce3b5391e 100644 --- a/charts/spire/values.yaml +++ b/charts/spire/values.yaml @@ -117,6 +117,9 @@ spire-server: controllerManager: ## @param spire-server.controllerManager.enabled Enable controller manager and provision CRD's enabled: true + externalControllerManagers: + ## @param spire-server.externalControllerManagers.enabled Enable external controller manager support + enabled: true ## @section Spire agent parameters ## Parameter values for Spire agent diff --git a/tests/integration/psat/kind-config.yaml b/tests/integration/psat/child-kind-config.yaml similarity index 81% rename from tests/integration/psat/kind-config.yaml rename to tests/integration/psat/child-kind-config.yaml index d85992ac3..086e96bf0 100644 --- a/tests/integration/psat/kind-config.yaml +++ b/tests/integration/psat/child-kind-config.yaml @@ -4,4 +4,4 @@ networking: apiServerAddress: "172.17.0.1" apiServerPort: 7443 podSubnet: "10.245.0.0/16" - serviceSubnet: "10.97.0.0/12" + serviceSubnet: "10.97.0.0/16" diff --git a/tests/integration/psat/child-values.yaml b/tests/integration/psat/child-values.yaml new file mode 100644 index 000000000..63b655d8e --- /dev/null +++ b/tests/integration/psat/child-values.yaml @@ -0,0 +1,20 @@ +global: + spire: + recommendations: + enabled: true + namespaces: + create: true + clusterName: production + trustDomain: production.other + +spire-server: + enabled: false + +spire-agent: + enabled: false + +spiffe-csi-drvier: + enabled: false + +spiffe-oidc-discovery-provider: + enabled: true diff --git a/tests/integration/psat/other-kind-config.yaml b/tests/integration/psat/other-kind-config.yaml new file mode 100644 index 000000000..0204e7529 --- /dev/null +++ b/tests/integration/psat/other-kind-config.yaml @@ -0,0 +1,7 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +networking: + apiServerAddress: "172.17.0.1" + apiServerPort: 5443 + podSubnet: "10.246.0.0/16" + serviceSubnet: "10.98.0.0/16" diff --git a/tests/integration/psat/run-tests.sh b/tests/integration/psat/run-tests.sh index 6b7785aec..fb302fef1 100755 --- a/tests/integration/psat/run-tests.sh +++ b/tests/integration/psat/run-tests.sh @@ -5,7 +5,7 @@ set -xe SCRIPT="$(readlink -f "$0")" SCRIPTPATH="$(dirname "${SCRIPT}")" TESTDIR="${SCRIPTPATH}/../../../.github/tests" -DEPS="${TESTDIR}/dependencies" +#DEPS="${TESTDIR}/dependencies" # shellcheck source=/dev/null source "${SCRIPTPATH}/../../../.github/scripts/parse-versions.sh" @@ -45,24 +45,45 @@ teardown() { trap 'EC=$? && trap - SIGTERM && teardown $EC' SIGINT SIGTERM EXIT -kubectl create namespace spire-system --dry-run=client -o yaml | kubectl apply -f - -kubectl label namespace spire-system pod-security.kubernetes.io/enforce=privileged || true -kubectl create namespace spire-server --dry-run=client -o yaml | kubectl apply -f - -kubectl label namespace spire-server pod-security.kubernetes.io/enforce=restricted || true - -helm upgrade --install --create-namespace spire charts/spire \ - --namespace spire-root-server \ - --values "${DEPS}/spire-root-server-values.yaml" \ - --wait - -kind create cluster --name other --kubeconfig "${SCRIPTPATH}/kubeconfig" --config "${SCRIPTPATH}/kind-config.yaml" -md5sum "${SCRIPTPATH}/kubeconfig" -wc -l "${SCRIPTPATH}/kubeconfig" -KCB64="$(base64 < "${SCRIPTPATH}/kubeconfig" | tr '\n' ' ' | sed 's/ //g')" -kubectl --kubeconfig "${SCRIPTPATH}/kubeconfig" create namespace spire-system -kubectl --kubeconfig "${SCRIPTPATH}/kubeconfig" create configmap -n spire-system spire-bundle-upstream - -helm upgrade --install --create-namespace --namespace spire-server --values "${SCRIPTPATH}/values.yaml" \ - --wait spire charts/spire --set "spire-server.kubeConfigs.other.kubeConfigBase64=$KCB64" -helm test --namespace spire-server spire -kubectl --kubeconfig "${SCRIPTPATH}/kubeconfig" get configmap -n spire-system spire-bundle-upstream +#helm upgrade --install --create-namespace spire charts/spire \ +# --namespace spire-root-server \ +# --values "${DEPS}/spire-root-server-values.yaml" \ +# --wait + +kind create cluster --name child --kubeconfig "${SCRIPTPATH}/kubeconfig-child" --config "${SCRIPTPATH}/child-kind-config.yaml" +md5sum "${SCRIPTPATH}/kubeconfig-child" +wc -l "${SCRIPTPATH}/kubeconfig-child" +CHILD_KCB64="$(base64 < "${SCRIPTPATH}/kubeconfig-child" | tr '\n' ' ' | sed 's/ //g')" + +helm upgrade --kubeconfig "${SCRIPTPATH}/kubeconfig-child" --install --create-namespace --namespace spire-mgmt spire-crds charts/spire-crds +kubectl --kubeconfig "${SCRIPTPATH}/kubeconfig-child" apply -f "${SCRIPTPATH}/sodp-clusterspiffeid.yaml" +helm upgrade --kubeconfig "${SCRIPTPATH}/kubeconfig-child" --install --namespace spire-mgmt --values "${SCRIPTPATH}/child-values.yaml" \ + spire charts/spire +kubectl --kubeconfig "${SCRIPTPATH}/kubeconfig-child" create configmap -n spire-system spire-bundle-upstream + +kind create cluster --name other --kubeconfig "${SCRIPTPATH}/kubeconfig-other" --config "${SCRIPTPATH}/other-kind-config.yaml" +md5sum "${SCRIPTPATH}/kubeconfig-other" +wc -l "${SCRIPTPATH}/kubeconfig-other" +OTHER_KCB64="$(base64 < "${SCRIPTPATH}/kubeconfig-other" | tr '\n' ' ' | sed 's/ //g')" + +helm upgrade --kubeconfig "${SCRIPTPATH}/kubeconfig-other" --install --create-namespace --namespace spire-mgmt spire-crds charts/spire-crds +kubectl --kubeconfig "${SCRIPTPATH}/kubeconfig-other" apply -f "${SCRIPTPATH}/sodp-clusterspiffeid.yaml" +helm upgrade --kubeconfig "${SCRIPTPATH}/kubeconfig-other" --install --namespace spire-mgmt --values "${SCRIPTPATH}/child-values.yaml" \ + spire charts/spire +kubectl --kubeconfig "${SCRIPTPATH}/kubeconfig-other" create configmap -n spire-system spire-bundle-upstream + +helm upgrade --install --create-namespace --namespace spire-mgmt --values "${SCRIPTPATH}/values.yaml" \ + --wait spire charts/spire \ + --set "spire-server.kubeConfigs.child.kubeConfigBase64=${CHILD_KCB64}" \ + --set "spire-server.kubeConfigs.other.kubeConfigBase64=${OTHER_KCB64}" +helm test --namespace spire-mgmt spire +kubectl --kubeconfig "${SCRIPTPATH}/kubeconfig-child" get configmap -n spire-system spire-bundle-upstream +kubectl --kubeconfig "${SCRIPTPATH}/kubeconfig-other" get configmap -n spire-system spire-bundle-upstream + +ENTRIES="$(kubectl exec -i -n spire-server spire-server-0 -- spire-server entry show)" + +if [[ "${ENTRIES}" == "Found 0 entries" ]]; then + echo "${ENTRIES}" + exit 1 +fi + diff --git a/tests/integration/psat/sodp-clusterspiffeid.yaml b/tests/integration/psat/sodp-clusterspiffeid.yaml new file mode 100644 index 000000000..2e3de5f1e --- /dev/null +++ b/tests/integration/psat/sodp-clusterspiffeid.yaml @@ -0,0 +1,23 @@ +apiVersion: spire.spiffe.io/v1alpha1 +kind: ClusterSPIFFEID +metadata: + name: spire-mgmt-spire-oidc-discovery-provider +spec: + autoPopulateDNSNames: true + className: spire-mgmt-spire + dnsNameTemplates: + - oidc-discovery.{{ .TrustDomain }} + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: In + values: + - spire-mgmt + - spire-server + - spire-system + podSelector: + matchLabels: + component: oidc-discovery-provider + release: spire + release-namespace: spire-mgmt + spiffeIDTemplate: spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }} diff --git a/tests/integration/psat/values.yaml b/tests/integration/psat/values.yaml index fbb7b55e8..1144d71ca 100644 --- a/tests/integration/psat/values.yaml +++ b/tests/integration/psat/values.yaml @@ -2,11 +2,29 @@ global: spire: recommendations: enabled: true + namespaces: + create: true clusterName: production trustDomain: production.other + caSubject: + country: US + organization: Production + commonName: production.other spire-server: - ca_subject: - country: US - organization: Production - common_name: production.other + controllerManager: + reconcile: + clusterSPIFFEIDs: false + clusterStaticEntries: true + clusterFederatedTrustDomains: true + identities: + clusterSPIFFEIDs: + default: + enabled: false + oidc-discovery-provider: + enabled: false + test-keys: + enabled: false + +spiffe-oidc-discovery-provider: + enabled: false From c37de1ea0cbda3b0c38bad1b04a5eaa6cf3e1c8c Mon Sep 17 00:00:00 2001 From: Mariusz Sabath Date: Fri, 3 May 2024 11:04:38 -0400 Subject: [PATCH 16/37] Fix Tornjak logsDir for Openshift (#344) * Fix Tornjak logsDir for Openshift Signed-off-by: Mariusz Sabath * Update docs Signed-off-by: Mariusz Sabath * Update charts/spire/charts/tornjak-frontend/templates/_helpers.tpl Co-authored-by: kfox1111 Signed-off-by: Mariusz Sabath --------- Signed-off-by: Mariusz Sabath Co-authored-by: kfox1111 --- charts/spire/charts/tornjak-frontend/README.md | 2 +- .../charts/tornjak-frontend/templates/_helpers.tpl | 10 ++++++++++ .../charts/tornjak-frontend/templates/deployment.yaml | 2 +- charts/spire/charts/tornjak-frontend/values.yaml | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/charts/spire/charts/tornjak-frontend/README.md b/charts/spire/charts/tornjak-frontend/README.md index 0216f4523..208aab7c5 100644 --- a/charts/spire/charts/tornjak-frontend/README.md +++ b/charts/spire/charts/tornjak-frontend/README.md @@ -89,7 +89,7 @@ port forwarding. See the chart NOTES output for more details. | `startupProbe.failureThreshold` | Failure threshold count for startupProbe | `6` | | `startupProbe.successThreshold` | Success threshold count for startupProbe | `1` | | `workingDir` | Set to override the default path containing the Tornjak frontend within the image | `""` | -| `logsDir` | Directory path for NPM logs | `/home/node/` | +| `logsDir` | Directory path for NPM logs | `""` | | `ingress.enabled` | Flag to enable ingress for Tornjak frontend service | `false` | | `ingress.className` | Ingress class name for Tornjak frontend service | `""` | | `ingress.controllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, auto-detection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | diff --git a/charts/spire/charts/tornjak-frontend/templates/_helpers.tpl b/charts/spire/charts/tornjak-frontend/templates/_helpers.tpl index c810f0f0a..d36812404 100644 --- a/charts/spire/charts/tornjak-frontend/templates/_helpers.tpl +++ b/charts/spire/charts/tornjak-frontend/templates/_helpers.tpl @@ -100,3 +100,13 @@ Create URL for accessing Tornjak APIs {{- printf "/usr/src/app" }} {{- end }} {{- end }} + +{{- define "tornjak-frontend.logsDir" }} +{{- if .Values.logsDir }} +{{- .Values.logsDir }} +{{- else if (dig "openshift" false .Values.global) }} +{{- printf "/opt/app-root/src/.npm/_cacache/" }} +{{- else }} +{{- printf "/home/node/" }} +{{- end }} +{{- end }} diff --git a/charts/spire/charts/tornjak-frontend/templates/deployment.yaml b/charts/spire/charts/tornjak-frontend/templates/deployment.yaml index 97d187a09..e70b5c0b0 100644 --- a/charts/spire/charts/tornjak-frontend/templates/deployment.yaml +++ b/charts/spire/charts/tornjak-frontend/templates/deployment.yaml @@ -61,7 +61,7 @@ spec: - name: env mountPath: {{ include "tornjak-frontend.workingDir" . }}/build/tmp - name: logs - mountPath: {{ .Values.logsDir }} + mountPath: {{ include "tornjak-frontend.logsDir" . }} {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} diff --git a/charts/spire/charts/tornjak-frontend/values.yaml b/charts/spire/charts/tornjak-frontend/values.yaml index 44ae3ec74..b7aab013f 100644 --- a/charts/spire/charts/tornjak-frontend/values.yaml +++ b/charts/spire/charts/tornjak-frontend/values.yaml @@ -120,7 +120,7 @@ startupProbe: workingDir: "" ## @param logsDir Directory path for NPM logs -logsDir: "/home/node/" +logsDir: "" ## @param ingress.enabled Flag to enable ingress for Tornjak frontend service ## @param ingress.className Ingress class name for Tornjak frontend service From da4ebdfcaf19f903dd6b257feb7e00feace8fd47 Mon Sep 17 00:00:00 2001 From: kfox1111 Date: Fri, 3 May 2024 14:17:31 -0700 Subject: [PATCH 17/37] Fix federation certificate name when upstream enabled (#347) When both federation certificates and upstream authority both use cert-manager, there is a naming conflict. Signed-off-by: Kevin Fox --- .../charts/spire-server/templates/federation-certificate.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/spire/charts/spire-server/templates/federation-certificate.yaml b/charts/spire/charts/spire-server/templates/federation-certificate.yaml index 013d3026f..a4329d7a3 100644 --- a/charts/spire/charts/spire-server/templates/federation-certificate.yaml +++ b/charts/spire/charts/spire-server/templates/federation-certificate.yaml @@ -20,7 +20,7 @@ secretName: {{ $issuerFullName }}-cert apiVersion: cert-manager.io/v1 kind: Certificate metadata: - name: {{ include "spire-server.fullname" . }} + name: {{ include "spire-server.fullname" . }}-fed namespace: {{ include "spire-server.namespace" . }} spec: {{ merge (include "spire-server.federation-cert-manager-default-cert" . | fromYaml) .Values.federation.tls.certManager.certificate | toYaml | nindent 2 }} From 7165b20ddf61a7b28fadf579426f9ae0207568ad Mon Sep 17 00:00:00 2001 From: "spire-helm-version-checker[bot]" <161522935+spire-helm-version-checker[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 12:57:43 -0700 Subject: [PATCH 18/37] Bump test chart dependencies (#350) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: marcofranssen <694733+marcofranssen@users.noreply.github.com> --- .github/tests/charts.json | 6 +++--- .../spire/charts/spiffe-oidc-discovery-provider/README.md | 4 ++-- .../spire/charts/spiffe-oidc-discovery-provider/values.yaml | 4 ++-- charts/spire/charts/spire-agent/README.md | 4 ++-- charts/spire/charts/spire-agent/values.yaml | 4 ++-- charts/spire/charts/spire-server/README.md | 2 +- charts/spire/charts/spire-server/values.yaml | 2 +- charts/spire/charts/tornjak-frontend/README.md | 2 +- charts/spire/charts/tornjak-frontend/values.yaml | 2 +- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/tests/charts.json b/.github/tests/charts.json index 82f843f57..c2abe96a8 100644 --- a/.github/tests/charts.json +++ b/.github/tests/charts.json @@ -2,7 +2,7 @@ { "name": "kube-prometheus-stack", "repo": "https://prometheus-community.github.io/helm-charts", - "version": "58.2.2" + "version": "58.4.0" }, { "name": "cert-manager", @@ -17,11 +17,11 @@ { "name": "mysql", "repo": "https://charts.bitnami.com/bitnami", - "version": "10.1.1" + "version": "10.2.2" }, { "name": "postgresql", "repo": "https://charts.bitnami.com/bitnami", - "version": "15.2.5" + "version": "15.2.8" } ] diff --git a/charts/spire/charts/spiffe-oidc-discovery-provider/README.md b/charts/spire/charts/spiffe-oidc-discovery-provider/README.md index da0fc2b39..06ea70037 100644 --- a/charts/spire/charts/spiffe-oidc-discovery-provider/README.md +++ b/charts/spire/charts/spiffe-oidc-discovery-provider/README.md @@ -115,11 +115,11 @@ A Helm chart to install the SPIFFE OIDC discovery provider. | `tests.bash.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.bash.image.repository` | The repository within the registry | `chainguard/bash` | | `tests.bash.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1` | +| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda` | | `tests.toolkit.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.toolkit.image.repository` | The repository within the registry | `chainguard/min-toolkit-debug` | | `tests.toolkit.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.toolkit.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:773ae9342541de6efc71425dbeeb863fc6cf9856c0dac89266ae3fd8d14a4528` | +| `tests.toolkit.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:de4a5424d61065ccfc444176c6dc34578210500c9466b0f317f83404fd94d2b6` | | `tests.step.image.registry` | The OCI registry to pull the image from | `docker.io` | | `tests.step.image.repository` | The repository within the registry | `smallstep/step-cli` | | `tests.step.image.pullPolicy` | The image pull policy | `IfNotPresent` | diff --git a/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml b/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml index 1f5483a2b..630054407 100644 --- a/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml +++ b/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml @@ -328,7 +328,7 @@ tests: registry: cgr.dev repository: chainguard/bash pullPolicy: IfNotPresent - tag: latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1 + tag: latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda toolkit: ## @param tests.toolkit.image.registry The OCI registry to pull the image from @@ -340,7 +340,7 @@ tests: registry: cgr.dev repository: chainguard/min-toolkit-debug pullPolicy: IfNotPresent - tag: latest@sha256:773ae9342541de6efc71425dbeeb863fc6cf9856c0dac89266ae3fd8d14a4528 + tag: latest@sha256:de4a5424d61065ccfc444176c6dc34578210500c9466b0f317f83404fd94d2b6 step: ## @param tests.step.image.registry The OCI registry to pull the image from diff --git a/charts/spire/charts/spire-agent/README.md b/charts/spire/charts/spire-agent/README.md index 29eefbe35..813ea025b 100644 --- a/charts/spire/charts/spire-agent/README.md +++ b/charts/spire/charts/spire-agent/README.md @@ -70,7 +70,7 @@ A Helm chart to install the SPIRE agent. | `fsGroupFix.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `fsGroupFix.image.repository` | The repository within the registry | `chainguard/bash` | | `fsGroupFix.image.pullPolicy` | The image pull policy | `Always` | -| `fsGroupFix.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1` | +| `fsGroupFix.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda` | | `fsGroupFix.resources` | Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | `{}` | | `keyManager.memory.enabled` | Enable the memory based Key Manager | `true` | | `nodeAttestor.k8sPsat.enabled` | Enable Psat k8s Node Attestor | `true` | @@ -106,7 +106,7 @@ A Helm chart to install the SPIRE agent. | `socketAlternate.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `socketAlternate.image.repository` | The repository within the registry | `chainguard/bash` | | `socketAlternate.image.pullPolicy` | The image pull policy | `Always` | -| `socketAlternate.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1` | +| `socketAlternate.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda` | | `socketAlternate.resources` | Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | `{}` | | `priorityClassName` | Priority class assigned to daemonset pods. Can be auto set with global.recommendations.priorityClassName. | `""` | | `extraEnvVars` | Extra environment variables to be added to the Spire Agent container | `[]` | diff --git a/charts/spire/charts/spire-agent/values.yaml b/charts/spire/charts/spire-agent/values.yaml index dc220e200..a9426a350 100644 --- a/charts/spire/charts/spire-agent/values.yaml +++ b/charts/spire/charts/spire-agent/values.yaml @@ -154,7 +154,7 @@ fsGroupFix: registry: cgr.dev repository: chainguard/bash pullPolicy: Always - tag: latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1 + tag: latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda ## @param fsGroupFix.resources Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ resources: {} @@ -262,7 +262,7 @@ socketAlternate: registry: cgr.dev repository: chainguard/bash pullPolicy: Always - tag: latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1 + tag: latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda ## @param socketAlternate.resources Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ resources: {} diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index c0c9b4904..a551813c1 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -404,5 +404,5 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `tests.bash.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.bash.image.repository` | The repository within the registry | `chainguard/bash` | | `tests.bash.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1` | +| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda` | | `kubeConfigs` | Manage additional kubeconfig files to talk to external Kubernetes clusters | `{}` | diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index 570454cbd..5588a20d6 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -1013,7 +1013,7 @@ tests: registry: cgr.dev repository: chainguard/bash pullPolicy: IfNotPresent - tag: latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1 + tag: latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda ## @param kubeConfigs [object] Manage additional kubeconfig files to talk to external Kubernetes clusters kubeConfigs: {} diff --git a/charts/spire/charts/tornjak-frontend/README.md b/charts/spire/charts/tornjak-frontend/README.md index 208aab7c5..01c41da0d 100644 --- a/charts/spire/charts/tornjak-frontend/README.md +++ b/charts/spire/charts/tornjak-frontend/README.md @@ -101,4 +101,4 @@ port forwarding. See the chart NOTES output for more details. | `tests.bash.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.bash.image.repository` | The repository within the registry | `chainguard/bash` | | `tests.bash.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1` | +| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda` | diff --git a/charts/spire/charts/tornjak-frontend/values.yaml b/charts/spire/charts/tornjak-frontend/values.yaml index b7aab013f..11260fef4 100644 --- a/charts/spire/charts/tornjak-frontend/values.yaml +++ b/charts/spire/charts/tornjak-frontend/values.yaml @@ -162,4 +162,4 @@ tests: registry: cgr.dev repository: chainguard/bash pullPolicy: IfNotPresent - tag: latest@sha256:a0383e176104e7840387deb9fda1782660e903654f39acf62931e2c9a60b7fe1 + tag: latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda From b2e9f407749b131036c837f3c30b56fc0cae9254 Mon Sep 17 00:00:00 2001 From: kfox1111 Date: Tue, 7 May 2024 20:16:15 -0700 Subject: [PATCH 19/37] Bump spire version (#352) Signed-off-by: Kevin Fox --- charts/spire/Chart.yaml | 2 +- charts/spire/README.md | 2 +- charts/spire/charts/spiffe-oidc-discovery-provider/Chart.yaml | 2 +- charts/spire/charts/spire-agent/Chart.yaml | 2 +- charts/spire/charts/spire-server/Chart.yaml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/charts/spire/Chart.yaml b/charts/spire/Chart.yaml index 3839ce265..ec16fed2a 100644 --- a/charts/spire/Chart.yaml +++ b/charts/spire/Chart.yaml @@ -4,7 +4,7 @@ description: > A Helm chart for deploying the complete Spire stack including: spire-server, spire-agent, spiffe-csi-driver, spiffe-oidc-discovery-provider and spire-controller-manager. type: application version: 0.20.0 -appVersion: "1.9.4" +appVersion: "1.9.5" keywords: ["spiffe", "spire", "spire-server", "spire-agent", "oidc", "spire-controller-manager"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire sources: diff --git a/charts/spire/README.md b/charts/spire/README.md index d9bc66ad3..1496fe81d 100644 --- a/charts/spire/README.md +++ b/charts/spire/README.md @@ -1,6 +1,6 @@ # spire -![Version: 0.20.0](https://img.shields.io/badge/Version-0.20.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.9.4](https://img.shields.io/badge/AppVersion-1.9.4-informational?style=flat-square) +![Version: 0.20.0](https://img.shields.io/badge/Version-0.20.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.9.5](https://img.shields.io/badge/AppVersion-1.9.5-informational?style=flat-square) [![Development Phase](https://github.com/spiffe/spiffe/blob/main/.img/maturity/dev.svg)](https://github.com/spiffe/spiffe/blob/main/MATURITY.md#development) A Helm chart for deploying the complete Spire stack including: spire-server, spire-agent, spiffe-csi-driver, spiffe-oidc-discovery-provider and spire-controller-manager. diff --git a/charts/spire/charts/spiffe-oidc-discovery-provider/Chart.yaml b/charts/spire/charts/spiffe-oidc-discovery-provider/Chart.yaml index 4c99b7707..cb678037f 100644 --- a/charts/spire/charts/spiffe-oidc-discovery-provider/Chart.yaml +++ b/charts/spire/charts/spiffe-oidc-discovery-provider/Chart.yaml @@ -3,7 +3,7 @@ name: spiffe-oidc-discovery-provider description: A Helm chart to install the SPIFFE OIDC discovery provider. type: application version: 0.1.0 -appVersion: "1.9.4" +appVersion: "1.9.5" keywords: ["spiffe", "oidc"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire sources: diff --git a/charts/spire/charts/spire-agent/Chart.yaml b/charts/spire/charts/spire-agent/Chart.yaml index 888df65ec..077fbd0b4 100644 --- a/charts/spire/charts/spire-agent/Chart.yaml +++ b/charts/spire/charts/spire-agent/Chart.yaml @@ -3,7 +3,7 @@ name: spire-agent description: A Helm chart to install the SPIRE agent. type: application version: 0.1.0 -appVersion: "1.9.4" +appVersion: "1.9.5" keywords: ["spiffe", "spire-agent"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire sources: diff --git a/charts/spire/charts/spire-server/Chart.yaml b/charts/spire/charts/spire-server/Chart.yaml index ec4dfc971..65d3e6dec 100644 --- a/charts/spire/charts/spire-server/Chart.yaml +++ b/charts/spire/charts/spire-server/Chart.yaml @@ -3,7 +3,7 @@ name: spire-server description: A Helm chart to install the SPIRE server. type: application version: 0.1.0 -appVersion: "1.9.4" +appVersion: "1.9.5" keywords: ["spiffe", "spire-server", "spire-controller-manager"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire sources: From be560d95d89148426c76e6ae38d1706927fb2bdd Mon Sep 17 00:00:00 2001 From: kfox1111 Date: Tue, 7 May 2024 20:38:04 -0700 Subject: [PATCH 20/37] Check for a misconfiguration of bundle endpoint profiles (#348) * Check for a misconfiguration of bundle endpoint profiles Signed-off-by: Kevin Fox * Update charts/spire/charts/spire-server/templates/configmap.yaml Co-authored-by: Faisal Memon Signed-off-by: kfox1111 --------- Signed-off-by: Kevin Fox Signed-off-by: kfox1111 Co-authored-by: Faisal Memon --- charts/spire/charts/spire-server/templates/configmap.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/charts/spire/charts/spire-server/templates/configmap.yaml b/charts/spire/charts/spire-server/templates/configmap.yaml index a4e07a5e1..dc1210867 100644 --- a/charts/spire/charts/spire-server/templates/configmap.yaml +++ b/charts/spire/charts/spire-server/templates/configmap.yaml @@ -38,6 +38,9 @@ {{- if and (eq (.Values.keyManager.awsKMS.keyIdentifierFile.enabled | toString) "true") (eq (.Values.keyManager.awsKMS.keyIdentifierValue.enabled | toString ) "true") }} {{- fail "You can only enable one of keyIdentifierFile or keyIdentifierValue at a time" }} {{- end }} +{{- if hasKey .Values.federation.bundleEndpoint "profile" }} +{{- fail "Configuring the federation bundle endpoint profile directly isn't supported. You can specify the settings via federation.tls" }} +{{- end }} {{- define "spire-server.yaml-config" -}} {{- $upstreamAuthorityUsed := 0 }} {{- $keyManagerUsed := 0 }} From a9b04fd86c3d9ca16e63d3273f354fd256b6860a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 May 2024 22:16:18 -0700 Subject: [PATCH 21/37] Bump github.com/onsi/ginkgo/v2 from 2.17.2 to 2.17.3 in /tests (#353) --- tests/go.mod | 2 +- tests/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/go.mod b/tests/go.mod index 4bb724eb9..85633f5f3 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -5,7 +5,7 @@ go 1.21 toolchain go1.21.5 require ( - github.com/onsi/ginkgo/v2 v2.17.2 + github.com/onsi/ginkgo/v2 v2.17.3 github.com/onsi/gomega v1.33.1 helm.sh/helm/v3 v3.14.4 ) diff --git a/tests/go.sum b/tests/go.sum index 7c5108932..51169c7e8 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -86,8 +86,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g= -github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= +github.com/onsi/ginkgo/v2 v2.17.3 h1:oJcvKpIb7/8uLpDDtnQuf18xVnwKp8DTD7DQ6gTd/MU= +github.com/onsi/ginkgo/v2 v2.17.3/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= From b6575c172db0069ab68532438bb99fd76e53c28b Mon Sep 17 00:00:00 2001 From: Mariusz Sabath Date: Thu, 9 May 2024 07:26:17 -0400 Subject: [PATCH 22/37] Update Tornjak deployment docs (#288) * Update Tornjak deployment docs Signed-off-by: Mariusz Sabath * Change the reference for installing standard Tornjak Signed-off-by: Mariusz Sabath * Update examples/tornjak/README.md Co-authored-by: kfox1111 Signed-off-by: Mariusz Sabath * Adjust deployment paths Signed-off-by: Mariusz Sabath * Remove the production README changes Signed-off-by: Mariusz Sabath * Minor text edits Signed-off-by: Mariusz Sabath * Fix incorrect namespace value Signed-off-by: Mariusz Sabath * Updat Tornjak README Signed-off-by: Mariusz Sabath * Update Keycloak README Signed-off-by: Mariusz Sabath * Text updates in Keycloak doc Signed-off-by: Mariusz Sabath * Post-review updates Signed-off-by: Mariusz Sabath * Update Tornjak message for User Management Signed-off-by: Mariusz Sabath * Update examples/tornjak/keycloak/README.md Co-authored-by: Mohammed Abdi Signed-off-by: Mariusz Sabath * Update Tornjak deployment doc Signed-off-by: Mariusz Sabath * Improve the Tornjak Auth message Signed-off-by: Mariusz Sabath * Fix error with incorrect Ingress value Signed-off-by: Mariusz Sabath * Fix documentation format Signed-off-by: Mariusz Sabath * Update parameter format Signed-off-by: Mariusz Sabath * Removed redundand doc sections Signed-off-by: Mariusz Sabath --------- Signed-off-by: Mariusz Sabath Co-authored-by: kfox1111 Co-authored-by: Mohammed Abdi --- .../charts/spire-server/templates/NOTES.txt | 4 +- .../tornjak-frontend/templates/NOTES.txt | 15 ++- examples/tornjak/README.md | 99 +++++++++----- examples/tornjak/keycloak/README.md | 121 +++++++----------- examples/tornjak/values.yaml | 1 - 5 files changed, 122 insertions(+), 118 deletions(-) diff --git a/charts/spire/charts/spire-server/templates/NOTES.txt b/charts/spire/charts/spire-server/templates/NOTES.txt index f4d368fa4..d882cada0 100644 --- a/charts/spire/charts/spire-server/templates/NOTES.txt +++ b/charts/spire/charts/spire-server/templates/NOTES.txt @@ -1,8 +1,8 @@ Installed {{ .Chart.Name }}… -1. Get the currently registered SPIFFE entries from the server: +Get the currently registered SPIFFE entries from the server: - kubectl exec -n {{ .Release.Namespace }} {{ include "spire-server.fullname" . }}-0 -c spire-server -- \ + kubectl exec -n {{ include "spire-server.namespace" . }} {{ include "spire-server.fullname" . }}-0 -c spire-server -- \ spire-server entry show {{- if eq (.Values.tornjak.enabled | toString) "true" }} diff --git a/charts/spire/charts/tornjak-frontend/templates/NOTES.txt b/charts/spire/charts/tornjak-frontend/templates/NOTES.txt index ad5c90fdb..752ca751b 100644 --- a/charts/spire/charts/tornjak-frontend/templates/NOTES.txt +++ b/charts/spire/charts/tornjak-frontend/templates/NOTES.txt @@ -6,14 +6,15 @@ Your release is named: {{ .Release.Name }} Namespace: {{ include "tornjak-frontend.namespace" . }} Tornjak UI (Frontend) - image: {{ template "spire-lib.image" (dict "appVersion" $.Chart.AppVersion "image" .Values.image "global" .Values.global) }} - pull policy: {{ .Values.image.pullPolicy }} + Image: {{ template "spire-lib.image" (dict "appVersion" $.Chart.AppVersion "image" .Values.image "global" .Values.global) }} + Image pull policy: {{ .Values.image.pullPolicy }} + Ingress enabled: {{ .Values.ingress.enabled }} Tornjak API (Backend): {{ include "tornjak-frontend.apiURL" . }} - SPIRE health check enabled: "{{ .Values.spireHealthCheck.enabled }}" - User Managemenet enabled: "{{ .Values.auth.enabled }}" - User Managemenet API: "{{ .Values.auth.serverURL }}" - -{{- if not .Values.auth.enabled }} + SPIRE health check enabled: {{ .Values.spireHealthCheck.enabled }} + User Management enabled: {{ .Values.auth.enabled }} +{{- if .Values.auth.enabled }} + User Management API: "{{ .Values.auth.serverURL }}" +{{- else }} ### WARNING ### Tornjak is configured to run without authentication and is therefore NOT suitable to run in production environments. diff --git a/examples/tornjak/README.md b/examples/tornjak/README.md index cf876ca34..8893209ef 100644 --- a/examples/tornjak/README.md +++ b/examples/tornjak/README.md @@ -1,29 +1,41 @@ # Recommended setup to deploy Tornjak -To install Spire with the least privileges possible we deploy spire across 2 namespaces. +> [!WARNING] +> The default version of Tornjak in this chart is deployed without authentication. Therefore it is not suitable to run this version in production. In order to enable the user authentication, +> follow [Keycloak instructions](keycloak/README.md) + +## Deploy Standard SPIRE + +Follow the production installation of SPIRE as described in the [install instructions] (https://artifacthub.io/packages/helm/spiffe/spire) document. + +## Upgrade to enable Tornjak + +Before we can deploy Tornjak with SPIRE we need to decide whether the services would be +using direct access, Ingress, or some other method. + +## Tornjak with Direct Access + +This can be done using port-forward. For example, to start Tornjak APIs on port 10000 + +Deploy SPIRE with Tornjak enabled ```shell -kubectl create namespace "spire-system" -kubectl label namespace "spire-system" pod-security.kubernetes.io/enforce=privileged -kubectl create namespace "spire-server" -kubectl label namespace "spire-server" pod-security.kubernetes.io/enforce=restricted - -# deploy SPIRE with Tornjak enabled -helm upgrade --install --namespace spire-server spire charts/spire \ ---values tests/integration/psat/values.yaml \ +export TORNJAK_API=http://localhost:10000 + +helm upgrade --install -n spire-mgmt spire spire \ +--repo https://spiffe.github.io/helm-charts-hardened/ \ +--set tornjak-frontend.apiServerURL=$TORNJAK_API \ --values examples/tornjak/values.yaml \ +--values your-values.yaml \ --render-subchart-notes - # test the Tornjak deployment helm test spire -n spire-server ``` -## Access Tornjak - -To access Tornjak you will have to use port-forwarding for the time being *(until we add authentication and ingress)*. - -Run following commands from your shell, if you ran with different values your namespace might differ. Consult the install notes printed when running above `helm upgrade` command in that case. +Run following commands from your shell, to start port forwarding for Tornjak backend (APIs) +and Tornjak frontend (UI) services. + If you deployed in different namespace, your values might differ. Consult the install notes printed when running above `helm upgrade` command in that case. Since `port-forward` is a blocking command, execute them in two different consoles: @@ -35,38 +47,63 @@ kubectl -n spire-server port-forward service/spire-tornjak-backend 10000:10000 kubectl -n spire-server port-forward service/spire-tornjak-frontend 3000:3000 ``` -You can now access Tornjak at [localhost:3000](http://localhost:3000). +You can now access Tornjak with your browser at [localhost:3000](http://localhost:3000). See [values.yaml](./values.yaml) for more details on the chart configurations to achieve this setup. -## Tornjak and Ingress with ingress-nginx +## Deploy Tornjak with ingress-nginx -Update examples/production/example-your-values.yaml with your information, most importantly, trustDomain. +Update your-values.yaml with your ingress information, most importantly, trustDomain, and redeploy +adding the following: ```shell -helm upgrade --install --namespace spire-server spire charts/spire \ ---values tests/integration/psat/values.yaml \ ---values examples/tornjak/values.yaml \ ---values examples/tornjak/values-ingress.yaml \ --set global.spire.ingressControllerType=ingress-nginx \ ---render-subchart-notes --debug +--values examples/tornjak/values-ingress.yaml ``` -## Tornjak and Ingress on Openshift +## Deploy Tornjak with Ingress on Openshift + +Obtain the OpenShift Apps Subdomain for Ingress and assign it to the `trustDomain` +environment variable: -When deploying on Openshift, follow the deployment setup as described in -[Openshift README](../openshift/README.md) +```shell +export appdomain=$(oc get cm -n openshift-config-managed console-public -o go-template="{{ .data.consoleURL }}" | sed 's@https://@@; s/^[^.]*\.//') +echo $appdomain +``` -Then just add Openshift specific configuration to the above command: +So it can be passed as follow: ```shell ---values examples/openshift/openshift-values.yaml +--set global.openshift=true \ +--set global.spire.trustDomain=$appdomain \ +--values examples/tornjak/values-ingress.yaml \ ``` When running on Openshift in some environments like IBM Cloud, -you might need to add the following configurations: +you might need to also add the following configurations: + +```shell +--values examples/openshift/values-ibm-cloud.yaml +``` + +## Validation + +Confirm access to the Tornjak API (backend): + +```shell +curl https://tornjak-backend.$appdomain +"Welcome to the Tornjak Backend!" +``` + +If the APIs are accessible, we can verify the Tornjak UI (A React application running in the local browser) can be accessed. +Test access to Tornjak by opening the URL provided in Tornjak-frontend route: + +```shell +oc get route -n spire-server -l=app.kubernetes.io/name=tornjak-frontend -o jsonpath='https://{ .items[0].spec.host }' +``` + +The value should match the following URL: ```shell ---set spiffe-csi-driver.kubeletPath=/var/data/kubelet \ ---set spiffe-csi-driver.restrictedScc.enabled=true \ +echo "https://tornjak-frontend.$appdomain" ``` diff --git a/examples/tornjak/keycloak/README.md b/examples/tornjak/keycloak/README.md index 46cc2c6af..f9c47315d 100644 --- a/examples/tornjak/keycloak/README.md +++ b/examples/tornjak/keycloak/README.md @@ -4,16 +4,18 @@ This example demonstrates Tornjak's capability to control access to the Frontend User Management via [Keycloak](https://www.keycloak.org/). Tested on: + - Keycloak Application Version - 24.0.3 - Keycloak Chart Version - 21.0.3 For more information regarding Tornjak User Management, please refer to the following documentation: -* [Tornjak User Management](https://github.com/spiffe/tornjak/blob/main/docs/keycloak-configuration.md) -* [Keycloak Configuration for Tornjak](https://github.com/spiffe/tornjak/blob/main/docs/keycloak-configuration.md) -* [Detailed Blogs on Tornjak User Management](https://github.com/spiffe/tornjak/blob/main/docs/blogs.md) +- [Tornjak User Management](https://github.com/spiffe/tornjak/blob/main/docs/user-management.md) +- [Keycloak Configuration for Tornjak](https://github.com/spiffe/tornjak/blob/main/docs/keycloak-configuration.md) +- [Detailed Blogs on Tornjak User Management](https://github.com/spiffe/tornjak/blob/main/docs/blogs.md) -**NOTE:** This example works only with the Vanilla version of Kubernetes; it does not yet support Openshift. +> [!NOTE] +> This example works only with the Vanilla version of Kubernetes; it does not yet support Openshift. As part of the exercise, an instance of Keycloak is deployed to illustrate how to manage users' access to Tornjak. Once enabled, the Tornjak UI will redirect all authentication calls to the Keycloak instance to obtain the @@ -21,94 +23,59 @@ correct credentials. Authorization is based on these credentials and occurs at t ## Deploy Keycloak Instance (Authentication Service) -We will deploy the instance of Keycloak in the same namespace as the SPIRE Server - -```shell -# Create a namespace to deploy Keycloak and SPIRE-server -kubectl create namespace spire-server -``` - -```shell -# Deploy Keycloak as an authentication service -helm upgrade --install -n spire-server keycloak --values examples/tornjak/keycloak/values.yaml oci://registry-1.docker.io/bitnamicharts/keycloak --render-subchart-notes -``` - -* It's important to start the service before configuring Tornjak with auth. +We will deploy the instance of Keycloak in a dedicated namespace ```shell -# Start an auth Service [Keycloak] (Terminal 3) -kubectl -n spire-server port-forward service/keycloak 8080:80 +# If does not exist, create a namespace to deploy Keycloak +kubectl create namespace keycloak ``` -## Deploy SPIRE with Tornjak User Management Enabled - -Please follow the instructions for deploying Tornjak as specified in Tornjak Example [here](../README.md) -with addition of the User Management values `--values examples/tornjak/values-auth.yaml`. -For example: +> [!IMPORTANT] +> The example uses default userid and password (`admin`,`admin`). You must change these values +> by setting `auth.adminUser` and `auth.adminPassword` as shown below. ```shell -# Install SPIRE CRDs -helm upgrade --install --create-namespace -n spire-mgmt spire-crds charts/spire-crds +# Deploy most recent Keycloak instance as an authentication service +helm upgrade --install -n keycloak keycloak \ +--values examples/tornjak/keycloak/values.yaml \ +--set auth.adminUser=your-userid --set auth.adminPassword=your-password \ +oci://registry-1.docker.io/bitnamicharts/keycloak --render-subchart-notes ``` -```shell -# Standard SPIRE and Tornjak deployment with Authentication enabled -helm upgrade --install \ ---set global.spire.namespaces.system.create=true \ ---values tests/integration/psat/values.yaml \ ---values examples/tornjak/values.yaml \ ---values examples/tornjak/values-auth.yaml \ ---render-subchart-notes spire charts/spire -``` +> [!IMPORTANT] +> It is important to start the Tornjak service before starting Tornjak with authentication -To test the deployment, you can run the SPIRE test: +The example below demonstrates port forward for local access. In cloud deployment scenario, +enable Ingress to the Keycloak service accordingly. ```shell -# Test the Tornjak deployment -helm test spire +# Start an auth Service [Keycloak] in separate terminal +kubectl -n keycloak port-forward service/keycloak 8080:80 ``` -## Access Tornjak - -To access Tornjak use port-forwarding or check the ingress option below. - -Run following commands from your shell, if you run with different values your namespace might differ. Consult the install notes printed when running above `helm upgrade` command in that case. +See the helm Notes for more information about accessing Keycloak -Since `port-forward` is a blocking command, execute them in three different consoles (one for backend, one for frontend and one for auth that you already started in the previous step): - -```shell -# Start a backend Service (Terminal 1) -kubectl -n spire-server port-forward service/spire-tornjak-backend 10000:10000 -``` - -```shell -# Start a frontend Service (Terminal 2) -kubectl -n spire-server port-forward service/spire-tornjak-frontend 3000:3000 -``` - -* You can now access Tornjak at [localhost:3000](http://localhost:3000). - -* This will redirect to the auth service for authentication to [localhost:8080](http://localhost:8080) - -See [values.yaml](./values.yaml) for more details on the chart configurations to customize authentication config. - -## Deploy SPIRE with Tornjak User Management Enabled using Ingress +## Deploy SPIRE with Tornjak User Management Enabled -When deployment uses Ingress, the access to Tornjak application and Keycloak will be different from above. -Please follow the deployment and configuration instructions as described [here](../README.md) -and make sure to add the `--values examples/tornjak/values-auth.yaml` parameter that is referencing Tornjak Authentication values. +Please follow the instructions for [deploying Tornjak](../README.md) +with addition of the User Management values `--values examples/tornjak/values-auth.yaml`. -And update your `examples/production/example-your-values.yaml` most importantly, `trustDomain`, accordingly. +> [!IMPORTANT] +> Make sure Tornjak backend User Management issuer points to the correct Keycloak issuer URL. Which is in format +> `http://.:/realms/tornjak`. +> For the example above it will be: `http://keycloak.keycloak:8080/realms/tornjak` +> You can set the issuer URL using `--set spire-server.tornjak.config.userManagement.issuer=http://tornjak.tornjak:8080/realms/tornjak` +> +> [!IMPORTANT] +> If audience is set, make sure the Tornjak backend `audience` is set correctly. You can set it using: +> `--set spire-server.tornjak.config.userManagement.audience=your-audience` +> +> [!TIP] +> Keep in mind, when redeploying Tornjak, you might have to recreate port forwarding for that service. + +The sample [examples/tornjak/values-auth.yaml](../values-auth.yaml) assumes local +Keycloak deployment using port forwarding. When using Ingress, update the URLs accordingly. -E.g: +## Access Tornjak -```shell -helm upgrade --install \ ---set global.spire.namespaces.create=true \ ---set global.spire.ingressControllerType=ingress-nginx \ ---values tests/integration/psat/values.yaml \ ---values examples/tornjak/values.yaml \ ---values examples/tornjak/values-auth.yaml \ ---values examples/tornjak/values-ingress.yaml \ ---render-subchart-notes spire charts/spire -``` +Follow the standard [steps for Accessing Tornjak](../README.md) diff --git a/examples/tornjak/values.yaml b/examples/tornjak/values.yaml index 688c2b0e2..6ba001503 100644 --- a/examples/tornjak/values.yaml +++ b/examples/tornjak/values.yaml @@ -4,7 +4,6 @@ spire-server: tornjak-frontend: enabled: true - apiServerURL: "http://localhost:10000" service: type: ClusterIP port: 3000 From a453a2c1b46fee0ee3d81bc003b5f74ac6117761 Mon Sep 17 00:00:00 2001 From: "spire-helm-version-checker[bot]" <161522935+spire-helm-version-checker[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 13:11:02 +0200 Subject: [PATCH 23/37] Bump test chart dependencies (#355) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: marcofranssen <694733+marcofranssen@users.noreply.github.com> --- .github/tests/charts.json | 4 ++-- charts/spire/charts/spiffe-oidc-discovery-provider/README.md | 2 +- .../spire/charts/spiffe-oidc-discovery-provider/values.yaml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/tests/charts.json b/.github/tests/charts.json index c2abe96a8..1d51f2c40 100644 --- a/.github/tests/charts.json +++ b/.github/tests/charts.json @@ -2,7 +2,7 @@ { "name": "kube-prometheus-stack", "repo": "https://prometheus-community.github.io/helm-charts", - "version": "58.4.0" + "version": "58.5.0" }, { "name": "cert-manager", @@ -22,6 +22,6 @@ { "name": "postgresql", "repo": "https://charts.bitnami.com/bitnami", - "version": "15.2.8" + "version": "15.2.12" } ] diff --git a/charts/spire/charts/spiffe-oidc-discovery-provider/README.md b/charts/spire/charts/spiffe-oidc-discovery-provider/README.md index 06ea70037..26882e968 100644 --- a/charts/spire/charts/spiffe-oidc-discovery-provider/README.md +++ b/charts/spire/charts/spiffe-oidc-discovery-provider/README.md @@ -119,7 +119,7 @@ A Helm chart to install the SPIFFE OIDC discovery provider. | `tests.toolkit.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.toolkit.image.repository` | The repository within the registry | `chainguard/min-toolkit-debug` | | `tests.toolkit.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.toolkit.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:de4a5424d61065ccfc444176c6dc34578210500c9466b0f317f83404fd94d2b6` | +| `tests.toolkit.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:59306310795cd6db02cdc44d3ffb10fbc8855eec63514d1e75bb7d5d34e3e0f9` | | `tests.step.image.registry` | The OCI registry to pull the image from | `docker.io` | | `tests.step.image.repository` | The repository within the registry | `smallstep/step-cli` | | `tests.step.image.pullPolicy` | The image pull policy | `IfNotPresent` | diff --git a/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml b/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml index 630054407..e30d1bf5d 100644 --- a/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml +++ b/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml @@ -340,7 +340,7 @@ tests: registry: cgr.dev repository: chainguard/min-toolkit-debug pullPolicy: IfNotPresent - tag: latest@sha256:de4a5424d61065ccfc444176c6dc34578210500c9466b0f317f83404fd94d2b6 + tag: latest@sha256:59306310795cd6db02cdc44d3ffb10fbc8855eec63514d1e75bb7d5d34e3e0f9 step: ## @param tests.step.image.registry The OCI registry to pull the image from From 2c5dfa010f4ae0c50c2d5c5f5d5fd75c10e5a021 Mon Sep 17 00:00:00 2001 From: Mariusz Sabath Date: Tue, 14 May 2024 16:52:55 -0400 Subject: [PATCH 24/37] Improve Tornjak NOTES. Fixes #132 (#354) * Improve Tornjak NOTES. Fixes #132 Signed-off-by: Mariusz Sabath * Fix Tornjak ingress value Signed-off-by: Mariusz Sabath --------- Signed-off-by: Mariusz Sabath --- .../charts/spire-server/templates/NOTES.txt | 18 ++++++++++++++---- .../tornjak-frontend/templates/NOTES.txt | 10 +++++++++- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/charts/spire/charts/spire-server/templates/NOTES.txt b/charts/spire/charts/spire-server/templates/NOTES.txt index d882cada0..7c445a203 100644 --- a/charts/spire/charts/spire-server/templates/NOTES.txt +++ b/charts/spire/charts/spire-server/templates/NOTES.txt @@ -10,18 +10,27 @@ Get the currently registered SPIFFE entries from the server: Installed {{ include "spire-tornjak.fullname" . }}… {{- if eq .Values.tornjak.config.userManagement.issuer "" }} + ### WARNING ### Tornjak is configured to run without authentication and is therefore NOT suitable to run in production environments. Only use in test environments! {{- end }} -Access Tornjak: - kubectl -n {{ include "spire-server.namespace" . }} port-forward service/{{ include "spire-tornjak.servicename" . }} {{ .Values.tornjak.service.ports.http }}:10000 +Access Tornjak API (Backend): + +{{- if .Values.tornjak.ingress.enabled }} + + Connect to: {{ printf "https://tornjak-backend.%s" (include "spire-lib.trust-domain" .) }} + +{{- else }} + kubectl -n {{ include "spire-server.namespace" . }} port-forward service/{{ include "spire-tornjak.servicename" . }} {{ .Values.tornjak.service.ports.http }}:10000 {{- if eq (include "spire-tornjak.connectionType" .) "http" }} - Open browser to: http://localhost:{{ .Values.tornjak.service.ports.http }} + + Connect to: http://localhost:{{ .Values.tornjak.service.ports.http }} {{- else if eq (include "spire-tornjak.connectionType" .) "tls" }} - Open browser to: https://localhost:{{ .Values.tornjak.service.ports.https }} + + Connect to: https://localhost:{{ .Values.tornjak.service.ports.https }} *** NOTE: You might get a security warning if using self-signed certificate or use curl: @@ -37,3 +46,4 @@ Access Tornjak: ERROR! Incorrect value selected for "Values.tornjak.config.connectionType" {{- end }} {{- end }} +{{- end }} diff --git a/charts/spire/charts/tornjak-frontend/templates/NOTES.txt b/charts/spire/charts/tornjak-frontend/templates/NOTES.txt index 752ca751b..ff51e5535 100644 --- a/charts/spire/charts/tornjak-frontend/templates/NOTES.txt +++ b/charts/spire/charts/tornjak-frontend/templates/NOTES.txt @@ -15,19 +15,27 @@ Tornjak UI (Frontend) {{- if .Values.auth.enabled }} User Management API: "{{ .Values.auth.serverURL }}" {{- else }} + ### WARNING ### Tornjak is configured to run without authentication and is therefore NOT suitable to run in production environments. Only use in test environments! {{- end }} -Access Tornjak: +Access Tornjak UI: + +{{- if .Values.ingress.enabled }} + +Open browser to: +{{ printf "https://tornjak-frontend.%s" (include "spire-lib.trust-domain" .) }} +{{- else }} kubectl -n {{ include "tornjak-frontend.namespace" . }} port-forward service/{{ include "tornjak-frontend.fullname" . }} {{ .Values.service.port }}:3000 Ensure you have port-forwarding for tornjak-backend as well. Open browser to: http://localhost:{{ .Values.service.port }} +{{- end }} To learn more about the release, try: From c31a2e9f65f155913d614d64493d42d5cf058551 Mon Sep 17 00:00:00 2001 From: kfox1111 Date: Thu, 16 May 2024 09:20:14 -0700 Subject: [PATCH 25/37] Bump up spire to 1.9.6 (#356) Signed-off-by: Kevin Fox --- charts/spire/Chart.yaml | 2 +- charts/spire/README.md | 2 +- .../spiffe-oidc-discovery-provider/Chart.yaml | 2 +- charts/spire/charts/spire-agent/Chart.yaml | 2 +- charts/spire/charts/spire-agent/README.md | 212 +++++++++--------- .../spire-agent/templates/configmap.yaml | 2 + charts/spire/charts/spire-agent/values.yaml | 4 + charts/spire/charts/spire-server/Chart.yaml | 2 +- 8 files changed, 118 insertions(+), 110 deletions(-) diff --git a/charts/spire/Chart.yaml b/charts/spire/Chart.yaml index ec16fed2a..a2e052c44 100644 --- a/charts/spire/Chart.yaml +++ b/charts/spire/Chart.yaml @@ -4,7 +4,7 @@ description: > A Helm chart for deploying the complete Spire stack including: spire-server, spire-agent, spiffe-csi-driver, spiffe-oidc-discovery-provider and spire-controller-manager. type: application version: 0.20.0 -appVersion: "1.9.5" +appVersion: "1.9.6" keywords: ["spiffe", "spire", "spire-server", "spire-agent", "oidc", "spire-controller-manager"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire sources: diff --git a/charts/spire/README.md b/charts/spire/README.md index 1496fe81d..bd0a3006e 100644 --- a/charts/spire/README.md +++ b/charts/spire/README.md @@ -1,6 +1,6 @@ # spire -![Version: 0.20.0](https://img.shields.io/badge/Version-0.20.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.9.5](https://img.shields.io/badge/AppVersion-1.9.5-informational?style=flat-square) +![Version: 0.20.0](https://img.shields.io/badge/Version-0.20.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.9.6](https://img.shields.io/badge/AppVersion-1.9.6-informational?style=flat-square) [![Development Phase](https://github.com/spiffe/spiffe/blob/main/.img/maturity/dev.svg)](https://github.com/spiffe/spiffe/blob/main/MATURITY.md#development) A Helm chart for deploying the complete Spire stack including: spire-server, spire-agent, spiffe-csi-driver, spiffe-oidc-discovery-provider and spire-controller-manager. diff --git a/charts/spire/charts/spiffe-oidc-discovery-provider/Chart.yaml b/charts/spire/charts/spiffe-oidc-discovery-provider/Chart.yaml index cb678037f..0416195e1 100644 --- a/charts/spire/charts/spiffe-oidc-discovery-provider/Chart.yaml +++ b/charts/spire/charts/spiffe-oidc-discovery-provider/Chart.yaml @@ -3,7 +3,7 @@ name: spiffe-oidc-discovery-provider description: A Helm chart to install the SPIFFE OIDC discovery provider. type: application version: 0.1.0 -appVersion: "1.9.5" +appVersion: "1.9.6" keywords: ["spiffe", "oidc"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire sources: diff --git a/charts/spire/charts/spire-agent/Chart.yaml b/charts/spire/charts/spire-agent/Chart.yaml index 077fbd0b4..d087332d8 100644 --- a/charts/spire/charts/spire-agent/Chart.yaml +++ b/charts/spire/charts/spire-agent/Chart.yaml @@ -3,7 +3,7 @@ name: spire-agent description: A Helm chart to install the SPIRE agent. type: application version: 0.1.0 -appVersion: "1.9.5" +appVersion: "1.9.6" keywords: ["spiffe", "spire-agent"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire sources: diff --git a/charts/spire/charts/spire-agent/README.md b/charts/spire/charts/spire-agent/README.md index 813ea025b..cba0aea3a 100644 --- a/charts/spire/charts/spire-agent/README.md +++ b/charts/spire/charts/spire-agent/README.md @@ -25,108 +25,110 @@ A Helm chart to install the SPIRE agent. ### Chart parameters -| Name | Description | Value | -| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -| `image.registry` | The OCI registry to pull the image from | `ghcr.io` | -| `image.repository` | The repository within the registry | `spiffe/spire-agent` | -| `image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `image.tag` | Overrides the image tag whose default is the chart appVersion | `""` | -| `imagePullSecrets` | Pull secrets for images | `[]` | -| `nameOverride` | Name override | `""` | -| `namespaceOverride` | Namespace override | `""` | -| `fullnameOverride` | Fullname override | `""` | -| `serviceAccount.create` | Specifies whether a service account should be created | `true` | -| `serviceAccount.annotations` | Annotations to add to the service account | `{}` | -| `serviceAccount.name` | The name of the service account to use. | `""` | -| `configMap.annotations` | Annotations to add to the SPIRE Agent ConfigMap | `{}` | -| `podAnnotations` | Annotations to add to pods | `{}` | -| `podLabels` | Labels to add to pods | `{}` | -| `podSecurityContext` | Pod security context | `{}` | -| `securityContext` | Security context | `{}` | -| `resources` | Resource requests and limits | `{}` | -| `nodeSelector` | Node selector | `{}` | -| `tolerations` | List of tolerations | `[]` | -| `affinity` | Node affinity | `{}` | -| `authorizedDelegates` | A list of the authorized delegates SPIFFE IDs. See Delegated Identity API for more information. | `[]` | -| `logLevel` | The log level, valid values are "debug", "info", "warn", and "error" | `info` | -| `clusterName` | The name of the Kubernetes cluster (`kubeadm init --service-dns-domain`) | `example-cluster` | -| `trustDomain` | The trust domain to be used for the SPIFFE identifiers | `example.org` | -| `trustBundleURL` | If set, obtain trust bundle from url instead of Kubernetes ConfigMap | `""` | -| `trustBundleFormat` | If using trustBundleURL, what format is the url. Choices are "pem" and "spiffe" | `pem` | -| `bundleConfigMap` | Configmap name for Spire bundle | `spire-bundle` | -| `availabilityTarget` | The minimum amount of time desired to gracefully handle SPIRE Server or Agent downtime. This configurable influences how aggressively X509 SVIDs should be rotated. If set, must be at least 24h. | `""` | -| `disableReattestToRenew` | Deprecated: Allow agent to renew certificate when it expires rather than reattest | `false` | -| `server.address` | Address for Spire server | `""` | -| `server.port` | Port number for Spire server | `443` | -| `server.namespaceOverride` | Override the namespace for Spire server | `""` | -| `server.nameOverride` | Override the name for Spire server. Should only be changed when building your own nested chart to ensure names align. | `""` | -| `healthChecks.port` | override the host port used for health checking | `9982` | -| `updateStrategy.type` | The update strategy to use to replace existing DaemonSet pods with new pods. Can be RollingUpdate or OnDelete. | `RollingUpdate` | -| `updateStrategy.rollingUpdate.maxUnavailable` | Max unavailable pods during update. Can be a number or a percentage. | `1` | -| `livenessProbe.initialDelaySeconds` | Initial delay seconds for probe | `15` | -| `livenessProbe.periodSeconds` | Period seconds for probe | `60` | -| `readinessProbe.initialDelaySeconds` | Initial delay seconds for probe | `10` | -| `readinessProbe.periodSeconds` | Period seconds for probe | `30` | -| `fsGroupFix.image.registry` | The OCI registry to pull the image from | `cgr.dev` | -| `fsGroupFix.image.repository` | The repository within the registry | `chainguard/bash` | -| `fsGroupFix.image.pullPolicy` | The image pull policy | `Always` | -| `fsGroupFix.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda` | -| `fsGroupFix.resources` | Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | `{}` | -| `keyManager.memory.enabled` | Enable the memory based Key Manager | `true` | -| `nodeAttestor.k8sPsat.enabled` | Enable Psat k8s Node Attestor | `true` | -| `nodeAttestor.tpmDirect.enabled` | Enable the direct TPM node attestor, a 3rd party plugin by Boxboat. This plugin is experimental. | `false` | -| `nodeAttestor.tpmDirect.plugin.image.registry` | The OCI registry to pull the image from | `docker.io` | -| `nodeAttestor.tpmDirect.plugin.image.repository` | The repository within the registry | `boxboat/spire-tpm-plugin-tpm-attestor-agent` | -| `nodeAttestor.tpmDirect.plugin.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `nodeAttestor.tpmDirect.plugin.image.tag` | Overrides the image tag | `v1.8.7` | -| `nodeAttestor.tpmDirect.plugin.checksum` | The sha256 checksum of the plugin binary | `1d7c73ccac948ee86cbd78ddde2d30128a1838b403f7bb2100d38d916a252244` | -| `nodeAttestor.tpmDirect.plugin.path` | The filename in the container of the plugin | `/app/tpm_attestor_agent` | -| `nodeAttestor.tpmDirect.pubHash.enabled` | Enable Psat k8s nodeattestor | `true` | -| `nodeAttestor.tpmDirect.pubHash.image.registry` | The OCI registry to pull the image from | `docker.io` | -| `nodeAttestor.tpmDirect.pubHash.image.repository` | The repository within the registry | `boxboat/spire-tpm-plugin-get-tpm-pubhash` | -| `nodeAttestor.tpmDirect.pubHash.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `nodeAttestor.tpmDirect.pubHash.image.tag` | Overrides the image tag | `v1.8.7` | -| `workloadAttestors.unix.enabled` | Enables the Unix workload attestor | `false` | -| `workloadAttestors.k8s.enabled` | Enables the Kubernetes workload attestor | `true` | -| `workloadAttestors.k8s.skipKubeletVerification` | If true, kubelet certificate verification is skipped | `true` | -| `workloadAttestors.k8s.disableContainerSelectors` | Set to true if using holdApplicationUntilProxyStarts in Istio | `false` | -| `sds.enabled` | Enables Envoy SDS configuration | `false` | -| `sds.defaultSvidName` | The TLS Certificate resource name to use for the default X509-SVID with Envoy SDS | `default` | -| `sds.defaultBundleName` | The Validation Context resource name to use for the default X.509 bundle with Envoy SDS | `ROOTCA` | -| `sds.defaultAllBundlesName` | The Validation Context resource name to use for all bundles (including federated) with Envoy SDS | `ALL` | -| `sds.disableSpiffeCertValidation` | Disable Envoy SDS custom validation | `false` | -| `telemetry.prometheus.enabled` | Flag to enable prometheus monitoring | `false` | -| `telemetry.prometheus.port` | Port for prometheus metrics | `9988` | -| `telemetry.prometheus.podMonitor.enabled` | Enable podMonitor for prometheus | `false` | -| `telemetry.prometheus.podMonitor.namespace` | Override where to install the podMonitor, if not set will use the same namespace as the spire-agent | `""` | -| `telemetry.prometheus.podMonitor.labels` | Pod labels to filter for prometheus monitoring | `{}` | -| `kubeletConnectByHostname` | If true, connect to kubelet using the nodes hostname. If false, uses localhost. If unset, defaults to true on OpenShift and false otherwise. | `""` | -| `socketPath` | The unix socket path to the spire-agent | `/run/spire/agent-sockets/spire-agent.sock` | -| `socketAlternate.names` | List of alternate names for the socket that workloads might expect to be able to access in the driver mount. | `["socket","spire-agent.sock","api.sock"]` | -| `socketAlternate.image.registry` | The OCI registry to pull the image from | `cgr.dev` | -| `socketAlternate.image.repository` | The repository within the registry | `chainguard/bash` | -| `socketAlternate.image.pullPolicy` | The image pull policy | `Always` | -| `socketAlternate.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda` | -| `socketAlternate.resources` | Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | `{}` | -| `priorityClassName` | Priority class assigned to daemonset pods. Can be auto set with global.recommendations.priorityClassName. | `""` | -| `extraEnvVars` | Extra environment variables to be added to the Spire Agent container | `[]` | -| `extraVolumes` | Extra volumes to be mounted on Spire Agent pods | `[]` | -| `extraVolumeMounts` | Extra volume mounts for Spire Agent pods | `[]` | -| `extraContainers` | Additional containers to create with Spire Agent pods | `[]` | -| `initContainers` | Additional init containers to create with Spire Agent pods | `[]` | -| `hostAliases` | Customize /etc/hosts file as described here https://kubernetes.io/docs/tasks/network/customize-hosts-file-for-pods/ | `[]` | -| `customPlugins.keyManager` | Custom plugins of type KeyManager are configured here | `{}` | -| `customPlugins.nodeAttestor` | Custom plugins of type NodeAttestor are configured here | `{}` | -| `customPlugins.svidStore` | Custom plugins of type SVIDStore are configured here | `{}` | -| `customPlugins.workloadAttestor` | Custom plugins of type WorkloadAttestor are configured here | `{}` | -| `experimental.enabled` | Allow configuration of experimental features | `false` | -| `experimental.syncInterval` | Sync interval with SPIRE server with exponential backoff | `5s` | -| `experimental.featureFlags` | List of developer feature flags | `[]` | -| `agents` | Configure multiple agent DaemonSets. Useful when you have different node types and nodeAttestors | `{}` | -| `tools.kubectl.image.registry` | The OCI registry to pull the image from | `docker.io` | -| `tools.kubectl.image.repository` | The repository within the registry | `rancher/kubectl` | -| `tools.kubectl.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tools.kubectl.image.tag` | Overrides the image tag whose default is the chart appVersion | `""` | -| `sockets.hostBasePath` | Path on which the agent socket is made available when admin.mountOnHost is true | `/run/spire/agent/sockets` | -| `sockets.admin.enabled` | Enable the admin socket. Useful for admin tasks or the Delegated Identity API. | `false` | -| `sockets.admin.mountOnHost` | Enable the admin socket to be visible on the host. | `false` | +| Name | Description | Value | +| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | +| `image.registry` | The OCI registry to pull the image from | `ghcr.io` | +| `image.repository` | The repository within the registry | `spiffe/spire-agent` | +| `image.pullPolicy` | The image pull policy | `IfNotPresent` | +| `image.tag` | Overrides the image tag whose default is the chart appVersion | `""` | +| `imagePullSecrets` | Pull secrets for images | `[]` | +| `nameOverride` | Name override | `""` | +| `namespaceOverride` | Namespace override | `""` | +| `fullnameOverride` | Fullname override | `""` | +| `serviceAccount.create` | Specifies whether a service account should be created | `true` | +| `serviceAccount.annotations` | Annotations to add to the service account | `{}` | +| `serviceAccount.name` | The name of the service account to use. | `""` | +| `configMap.annotations` | Annotations to add to the SPIRE Agent ConfigMap | `{}` | +| `podAnnotations` | Annotations to add to pods | `{}` | +| `podLabels` | Labels to add to pods | `{}` | +| `podSecurityContext` | Pod security context | `{}` | +| `securityContext` | Security context | `{}` | +| `resources` | Resource requests and limits | `{}` | +| `nodeSelector` | Node selector | `{}` | +| `tolerations` | List of tolerations | `[]` | +| `affinity` | Node affinity | `{}` | +| `authorizedDelegates` | A list of the authorized delegates SPIFFE IDs. See Delegated Identity API for more information. | `[]` | +| `logLevel` | The log level, valid values are "debug", "info", "warn", and "error" | `info` | +| `clusterName` | The name of the Kubernetes cluster (`kubeadm init --service-dns-domain`) | `example-cluster` | +| `trustDomain` | The trust domain to be used for the SPIFFE identifiers | `example.org` | +| `trustBundleURL` | If set, obtain trust bundle from url instead of Kubernetes ConfigMap | `""` | +| `trustBundleFormat` | If using trustBundleURL, what format is the url. Choices are "pem" and "spiffe" | `pem` | +| `bundleConfigMap` | Configmap name for Spire bundle | `spire-bundle` | +| `availabilityTarget` | The minimum amount of time desired to gracefully handle SPIRE Server or Agent downtime. This configurable influences how aggressively X509 SVIDs should be rotated. If set, must be at least 24h. | `""` | +| `disableReattestToRenew` | Deprecated: Allow agent to renew certificate when it expires rather than reattest | `false` | +| `server.address` | Address for Spire server | `""` | +| `server.port` | Port number for Spire server | `443` | +| `server.namespaceOverride` | Override the namespace for Spire server | `""` | +| `server.nameOverride` | Override the name for Spire server. Should only be changed when building your own nested chart to ensure names align. | `""` | +| `healthChecks.port` | override the host port used for health checking | `9982` | +| `updateStrategy.type` | The update strategy to use to replace existing DaemonSet pods with new pods. Can be RollingUpdate or OnDelete. | `RollingUpdate` | +| `updateStrategy.rollingUpdate.maxUnavailable` | Max unavailable pods during update. Can be a number or a percentage. | `1` | +| `livenessProbe.initialDelaySeconds` | Initial delay seconds for probe | `15` | +| `livenessProbe.periodSeconds` | Period seconds for probe | `60` | +| `readinessProbe.initialDelaySeconds` | Initial delay seconds for probe | `10` | +| `readinessProbe.periodSeconds` | Period seconds for probe | `30` | +| `fsGroupFix.image.registry` | The OCI registry to pull the image from | `cgr.dev` | +| `fsGroupFix.image.repository` | The repository within the registry | `chainguard/bash` | +| `fsGroupFix.image.pullPolicy` | The image pull policy | `Always` | +| `fsGroupFix.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda` | +| `fsGroupFix.resources` | Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | `{}` | +| `keyManager.memory.enabled` | Enable the memory based Key Manager | `true` | +| `nodeAttestor.k8sPsat.enabled` | Enable Psat k8s Node Attestor | `true` | +| `nodeAttestor.tpmDirect.enabled` | Enable the direct TPM node attestor, a 3rd party plugin by Boxboat. This plugin is experimental. | `false` | +| `nodeAttestor.tpmDirect.plugin.image.registry` | The OCI registry to pull the image from | `docker.io` | +| `nodeAttestor.tpmDirect.plugin.image.repository` | The repository within the registry | `boxboat/spire-tpm-plugin-tpm-attestor-agent` | +| `nodeAttestor.tpmDirect.plugin.image.pullPolicy` | The image pull policy | `IfNotPresent` | +| `nodeAttestor.tpmDirect.plugin.image.tag` | Overrides the image tag | `v1.8.7` | +| `nodeAttestor.tpmDirect.plugin.checksum` | The sha256 checksum of the plugin binary | `1d7c73ccac948ee86cbd78ddde2d30128a1838b403f7bb2100d38d916a252244` | +| `nodeAttestor.tpmDirect.plugin.path` | The filename in the container of the plugin | `/app/tpm_attestor_agent` | +| `nodeAttestor.tpmDirect.pubHash.enabled` | Enable Psat k8s nodeattestor | `true` | +| `nodeAttestor.tpmDirect.pubHash.image.registry` | The OCI registry to pull the image from | `docker.io` | +| `nodeAttestor.tpmDirect.pubHash.image.repository` | The repository within the registry | `boxboat/spire-tpm-plugin-get-tpm-pubhash` | +| `nodeAttestor.tpmDirect.pubHash.image.pullPolicy` | The image pull policy | `IfNotPresent` | +| `nodeAttestor.tpmDirect.pubHash.image.tag` | Overrides the image tag | `v1.8.7` | +| `workloadAttestors.unix.enabled` | Enables the Unix workload attestor | `false` | +| `workloadAttestors.k8s.enabled` | Enables the Kubernetes workload attestor | `true` | +| `workloadAttestors.k8s.skipKubeletVerification` | If true, kubelet certificate verification is skipped | `true` | +| `workloadAttestors.k8s.disableContainerSelectors` | Set to true if using holdApplicationUntilProxyStarts in Istio | `false` | +| `workloadAttestors.k8s.useNewContainerLocator` | If true, enables the new container locator algorithm that has support for cgroups v2. Defaults to false | `false` | +| `workloadAttestors.k8s.verboseContainerLocatorLogs` | If true, enables verbose logging of mountinfo and cgroup information used to locate containers. Defaults to false | `false` | +| `sds.enabled` | Enables Envoy SDS configuration | `false` | +| `sds.defaultSvidName` | The TLS Certificate resource name to use for the default X509-SVID with Envoy SDS | `default` | +| `sds.defaultBundleName` | The Validation Context resource name to use for the default X.509 bundle with Envoy SDS | `ROOTCA` | +| `sds.defaultAllBundlesName` | The Validation Context resource name to use for all bundles (including federated) with Envoy SDS | `ALL` | +| `sds.disableSpiffeCertValidation` | Disable Envoy SDS custom validation | `false` | +| `telemetry.prometheus.enabled` | Flag to enable prometheus monitoring | `false` | +| `telemetry.prometheus.port` | Port for prometheus metrics | `9988` | +| `telemetry.prometheus.podMonitor.enabled` | Enable podMonitor for prometheus | `false` | +| `telemetry.prometheus.podMonitor.namespace` | Override where to install the podMonitor, if not set will use the same namespace as the spire-agent | `""` | +| `telemetry.prometheus.podMonitor.labels` | Pod labels to filter for prometheus monitoring | `{}` | +| `kubeletConnectByHostname` | If true, connect to kubelet using the nodes hostname. If false, uses localhost. If unset, defaults to true on OpenShift and false otherwise. | `""` | +| `socketPath` | The unix socket path to the spire-agent | `/run/spire/agent-sockets/spire-agent.sock` | +| `socketAlternate.names` | List of alternate names for the socket that workloads might expect to be able to access in the driver mount. | `["socket","spire-agent.sock","api.sock"]` | +| `socketAlternate.image.registry` | The OCI registry to pull the image from | `cgr.dev` | +| `socketAlternate.image.repository` | The repository within the registry | `chainguard/bash` | +| `socketAlternate.image.pullPolicy` | The image pull policy | `Always` | +| `socketAlternate.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda` | +| `socketAlternate.resources` | Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | `{}` | +| `priorityClassName` | Priority class assigned to daemonset pods. Can be auto set with global.recommendations.priorityClassName. | `""` | +| `extraEnvVars` | Extra environment variables to be added to the Spire Agent container | `[]` | +| `extraVolumes` | Extra volumes to be mounted on Spire Agent pods | `[]` | +| `extraVolumeMounts` | Extra volume mounts for Spire Agent pods | `[]` | +| `extraContainers` | Additional containers to create with Spire Agent pods | `[]` | +| `initContainers` | Additional init containers to create with Spire Agent pods | `[]` | +| `hostAliases` | Customize /etc/hosts file as described here https://kubernetes.io/docs/tasks/network/customize-hosts-file-for-pods/ | `[]` | +| `customPlugins.keyManager` | Custom plugins of type KeyManager are configured here | `{}` | +| `customPlugins.nodeAttestor` | Custom plugins of type NodeAttestor are configured here | `{}` | +| `customPlugins.svidStore` | Custom plugins of type SVIDStore are configured here | `{}` | +| `customPlugins.workloadAttestor` | Custom plugins of type WorkloadAttestor are configured here | `{}` | +| `experimental.enabled` | Allow configuration of experimental features | `false` | +| `experimental.syncInterval` | Sync interval with SPIRE server with exponential backoff | `5s` | +| `experimental.featureFlags` | List of developer feature flags | `[]` | +| `agents` | Configure multiple agent DaemonSets. Useful when you have different node types and nodeAttestors | `{}` | +| `tools.kubectl.image.registry` | The OCI registry to pull the image from | `docker.io` | +| `tools.kubectl.image.repository` | The repository within the registry | `rancher/kubectl` | +| `tools.kubectl.image.pullPolicy` | The image pull policy | `IfNotPresent` | +| `tools.kubectl.image.tag` | Overrides the image tag whose default is the chart appVersion | `""` | +| `sockets.hostBasePath` | Path on which the agent socket is made available when admin.mountOnHost is true | `/run/spire/agent/sockets` | +| `sockets.admin.enabled` | Enable the admin socket. Useful for admin tasks or the Delegated Identity API. | `false` | +| `sockets.admin.mountOnHost` | Enable the admin socket to be visible on the host. | `false` | diff --git a/charts/spire/charts/spire-agent/templates/configmap.yaml b/charts/spire/charts/spire-agent/templates/configmap.yaml index 215bee637..6da906918 100644 --- a/charts/spire/charts/spire-agent/templates/configmap.yaml +++ b/charts/spire/charts/spire-agent/templates/configmap.yaml @@ -114,6 +114,8 @@ plugins: # can authenticate the kubelet cert, so skip validation. skip_kubelet_verification: {{ .Values.workloadAttestors.k8s.skipKubeletVerification }} disable_container_selectors: {{ .Values.workloadAttestors.k8s.disableContainerSelectors }} + use_new_container_locator: {{ .Values.workloadAttestors.k8s.useNewContainerLocator }} + verbose_container_locator_logs: {{ .Values.workloadAttestors.k8s.verboseContainerLocatorLogs }} {{- end }} {{- if .Values.workloadAttestors.unix.enabled }} diff --git a/charts/spire/charts/spire-agent/values.yaml b/charts/spire/charts/spire-agent/values.yaml index a9426a350..e38abd45a 100644 --- a/charts/spire/charts/spire-agent/values.yaml +++ b/charts/spire/charts/spire-agent/values.yaml @@ -213,6 +213,10 @@ workloadAttestors: skipKubeletVerification: true ## @param workloadAttestors.k8s.disableContainerSelectors Set to true if using holdApplicationUntilProxyStarts in Istio disableContainerSelectors: false + ## @param workloadAttestors.k8s.useNewContainerLocator If true, enables the new container locator algorithm that has support for cgroups v2. Defaults to false + useNewContainerLocator: false + ## @param workloadAttestors.k8s.verboseContainerLocatorLogs If true, enables verbose logging of mountinfo and cgroup information used to locate containers. Defaults to false + verboseContainerLocatorLogs: false sds: ## @param sds.enabled Enables Envoy SDS configuration diff --git a/charts/spire/charts/spire-server/Chart.yaml b/charts/spire/charts/spire-server/Chart.yaml index 65d3e6dec..62c510fbe 100644 --- a/charts/spire/charts/spire-server/Chart.yaml +++ b/charts/spire/charts/spire-server/Chart.yaml @@ -3,7 +3,7 @@ name: spire-server description: A Helm chart to install the SPIRE server. type: application version: 0.1.0 -appVersion: "1.9.5" +appVersion: "1.9.6" keywords: ["spiffe", "spire-server", "spire-controller-manager"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire sources: From 39453a1365a1a90a5089afafdb8c3e7656e23cb2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 May 2024 16:39:17 +0000 Subject: [PATCH 26/37] Bump helm.sh/helm/v3 from 3.14.4 to 3.15.0 in /tests (#357) Bumps [helm.sh/helm/v3](https://github.com/helm/helm) from 3.14.4 to 3.15.0. - [Release notes](https://github.com/helm/helm/releases) - [Commits](https://github.com/helm/helm/compare/v3.14.4...v3.15.0) --- updated-dependencies: - dependency-name: helm.sh/helm/v3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/go.mod | 21 ++++++++++----------- tests/go.sum | 41 ++++++++++++++++++----------------------- 2 files changed, 28 insertions(+), 34 deletions(-) diff --git a/tests/go.mod b/tests/go.mod index 85633f5f3..3aac27959 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -1,13 +1,12 @@ module github.com/spiffe/helm-charts/tests go 1.21 - -toolchain go1.21.5 +toolchain go1.22.2 require ( github.com/onsi/ginkgo/v2 v2.17.3 github.com/onsi/gomega v1.33.1 - helm.sh/helm/v3 v3.14.4 + helm.sh/helm/v3 v3.15.0 ) require ( @@ -25,7 +24,7 @@ require ( github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect @@ -60,14 +59,14 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.29.0 // indirect - k8s.io/apiextensions-apiserver v0.29.0 // indirect - k8s.io/apimachinery v0.29.0 // indirect - k8s.io/client-go v0.29.0 // indirect - k8s.io/klog/v2 v2.110.1 // indirect - k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect + k8s.io/api v0.30.0 // indirect + k8s.io/apiextensions-apiserver v0.30.0 // indirect + k8s.io/apimachinery v0.30.0 // indirect + k8s.io/client-go v0.30.0 // indirect + k8s.io/klog/v2 v2.120.1 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/tests/go.sum b/tests/go.sum index 51169c7e8..aa29cb137 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -19,7 +19,6 @@ github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= -github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= @@ -35,12 +34,10 @@ github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJA github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -188,8 +185,6 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -206,25 +201,25 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -helm.sh/helm/v3 v3.14.4 h1:6FSpEfqyDalHq3kUr4gOMThhgY55kXUEjdQoyODYnrM= -helm.sh/helm/v3 v3.14.4/go.mod h1:Tje7LL4gprZpuBNTbG34d1Xn5NmRT3OWfBRwpOSer9I= -k8s.io/api v0.29.0 h1:NiCdQMY1QOp1H8lfRyeEf8eOwV6+0xA6XEE44ohDX2A= -k8s.io/api v0.29.0/go.mod h1:sdVmXoz2Bo/cb77Pxi71IPTSErEW32xa4aXwKH7gfBA= -k8s.io/apiextensions-apiserver v0.29.0 h1:0VuspFG7Hj+SxyF/Z/2T0uFbI5gb5LRgEyUVE3Q4lV0= -k8s.io/apiextensions-apiserver v0.29.0/go.mod h1:TKmpy3bTS0mr9pylH0nOt/QzQRrW7/h7yLdRForMZwc= -k8s.io/apimachinery v0.29.0 h1:+ACVktwyicPz0oc6MTMLwa2Pw3ouLAfAon1wPLtG48o= -k8s.io/apimachinery v0.29.0/go.mod h1:eVBxQ/cwiJxH58eK/jd/vAk4mrxmVlnpBH5J2GbMeis= -k8s.io/client-go v0.29.0 h1:KmlDtFcrdUzOYrBhXHgKw5ycWzc3ryPX5mQe0SkG3y8= -k8s.io/client-go v0.29.0/go.mod h1:yLkXH4HKMAywcrD82KMSmfYg2DlE8mepPR4JGSo5n38= -k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= -k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= -k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= -k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +helm.sh/helm/v3 v3.15.0 h1:gcLxHeFp0Hfo7lYi6KIZ84ZyvlAnfFRSJ8lTL3zvG5U= +helm.sh/helm/v3 v3.15.0/go.mod h1:fvfoRcB8UKRUV5jrIfOTaN/pG1TPhuqSb56fjYdTKXg= +k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA= +k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE= +k8s.io/apiextensions-apiserver v0.30.0 h1:jcZFKMqnICJfRxTgnC4E+Hpcq8UEhT8B2lhBcQ+6uAs= +k8s.io/apiextensions-apiserver v0.30.0/go.mod h1:N9ogQFGcrbWqAY9p2mUAL5mGxsLqwgtUce127VtRX5Y= +k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA= +k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= +k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ= +k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY= +k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= +k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= From bfcf4183018a3e33887faa843fc6da16031e1033 Mon Sep 17 00:00:00 2001 From: "spire-helm-version-checker[bot]" <161522935+spire-helm-version-checker[bot]@users.noreply.github.com> Date: Mon, 20 May 2024 06:33:31 -0700 Subject: [PATCH 27/37] Bump test chart dependencies (#359) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: marcofranssen <694733+marcofranssen@users.noreply.github.com> --- .github/tests/charts.json | 6 +++--- .../spire/charts/spiffe-oidc-discovery-provider/README.md | 4 ++-- .../spire/charts/spiffe-oidc-discovery-provider/values.yaml | 4 ++-- charts/spire/charts/spire-agent/README.md | 4 ++-- charts/spire/charts/spire-agent/values.yaml | 4 ++-- charts/spire/charts/spire-server/README.md | 2 +- charts/spire/charts/spire-server/values.yaml | 2 +- charts/spire/charts/tornjak-frontend/README.md | 2 +- charts/spire/charts/tornjak-frontend/values.yaml | 2 +- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/tests/charts.json b/.github/tests/charts.json index 1d51f2c40..03c3018c2 100644 --- a/.github/tests/charts.json +++ b/.github/tests/charts.json @@ -2,7 +2,7 @@ { "name": "kube-prometheus-stack", "repo": "https://prometheus-community.github.io/helm-charts", - "version": "58.5.0" + "version": "58.6.0" }, { "name": "cert-manager", @@ -17,11 +17,11 @@ { "name": "mysql", "repo": "https://charts.bitnami.com/bitnami", - "version": "10.2.2" + "version": "10.2.4" }, { "name": "postgresql", "repo": "https://charts.bitnami.com/bitnami", - "version": "15.2.12" + "version": "15.3.3" } ] diff --git a/charts/spire/charts/spiffe-oidc-discovery-provider/README.md b/charts/spire/charts/spiffe-oidc-discovery-provider/README.md index 26882e968..26c82dae4 100644 --- a/charts/spire/charts/spiffe-oidc-discovery-provider/README.md +++ b/charts/spire/charts/spiffe-oidc-discovery-provider/README.md @@ -115,11 +115,11 @@ A Helm chart to install the SPIFFE OIDC discovery provider. | `tests.bash.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.bash.image.repository` | The repository within the registry | `chainguard/bash` | | `tests.bash.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda` | +| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a996ae2a4dc5601e29821e8f1bfc3ea886f3e544204042b0ed103be697a2b0eb` | | `tests.toolkit.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.toolkit.image.repository` | The repository within the registry | `chainguard/min-toolkit-debug` | | `tests.toolkit.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.toolkit.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:59306310795cd6db02cdc44d3ffb10fbc8855eec63514d1e75bb7d5d34e3e0f9` | +| `tests.toolkit.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:c1f66766ac8d0a5bd1f8d1ed741e3c6584019fa9537697a97ac003d8d604493f` | | `tests.step.image.registry` | The OCI registry to pull the image from | `docker.io` | | `tests.step.image.repository` | The repository within the registry | `smallstep/step-cli` | | `tests.step.image.pullPolicy` | The image pull policy | `IfNotPresent` | diff --git a/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml b/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml index e30d1bf5d..6523bb5d4 100644 --- a/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml +++ b/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml @@ -328,7 +328,7 @@ tests: registry: cgr.dev repository: chainguard/bash pullPolicy: IfNotPresent - tag: latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda + tag: latest@sha256:a996ae2a4dc5601e29821e8f1bfc3ea886f3e544204042b0ed103be697a2b0eb toolkit: ## @param tests.toolkit.image.registry The OCI registry to pull the image from @@ -340,7 +340,7 @@ tests: registry: cgr.dev repository: chainguard/min-toolkit-debug pullPolicy: IfNotPresent - tag: latest@sha256:59306310795cd6db02cdc44d3ffb10fbc8855eec63514d1e75bb7d5d34e3e0f9 + tag: latest@sha256:c1f66766ac8d0a5bd1f8d1ed741e3c6584019fa9537697a97ac003d8d604493f step: ## @param tests.step.image.registry The OCI registry to pull the image from diff --git a/charts/spire/charts/spire-agent/README.md b/charts/spire/charts/spire-agent/README.md index cba0aea3a..5b84acc09 100644 --- a/charts/spire/charts/spire-agent/README.md +++ b/charts/spire/charts/spire-agent/README.md @@ -70,7 +70,7 @@ A Helm chart to install the SPIRE agent. | `fsGroupFix.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `fsGroupFix.image.repository` | The repository within the registry | `chainguard/bash` | | `fsGroupFix.image.pullPolicy` | The image pull policy | `Always` | -| `fsGroupFix.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda` | +| `fsGroupFix.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a996ae2a4dc5601e29821e8f1bfc3ea886f3e544204042b0ed103be697a2b0eb` | | `fsGroupFix.resources` | Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | `{}` | | `keyManager.memory.enabled` | Enable the memory based Key Manager | `true` | | `nodeAttestor.k8sPsat.enabled` | Enable Psat k8s Node Attestor | `true` | @@ -108,7 +108,7 @@ A Helm chart to install the SPIRE agent. | `socketAlternate.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `socketAlternate.image.repository` | The repository within the registry | `chainguard/bash` | | `socketAlternate.image.pullPolicy` | The image pull policy | `Always` | -| `socketAlternate.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda` | +| `socketAlternate.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a996ae2a4dc5601e29821e8f1bfc3ea886f3e544204042b0ed103be697a2b0eb` | | `socketAlternate.resources` | Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | `{}` | | `priorityClassName` | Priority class assigned to daemonset pods. Can be auto set with global.recommendations.priorityClassName. | `""` | | `extraEnvVars` | Extra environment variables to be added to the Spire Agent container | `[]` | diff --git a/charts/spire/charts/spire-agent/values.yaml b/charts/spire/charts/spire-agent/values.yaml index e38abd45a..9d25c7edf 100644 --- a/charts/spire/charts/spire-agent/values.yaml +++ b/charts/spire/charts/spire-agent/values.yaml @@ -154,7 +154,7 @@ fsGroupFix: registry: cgr.dev repository: chainguard/bash pullPolicy: Always - tag: latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda + tag: latest@sha256:a996ae2a4dc5601e29821e8f1bfc3ea886f3e544204042b0ed103be697a2b0eb ## @param fsGroupFix.resources Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ resources: {} @@ -266,7 +266,7 @@ socketAlternate: registry: cgr.dev repository: chainguard/bash pullPolicy: Always - tag: latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda + tag: latest@sha256:a996ae2a4dc5601e29821e8f1bfc3ea886f3e544204042b0ed103be697a2b0eb ## @param socketAlternate.resources Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ resources: {} diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index a551813c1..cecdcd68d 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -404,5 +404,5 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `tests.bash.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.bash.image.repository` | The repository within the registry | `chainguard/bash` | | `tests.bash.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda` | +| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a996ae2a4dc5601e29821e8f1bfc3ea886f3e544204042b0ed103be697a2b0eb` | | `kubeConfigs` | Manage additional kubeconfig files to talk to external Kubernetes clusters | `{}` | diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index 5588a20d6..4f0d0e399 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -1013,7 +1013,7 @@ tests: registry: cgr.dev repository: chainguard/bash pullPolicy: IfNotPresent - tag: latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda + tag: latest@sha256:a996ae2a4dc5601e29821e8f1bfc3ea886f3e544204042b0ed103be697a2b0eb ## @param kubeConfigs [object] Manage additional kubeconfig files to talk to external Kubernetes clusters kubeConfigs: {} diff --git a/charts/spire/charts/tornjak-frontend/README.md b/charts/spire/charts/tornjak-frontend/README.md index 01c41da0d..7f2e6db3c 100644 --- a/charts/spire/charts/tornjak-frontend/README.md +++ b/charts/spire/charts/tornjak-frontend/README.md @@ -101,4 +101,4 @@ port forwarding. See the chart NOTES output for more details. | `tests.bash.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.bash.image.repository` | The repository within the registry | `chainguard/bash` | | `tests.bash.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda` | +| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a996ae2a4dc5601e29821e8f1bfc3ea886f3e544204042b0ed103be697a2b0eb` | diff --git a/charts/spire/charts/tornjak-frontend/values.yaml b/charts/spire/charts/tornjak-frontend/values.yaml index 11260fef4..25a8b0807 100644 --- a/charts/spire/charts/tornjak-frontend/values.yaml +++ b/charts/spire/charts/tornjak-frontend/values.yaml @@ -162,4 +162,4 @@ tests: registry: cgr.dev repository: chainguard/bash pullPolicy: IfNotPresent - tag: latest@sha256:0aaead0a1bfc92e8d5888b7cb7b77be321fb2aed52526168b107ce26cf0ecfda + tag: latest@sha256:a996ae2a4dc5601e29821e8f1bfc3ea886f3e544204042b0ed103be697a2b0eb From a2689c986ca4192be7c0cb111c5b40de68d97890 Mon Sep 17 00:00:00 2001 From: kfox1111 Date: Mon, 20 May 2024 08:58:22 -0700 Subject: [PATCH 28/37] Add spire-nested chart (#294) * Complete Server K8S PSAT support Add all the SPIRE Server supported options for the K8S PSAT attestor. This retains the ease of use for configuring local cluster support while adding the ability to configure multiple/external clusters as well. Kubeconfig support is added in its own config block as it will be used/shared with spire-controller-manager support in the future. Signed-off-by: Kevin Fox * Fix merge conflict Signed-off-by: Kevin Fox * Add support for integration tests in the tests/integration dir Signed-off-by: Kevin Fox * Fix split issue and typo Signed-off-by: Kevin Fox * Add basic psat test Signed-off-by: Kevin Fox * Fix linter Signed-off-by: Kevin Fox * Fix up test Signed-off-by: Kevin Fox * Add missing file Signed-off-by: Kevin Fox * Better encode config Signed-off-by: Kevin Fox * Update charts/spire/charts/spire-server/values.yaml Co-authored-by: Faisal Memon Signed-off-by: kfox1111 * Update docs Signed-off-by: Kevin Fox * Apply suggestions from code review Co-authored-by: Faisal Memon Signed-off-by: kfox1111 * Fix docs Signed-off-by: Kevin Fox * Update charts/spire/charts/spire-server/values.yaml Co-authored-by: Faisal Memon Signed-off-by: kfox1111 * Fix docs Signed-off-by: Kevin Fox * Add external k8s bundler Adds support to sync the CA bundle to configmaps in external Kubernetes clusters Signed-off-by: Kevin Fox * Update default Signed-off-by: Kevin Fox * Fix config file layout. Incorperate feedback. Signed-off-by: Kevin Fox * Incorperate feedback Signed-off-by: Kevin Fox * Update based on parent pr feedback Signed-off-by: Kevin Fox * Reformat config file Signed-off-by: Kevin Fox * Fix some things Signed-off-by: Kevin Fox * Reconfigure kind Signed-off-by: Kevin Fox * More debugging Signed-off-by: Kevin Fox * Fix up kind Signed-off-by: Kevin Fox * Incorperate feedback Signed-off-by: Kevin Fox * Add external spire-controller-managers Only one external controller manager is supported at a time until https://github.com/spiffe/spire/issues/4898 is resolved. Signed-off-by: Kevin Fox * Fix tests Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Add Entry ID Prefix support Signed-off-by: Kevin Fox * Mulitcluster test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Implement cleanup setting too Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Make spire-lib bits into its own library chart. Signed-off-by: Kevin Fox * Add spire-nested chart Signed-off-by: Kevin Fox * Fix docs Signed-off-by: Kevin Fox * Fix tests Signed-off-by: Kevin Fox * Fix lint issue Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Add missing file Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Add nameOverride option Signed-off-by: Kevin Fox * Simplify upstream config. Reorder test for faster executation Signed-off-by: Kevin Fox * Enable service account allow list to calculate namespace Signed-off-by: Kevin Fox * Add identity type for child servers Signed-off-by: Kevin Fox * Enable name override setting Signed-off-by: Kevin Fox * Fix printing Signed-off-by: Kevin Fox * Fix formatting Signed-off-by: Kevin Fox * Fix name length issue Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Switch to non cluster-admin user Signed-off-by: Kevin Fox * Test out adding roles Signed-off-by: Kevin Fox * Namespace needs to exist Signed-off-by: Kevin Fox * Remove tty Signed-off-by: Kevin Fox * Fix name Signed-off-by: Kevin Fox * Add missing role Signed-off-by: Kevin Fox * Add kind=none to not require extra objects Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Remove unneeded code Signed-off-by: Kevin Fox * Add security cluster example Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Dont preinstall crds for nested-security Signed-off-by: Kevin Fox * Fix tests Signed-off-by: Kevin Fox * Fix address Signed-off-by: Kevin Fox * Update port Signed-off-by: Kevin Fox * Update psat setting Signed-off-by: Kevin Fox * Update psat setting Signed-off-by: Kevin Fox * Fix tests Signed-off-by: Kevin Fox * Fix tests Signed-off-by: Kevin Fox * Remove older tests that newer tests cover Signed-off-by: Kevin Fox * Fix docs Signed-off-by: Kevin Fox * Fix kind logic Signed-off-by: Kevin Fox * Fix docs Signed-off-by: Kevin Fox * Seems unneeded. Try and remove Signed-off-by: Kevin Fox * Update the default ports to be more user friendly Signed-off-by: Kevin Fox * See if we can leave controller manager port alone Signed-off-by: Kevin Fox * Change the agent default port too Signed-off-by: Kevin Fox * Bump up test container Signed-off-by: Kevin Fox * Swith to testing with nightly Signed-off-by: Kevin Fox * Fix value name Signed-off-by: Kevin Fox * Fix docs Signed-off-by: Kevin Fox * Upgrade to spire-controller-manager 0.5.0 Signed-off-by: Kevin Fox * Try to isolate config differences just to child cluster Signed-off-by: Kevin Fox * Update for released 0.5.0 Signed-off-by: Kevin Fox * Merge in some of the id prefix pr Signed-off-by: Kevin Fox * Entry ID Prefix (#287) * Add Entry ID Prefix support Signed-off-by: Kevin Fox * Mulitcluster test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Implement cleanup setting too Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix docs Signed-off-by: Kevin Fox * Bump up test container Signed-off-by: Kevin Fox * Swith to testing with nightly Signed-off-by: Kevin Fox * Fix value name Signed-off-by: Kevin Fox * Fix docs Signed-off-by: Kevin Fox --------- Signed-off-by: Kevin Fox Signed-off-by: kfox1111 * Fix up doc formatting Signed-off-by: Kevin Fox * Revert notes Signed-off-by: Kevin Fox * Use tags for nested chart Signed-off-by: Kevin Fox * Add missing tag Signed-off-by: Kevin Fox * Fix docs Signed-off-by: Kevin Fox * Fix test Signed-off-by: Kevin Fox * Fix formatting Signed-off-by: Kevin Fox * Fix class name Signed-off-by: Kevin Fox * Fix docs Signed-off-by: Kevin Fox * Fix merge conflict issue Signed-off-by: Kevin Fox * Fix merge issue Signed-off-by: Kevin Fox * Fix docs Signed-off-by: Kevin Fox * Fix merge issue Signed-off-by: Kevin Fox * Incorperate feedback. Switch setting to be externalServer. Signed-off-by: Kevin Fox * Update nested chart to use new setting Signed-off-by: Kevin Fox * Fix merge issue Signed-off-by: Kevin Fox * Fix merge conflict Signed-off-by: Kevin Fox * Fix merge conflict Signed-off-by: Kevin Fox * Add docs about which sections are used with which tags Signed-off-by: Kevin Fox * Update versions Signed-off-by: Kevin Fox --------- Signed-off-by: Kevin Fox Signed-off-by: kfox1111 Co-authored-by: Faisal Memon --- .../spire-root-server-values.yaml | 45 -- .github/workflows/helm-chart-ci.yaml | 2 +- charts/spire-nested/.helmignore | 24 ++ charts/spire-nested/Chart.yaml | 116 ++++++ charts/spire-nested/LICENSE | 201 +++++++++ charts/spire-nested/README.md | 352 ++++++++++++++++ charts/spire-nested/templates/namespaces.yaml | 3 + charts/spire-nested/values.yaml | 388 ++++++++++++++++++ .../.test-files}/child-kind-config.yaml | 0 .../.test-files}/other-kind-config.yaml | 0 examples/nested-full/child-values.yaml | 8 + examples/nested-full/root-values.yaml | 10 + examples/nested-full/run-tests.sh | 137 +++++++ .../.test-files/child-kind-config.yaml | 7 + .../.test-files/other-kind-config.yaml | 7 + examples/nested-security/child-values.yaml | 12 + examples/nested-security/root-values.yaml | 10 + examples/nested-security/run-tests.sh | 126 ++++++ examples/nested/README.md | 10 - examples/nested/multicluster.dot | 61 --- examples/nested/multicluster.png | Bin 129722 -> 0 bytes examples/nested/run-tests.sh | 61 --- examples/nested/singlehardened.dot | 55 --- examples/nested/singlehardened.png | Bin 110148 -> 0 bytes examples/nested/values.yaml | 19 - tests/integration/psat/child-values.yaml | 20 - tests/integration/psat/run-tests.sh | 89 ---- .../psat/sodp-clusterspiffeid.yaml | 23 -- tests/integration/psat/values.yaml | 30 -- 29 files changed, 1402 insertions(+), 414 deletions(-) delete mode 100644 .github/tests/dependencies/spire-root-server-values.yaml create mode 100644 charts/spire-nested/.helmignore create mode 100644 charts/spire-nested/Chart.yaml create mode 100644 charts/spire-nested/LICENSE create mode 100644 charts/spire-nested/README.md create mode 100644 charts/spire-nested/templates/namespaces.yaml create mode 100644 charts/spire-nested/values.yaml rename {tests/integration/psat => examples/nested-full/.test-files}/child-kind-config.yaml (100%) rename {tests/integration/psat => examples/nested-full/.test-files}/other-kind-config.yaml (100%) create mode 100644 examples/nested-full/child-values.yaml create mode 100644 examples/nested-full/root-values.yaml create mode 100755 examples/nested-full/run-tests.sh create mode 100644 examples/nested-security/.test-files/child-kind-config.yaml create mode 100644 examples/nested-security/.test-files/other-kind-config.yaml create mode 100644 examples/nested-security/child-values.yaml create mode 100644 examples/nested-security/root-values.yaml create mode 100755 examples/nested-security/run-tests.sh delete mode 100644 examples/nested/README.md delete mode 100644 examples/nested/multicluster.dot delete mode 100644 examples/nested/multicluster.png delete mode 100755 examples/nested/run-tests.sh delete mode 100644 examples/nested/singlehardened.dot delete mode 100644 examples/nested/singlehardened.png delete mode 100644 examples/nested/values.yaml delete mode 100644 tests/integration/psat/child-values.yaml delete mode 100755 tests/integration/psat/run-tests.sh delete mode 100644 tests/integration/psat/sodp-clusterspiffeid.yaml delete mode 100644 tests/integration/psat/values.yaml diff --git a/.github/tests/dependencies/spire-root-server-values.yaml b/.github/tests/dependencies/spire-root-server-values.yaml deleted file mode 100644 index e7bda6298..000000000 --- a/.github/tests/dependencies/spire-root-server-values.yaml +++ /dev/null @@ -1,45 +0,0 @@ -global: - spire: - clusterName: production - trustDomain: production.other - -spire-server: - controllerManager: - identities: - clusterSPIFFEIDs: - default: - type: raw - spiffeIDTemplate: spiffe://{{ .TrustDomain }}/k8s/{{ .ClusterName }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }} - namespaceSelector: - matchExpressions: - - key: "kubernetes.io/metadata.name" - operator: In - values: [spire-server] - podSelector: - matchLabels: - release: spire - release-namespace: spire-server - component: server - downstream: true - oidc-discovery-provider: - enabled: false - test-keys: - enabled: false - nodeAttestor: - k8sPsat: - serviceAccountAllowList: - - spire-system:spire-agent-upstream - bundleConfigMap: spire-bundle-upstream - notifier: - k8sbundle: - namespace: spire-system - -spiffe-oidc-discovery-provider: - enabled: false - -spire-agent: - enabled: false - -spiffe-csi-driver: - enabled: false - diff --git a/.github/workflows/helm-chart-ci.yaml b/.github/workflows/helm-chart-ci.yaml index c664f9ed5..d93646507 100644 --- a/.github/workflows/helm-chart-ci.yaml +++ b/.github/workflows/helm-chart-ci.yaml @@ -249,7 +249,7 @@ jobs: - name: Install and test example run: | - if [ "${{ matrix.example }}" = "examples/federation" ]; then + if [ "${{ matrix.example }}" = "examples/federation" -o "${{ matrix.example }}" = "examples/nested-full" -o "${{ matrix.example }}" = "examples/nested-security" ]; then kubectl create namespace spire-mgmt helm install -n spire-mgmt spire-crds charts/spire-crds else diff --git a/charts/spire-nested/.helmignore b/charts/spire-nested/.helmignore new file mode 100644 index 000000000..5bdaa3eb0 --- /dev/null +++ b/charts/spire-nested/.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/ +ci/ diff --git a/charts/spire-nested/Chart.yaml b/charts/spire-nested/Chart.yaml new file mode 100644 index 000000000..c5fb70f16 --- /dev/null +++ b/charts/spire-nested/Chart.yaml @@ -0,0 +1,116 @@ +apiVersion: v2 +name: spire-nested +description: > + A Helm chart for deploying the complete Spire stack including: spire-server, spire-agent, spiffe-csi-driver, spiffe-oidc-discovery-provider and spire-controller-manager. +type: application +version: 0.20.0 +appVersion: "1.9.6" +keywords: ["spiffe", "spire", "spire-server", "spire-agent", "oidc", "spire-controller-manager"] +home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire +sources: + - https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire +icon: https://spiffe.io/img/logos/spire/icon/color/spire-icon-color.png +maintainers: + - name: marcofranssen + email: marco.franssen@gmail.com + url: https://marcofranssen.nl + - name: kfox1111 + email: Kevin.Fox@pnnl.gov + - name: faisal-memon + email: fymemon@yahoo.com +kubeVersion: ">=1.21.0-0" +dependencies: + - name: spire-lib + repository: file://../spire/charts/spire-lib + version: 0.1.0 + - name: spire-server + alias: root-spire-server + condition: root-spire-server.enabled + tags: + - nestedRoot + repository: file://../spire/charts/spire-server + version: 0.1.0 + - name: spire-server + alias: external-root-spire-server-full + condition: external-root-spire-server-full.enabled + tags: + - nestedChildFull + repository: file://../spire/charts/spire-server + version: 0.1.0 + - name: spire-server + alias: external-root-spire-server-security + condition: external-root-spire-server-security.enabled + tags: + - nestedChildSecurity + repository: file://../spire/charts/spire-server + version: 0.1.0 + - name: spire-server + alias: internal-spire-server + condition: internal-spire-server.enabled + tags: + - nestedRoot + - nestedChildFull + repository: file://../spire/charts/spire-server + version: 0.1.0 + - name: spire-server + alias: external-spire-server + condition: external-spire-server.enabled + tags: + - nestedRoot + repository: file://../spire/charts/spire-server + version: 0.1.0 + - name: spire-agent + alias: downstream-spire-agent-full + condition: downstream-spire-agent-full.enabled + tags: + - nestedRoot + - nestedChildFull + repository: file://../spire/charts/spire-agent + version: 0.1.0 + - name: spire-agent + alias: downstream-spire-agent-security + condition: downstream-spire-agent-security.enabled + tags: + - nestedChildSecurity + repository: file://../spire/charts/spire-agent + version: 0.1.0 + - name: spire-agent + alias: upstream-spire-agent + condition: upstream-spire-agent.enabled + tags: + - nestedRoot + - nestedChildFull + repository: file://../spire/charts/spire-agent + version: 0.1.0 + - name: spiffe-csi-driver + alias: downstream-spiffe-csi-driver + condition: downstream-spiffe-csi-driver.enabled + tags: + - nestedRoot + - nestedChildFull + - nestedChildSecurity + repository: file://../spire/charts/spiffe-csi-driver + version: 0.1.0 + - name: spiffe-csi-driver + alias: upstream-spiffe-csi-driver + condition: upstream-spiffe-csi-driver.enabled + tags: + - nestedRoot + - nestedChildFull + repository: file://../spire/charts/spiffe-csi-driver + version: 0.1.0 + - name: spiffe-oidc-discovery-provider + condition: spiffe-oidc-discovery-provider.enabled + tags: + - nestedRoot + - nestedChildFull + - nestedChildSecurity + repository: file://../spire/charts/spiffe-oidc-discovery-provider + version: 0.1.0 + - name: tornjak-frontend + condition: tornjak-frontend.enabled + repository: file://../spire/charts/tornjak-frontend + version: 0.1.0 +annotations: + artifacthub.io/category: security + artifacthub.io/license: Apache-2.0 diff --git a/charts/spire-nested/LICENSE b/charts/spire-nested/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/charts/spire-nested/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/charts/spire-nested/README.md b/charts/spire-nested/README.md new file mode 100644 index 000000000..03ca67f4d --- /dev/null +++ b/charts/spire-nested/README.md @@ -0,0 +1,352 @@ +# spire + +![Version: 0.20.0](https://img.shields.io/badge/Version-0.20.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.9.6](https://img.shields.io/badge/AppVersion-1.9.6-informational?style=flat-square) +[![Development Phase](https://github.com/spiffe/spiffe/blob/main/.img/maturity/dev.svg)](https://github.com/spiffe/spiffe/blob/main/MATURITY.md#development) + +A Helm chart for deploying the complete Spire stack including: spire-server, spire-agent, spiffe-csi-driver, spiffe-oidc-discovery-provider and spire-controller-manager. + +**Homepage:** + +## Install Instructions + +### Non Production +To do a quick install suitable for testing in something like minikube: + +```shell +helm upgrade --install -n spire-server spire-crds spire-crds --repo https://spiffe.github.io/helm-charts-hardened/ --create-namespace +helm upgrade --install -n spire-server spire spire-nested --repo https://spiffe.github.io/helm-charts-hardened/ +``` + +### Production + +Preparing a production deployment requires a few steps. + +1. Save the following to your-values.yaml, ideally in your git repo. +```yaml +global: + openshift: false # If running on openshift, set to true + spire: + recommendations: + enabled: true + namespaces: + create: true + ingressControllerType: "" # If not openshift, and want to expose services, set to a supported option [ingress-nginx] + # Update these + clusterName: example-cluster + trustDomain: example.org + caSubject: + country: ARPA + organization: Example + commonName: example.org +``` + +2. If you need a non default storageClass, append the following to the spire-server section and update: +``` + persistence: + storageClass: your-storage-class +``` + +3. If your Kubernetes cluster is OpenShift based, use the output of the following command to update the trustDomain setting: +```shell +oc get cm -n openshift-config-managed console-public -o go-template="{{ .data.consoleURL }}" | sed 's@https://@@; s/^[^.]*\.//' +``` + +4. Find any additional values you might want to set based on the documentation below or using the [examples](https://github.com/spiffe/helm-charts-hardened/tree/main/examples) + +In particular, consider using an external database. + +5. Deploy + +```shell +helm upgrade --install -n spire-mgmt spire-crds spire-crds --repo https://spiffe.github.io/helm-charts-hardened/ --create-namespace +helm upgrade --install -n spire-mgmt spire spire-nested --repo https://spiffe.github.io/helm-charts-hardened/ -f your-values.yaml +``` + +## Clean up + +```shell +helm -n spire-mgmt uninstall spire-crds +helm -n spire-mgmt uninstall spire +kubectl -n spire-server delete pvc -l app.kubernetes.io/instance=spire +kubectl delete crds clusterfederatedtrustdomains.spire.spiffe.io clusterspiffeids.spire.spiffe.io clusterstaticentries.spire.spiffe.io +``` + +## Upgrade notes + +We only support upgrading one major version at a time. Version skipping isn't supported. + +### 0.17.X + +- If you set spire-server.replicaCount > 1, update it to 1 before upgrading and after upgrade you can set it back to its previous value. +- The SPIFFE OIDC Discovery Provider now has many new TLS options and defaults to using SPIRE to issue its certificate. +- The `spiffe-oidc-discovery-provider.insecureScheme.enabled` flag was removed. If you previously set that flag, remove the setting from your values.yaml and see if the new default of using a SPIRE issued certificate is suitable for your deployment. If it isn't, please consider one of the other options under `spiffe-oidc-discovery-provider.tls`. If all other options are still unsuitable, you can still enable the previous mode by disabling TLS. (`spiffe-oidc-discovery-provider.tls.spire.enabled=false`) + +- The SPIFFE OIDC Discovery Provider is now enabled by default. If you previously chose to have it off, you can disable it explicitly with `spiffe-oidc-discovery-provider.enabled=false`. + +### 0.16.X + +The settings under "spire-server.controllerManager.identities" have all been moved under "spire-server.controllerManager.identities.clusterSPIFFEIDs.default". If you have changed any from the defaults, please update them to the new location during upgrade. + +### 0.15.X + +The spire-crds chart has been updated. Please ensure you have upgraded spire-crds before upgrading the spire chart. + +The chart now supports multiple parallel installs of spire-controller-manager. Each install will handle all custom resources with a matching `className` field. By default this is set to `Release.Namespace-Release.Name` and the controller manager will only pick up custom resources with this `className`. + +If you have not loaded any SPIRE custom resources yourself, the upgrade process will be transparent. If you have loaded your own SPIRE custom resources, set `spire-server.controllerManager.watchClassless=true` until you can update your SPIRE custom resources to have the `className` for the instance specified. + +### 0.14.X + +If coming from a chart version before 0.14.0, you must relabel your crds to switch to using the new spire-crds chart. To migrate to the spire-crds chart +run the following: + +Replace the spire-server namespace in the commands below with the namespace you want to install the spire-crds chart in. + +```shell +kubectl label crd "clusterfederatedtrustdomains.spire.spiffe.io" "app.kubernetes.io/managed-by=Helm" +kubectl annotate crd "clusterfederatedtrustdomains.spire.spiffe.io" "meta.helm.sh/release-name=spire-crds" +kubectl annotate crd "clusterfederatedtrustdomains.spire.spiffe.io" "meta.helm.sh/release-namespace=spire-server" +kubectl label crd "clusterspiffeids.spire.spiffe.io" "app.kubernetes.io/managed-by=Helm" +kubectl annotate crd "clusterspiffeids.spire.spiffe.io" "meta.helm.sh/release-name=spire-crds" +kubectl annotate crd "clusterspiffeids.spire.spiffe.io" "meta.helm.sh/release-namespace=spire-server" +kubectl label crd "controllermanagerconfigs.spire.spiffe.io" "app.kubernetes.io/managed-by=Helm" +kubectl annotate crd "controllermanagerconfigs.spire.spiffe.io" "meta.helm.sh/release-name=spire-crds" +kubectl annotate crd "controllermanagerconfigs.spire.spiffe.io" "meta.helm.sh/release-namespace=spire-server" +helm install -n spire-server spire-crds charts/spire-crds +``` + +## Version support + +> [!Warning] +> This Chart is still in development and still subject to change the API (`values.yaml`). +> Until we reach a `1.0.0` version of the chart we can't guarantee backwards compatibility although +> we do aim for as much stability as possible. + +| Dependency | Supported Versions | +|:-----------|:-------------------| +| Helm | `3.x` | +| Kubernetes | `1.22+` | + +> [!Note] +> For Kubernetes, we will officially support the last 3 versions as described in [k8s versioning](https://kubernetes.io/releases/version-skew-policy/#supported-versions). Any version before the last 3 we will try to support as long it doesn't bring security issues or any big maintenance burden. + +## FAQ + +For any issues see our [FAQ](../../FAQ.md)… + +## Usage + +To utilize Spire in your own workloads you should add the following to your workload: + +```diff + apiVersion: v1 + kind: Pod + metadata: + name: my-app + spec: + containers: + - name: my-app + image: "my-app:latest" + imagePullPolicy: Always ++ volumeMounts: ++ - name: spiffe-workload-api ++ mountPath: /spiffe-workload-api ++ readOnly: true + resources: + requests: + cpu: 200m + memory: 32Mi + limits: + cpu: 500m + memory: 64Mi ++ volumes: ++ - name: spiffe-workload-api ++ csi: ++ driver: "csi.spiffe.io" ++ readOnly: true +``` + +Now you can interact with the Spire agent socket from your own application. The socket is mounted on `/spiffe-workload-api/spire-agent.sock`. + +## Maintainers + +| Name | Email | Url | +| ---- | ------ | --- | +| marcofranssen | | | +| kfox1111 | | | +| faisal-memon | | | +| edwbuck | | | + +## Source Code + +* + +## Requirements + +| Repository | Name | Version | +|------------|------|---------| +| file://./charts/spiffe-csi-driver | spiffe-csi-driver | 0.1.0 | +| file://./charts/spiffe-csi-driver | upstream-spiffe-csi-driver(spiffe-csi-driver) | 0.1.0 | +| file://./charts/spiffe-oidc-discovery-provider | spiffe-oidc-discovery-provider | 0.1.0 | +| file://./charts/spire-agent | spire-agent | 0.1.0 | +| file://./charts/spire-agent | upstream-spire-agent(spire-agent) | 0.1.0 | +| file://./charts/spire-server | spire-server | 0.1.0 | +| file://./charts/tornjak-frontend | tornjak-frontend | 0.1.0 | + + + +## Parameters + +### Global parameters + +| Name | Description | Value | +| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | +| `global.k8s.clusterDomain` | Cluster domain name configured for Spire install | `cluster.local` | +| `global.spire.clusterName` | The name of the k8s cluster for Spire install | `example-cluster` | +| `global.spire.jwtIssuer` | The issuer for Spire JWT tokens. Defaults to oidc-discovery.$trustDomain if unset | `""` | +| `global.spire.trustDomain` | The trust domain for Spire install | `example.org` | +| `global.spire.caSubject.country` | Country for Spire server CA | `""` | +| `global.spire.caSubject.organization` | Organization for Spire server CA | `""` | +| `global.spire.caSubject.commonName` | Common Name for Spire server CA | `""` | +| `global.spire.recommendations.enabled` | Use recommended settings for production deployments. Default is off. | `false` | +| `global.spire.recommendations.namespaceLayout` | Set to true to use recommended values for installing across namespaces | `true` | +| `global.spire.recommendations.namespacePSS` | When chart namespace creation is enabled, label them with preffered Pod Security Standard labels | `true` | +| `global.spire.recommendations.priorityClassName` | Set to true to use recommended values for Pod Priority Class Names | `true` | +| `global.spire.recommendations.strictMode` | Check values, such as trustDomain, are overridden with a suitable value for production. | `true` | +| `global.spire.recommendations.securityContexts` | Set to true to use recommended values for Pod and Container Security Contexts | `true` | +| `global.spire.recommendations.prometheus` | Enable prometheus exporters for monitoring | `true` | +| `global.spire.image.registry` | Override all Spire image registries at once | `""` | +| `global.spire.namespaces.create` | Set to true to Create all namespaces. If this or either of the namespace specific create flags is set, the namespace will be created. | `false` | +| `global.spire.namespaces.system.name` | Name of the Spire system Namespace. | `spire-system` | +| `global.spire.namespaces.system.create` | Create a Namespace for Spire system resources. | `false` | +| `global.spire.namespaces.system.annotations` | Annotations to apply to the Spire system Namespace. | `{}` | +| `global.spire.namespaces.system.labels` | Labels to apply to the Spire system Namespace. | `{}` | +| `global.spire.namespaces.server.name` | Name of the Spire server Namespace. | `spire-server` | +| `global.spire.namespaces.server.create` | Create a Namespace for Spire server resources. | `false` | +| `global.spire.namespaces.server.annotations` | Annotations to apply to the Spire server Namespace. | `{}` | +| `global.spire.namespaces.server.labels` | Labels to apply to the Spire server Namespace. | `{}` | +| `global.spire.strictMode` | Check values, such as trustDomain, are overridden with a suitable value for production. | `false` | +| `global.spire.ingressControllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` | +| `global.spire.tools.kubectl.tag` | Set to force the tag to use for all kubectl instances | `""` | +| `global.installAndUpgradeHooks.enabled` | Enable Helm hooks to autofix common install/upgrade issues (should be disabled when using `helm template`) | `true` | +| `global.deleteHooks.enabled` | Enable Helm hooks to autofix common delete issues (should be disabled when using `helm template`) | `true` | +| `tags.nestedRoot` | Set the chart architecture to root nested | `false` | +| `tags.nestedChildFull` | Set the chart mode to a child cluster with its own nested server | `false` | +| `tags.nestedChildSecurity` | Set the chart mode to a child cluster for use with a security cluster | `false` | + +### Spire agent parameters + +| Name | Description | Value | +| ------------------------------------------------- | ------------------------------------------------------------- | ------------------------- | +| `downstream-spire-agent-full.nameOverride` | Overrides the name of Spire agent pods | `agent-downstream` | +| `downstream-spire-agent-full.server.nameOverride` | The name override setting of the internal SPIRE server | `internal-server` | +| `downstream-spire-agent-full.bundleConfigMap` | The name of the configmap that contains the downstream bundle | `spire-bundle-downstream` | + +### Spire agent parameters + +| Name | Description | Value | +| ----------------------------------------------------- | ------------------------------------------------------------- | ----------------------- | +| `downstream-spire-agent-security.nameOverride` | Overrides the name of Spire agent pods | `agent-downstream` | +| `downstream-spire-agent-security.bundleConfigMap` | The name of the configmap that contains the downstream bundle | `spire-bundle-upstream` | +| `downstream-spire-agent-security.serviceAccount.name` | The name of the service account to use | `spire-agent-upstream` | + +### Upstream Spire agent parameters + +| Name | Description | Value | +| ------------------------------------------------ | -------------------------------------------------- | ---------------------------------------------------- | +| `upstream-spire-agent.upstream` | Flag for enabling upstream Spire agent | `true` | +| `upstream-spire-agent.nameOverride` | Name override for upstream Spire agent | `agent-upstream` | +| `upstream-spire-agent.bundleConfigMap` | The configmap name for upstream Spire agent bundle | `spire-bundle-upstream` | +| `upstream-spire-agent.socketPath` | Socket path where Spire agent socket is mounted | `/run/spire/agent-sockets-upstream/spire-agent.sock` | +| `upstream-spire-agent.serviceAccount.name` | Service account name for upstream Spire agent | `spire-agent-upstream` | +| `upstream-spire-agent.healthChecks.port` | Health check port number for upstream Spire agent | `9981` | +| `upstream-spire-agent.telemetry.prometheus.port` | The port where prometheus metrics are available | `9989` | +| `upstream-spire-agent.server.nameOverride` | The name override setting of the root SPIRE server | `root-server` | + +### SPIFFE CSI Driver parameters + +| Name | Description | Value | +| ----------------------------------------------- | ----------------- | ------------------------------ | +| `downstream-spiffe-csi-driver.fullnameOverride` | Fullname override | `spiffe-csi-driver-downstream` | + +### Upstream SPIFFE CSI Driver parameters + +| Name | Description | Value | +| ---------------------------------------------- | ----------------------------------------------------------- | ---------------------------------------------------- | +| `upstream-spiffe-csi-driver.fullnameOverride` | Fullname override | `spiffe-csi-driver-upstream` | +| `upstream-spiffe-csi-driver.pluginName` | The plugin name for configuring upstream Spiffe CSI driver | `upstream.csi.spiffe.io` | +| `upstream-spiffe-csi-driver.agentSocketPath` | The socket path where Spiffe CSI driver mounts agent socket | `/run/spire/agent-sockets-upstream/spire-agent.sock` | +| `upstream-spiffe-csi-driver.healthChecks.port` | The port where Spiffe CSI driver health checks are exposed | `9810` | + +### SPIFFE oidc discovery provider parameters + +| Name | Description | Value | +| ------------------------------------------------- | ----------------- | -------------------------------- | +| `spiffe-oidc-discovery-provider.fullnameOverride` | Fullname override | `spiffe-oidc-discovery-provider` | + +### Tornjak frontend parameters + +| Name | Description | Value | +| --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | +| `tornjak-frontend.enabled` | Enables deployment of Tornjak frontend/UI (Not for production) | `false` | +| `root-spire-server.nameOverride` | Name override | `root-server` | +| `root-spire-server.crNameOverride` | Custom Resource name override | `root` | +| `root-spire-server.controllerManager.enabled` | Enable controller manager and provision CRD's | `true` | +| `root-spire-server.controllerManager.externalControllerManagers.enabled` | Flag to enable external controller managers | `true` | +| `root-spire-server.controllerManager.validatingWebhookConfiguration.enabled` | Disable only when you have another instance on the k8s cluster with webhooks enabled. | `false` | +| `root-spire-server.controllerManager.className` | specify to use an explicit class name. | `spire-mgmt-root-server` | +| `root-spire-server.controllerManager.identities.clusterSPIFFEIDs.child-servers.enabled` | Enable child servers | `true` | +| `root-spire-server.controllerManager.identities.clusterSPIFFEIDs.default.enabled` | Enable the default cluster spiffe id | `false` | +| `root-spire-server.controllerManager.identities.clusterSPIFFEIDs.oidc-discovery-provider.enabled` | Enable the test-keys identity | `false` | +| `root-spire-server.controllerManager.identities.clusterSPIFFEIDs.test-keys.enabled` | Enable the test-keys identity | `false` | +| `root-spire-server.externalControllerManagers.enabled` | Flag to enable external controller managers | `true` | +| `root-spire-server.nodeAttestor.k8sPsat.serviceAccountAllowList` | Allowed service accounts for Psat nodeattestor | `[]` | +| `root-spire-server.bundleConfigMap` | The name of the configmap to store the upstream bundle | `spire-bundle-upstream` | +| `external-root-spire-server-full.externalServer` | Set to true to setup the bundle configmap, rbac rules, and identity documents but doesn't deploy the server locally. Useful for external servers. | `true` | +| `external-root-spire-server-full.nameOverride` | Name override | `root-server` | +| `external-root-spire-server-full.crNameOverride` | Custom Resource name override | `root` | +| `external-root-spire-server-full.controllerManager.enabled` | Enable controller manager and provision CRD's | `true` | +| `external-root-spire-server-full.controllerManager.validatingWebhookConfiguration.enabled` | Disable only when you have another instance on the k8s cluster with webhooks enabled. | `false` | +| `external-root-spire-server-full.controllerManager.className` | specify to use an explicit class name. | `spire-mgmt-external-server` | +| `external-root-spire-server-full.controllerManager.identities.clusterSPIFFEIDs.child-servers.enabled` | Enable child servers | `true` | +| `external-root-spire-server-full.controllerManager.identities.clusterSPIFFEIDs.default.enabled` | Enable the default cluster spiffe id | `false` | +| `external-root-spire-server-full.controllerManager.identities.clusterSPIFFEIDs.oidc-discovery-provider.enabled` | Enable the test-keys identity | `false` | +| `external-root-spire-server-full.controllerManager.identities.clusterSPIFFEIDs.test-keys.enabled` | Enable the test-keys identity | `false` | +| `external-root-spire-server-full.nodeAttestor.k8sPsat.serviceAccountAllowList` | Allowed service accounts for Psat nodeattestor | `[]` | +| `external-root-spire-server-full.bundleConfigMap` | The name of the configmap to store the upstream bundle | `spire-bundle-upstream` | +| `external-root-spire-server-security.externalServer` | Set to true to setup the bundle configmap, rbac rules, and identity documents but doesn't deploy the server locally. Useful for external servers. | `true` | +| `external-root-spire-server-security.nameOverride` | Name override | `root-server` | +| `external-root-spire-server-security.crNameOverride` | Custom Resource name override | `root` | +| `external-root-spire-server-security.controllerManager.enabled` | Enable controller manager and provision CRD's | `true` | +| `external-root-spire-server-security.controllerManager.validatingWebhookConfiguration.enabled` | Disable only when you have another instance on the k8s cluster with webhooks enabled. | `false` | +| `external-root-spire-server-security.controllerManager.className` | specify to use an explicit class name. | `spire-mgmt-external-server` | +| `external-root-spire-server-security.nodeAttestor.k8sPsat.serviceAccountAllowList` | Allowed service accounts for Psat nodeattestor | `[]` | +| `external-root-spire-server-security.bundleConfigMap` | The name of the configmap to store the upstream bundle | `spire-bundle-upstream` | + +### Spire server parameters + +| Name | Description | Value | +| ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- | ---------------------------- | +| `internal-spire-server.nameOverride` | Overrides the name of Spire server pods | `internal-server` | +| `internal-spire-server.controllerManager.enabled` | Enable controller manager and provision CRD's | `true` | +| `internal-spire-server.controllerManager.identities.clusterSPIFFEIDs.oidc-discovery-provider.autoPopulateDNSNames` | Auto populate dns entries | `false` | +| `internal-spire-server.externalControllerManagers.enabled` | Flag to enable external controller managers | `true` | +| `internal-spire-server.upstreamAuthority.spire.enabled` | Enable upstream SPIRE server | `true` | +| `internal-spire-server.upstreamAuthority.spire.upstreamDriver` | Use an upstream driver for authentication | `upstream.csi.spiffe.io` | +| `internal-spire-server.upstreamAuthority.spire.server.nameOverride` | The name override setting of the root SPIRE server | `root-server` | +| `internal-spire-server.bundleConfigMap` | The name of the configmap to store the downstream bundle | `spire-bundle-downstream` | +| `external-spire-server.nameOverride` | Overrides the name of Spire server pods | `external-server` | +| `external-spire-server.crNameOverride` | Custom Resource name override | `external` | +| `external-spire-server.controllerManager.enabled` | Enable controller manager and provision CRD's | `true` | +| `external-spire-server.controllerManager.validatingWebhookConfiguration.enabled` | Disable only when you have another instance on the k8s cluster with webhooks enabled. | `false` | +| `external-spire-server.controllerManager.className` | specify to use an explicit class name. | `spire-mgmt-external-server` | +| `external-spire-server.controllerManager.identities.clusterSPIFFEIDs.default.enabled` | Enable the default identity | `false` | +| `external-spire-server.controllerManager.identities.clusterSPIFFEIDs.oidc-discovery-provider.enabled` | Enable the oidc-discovery-provider identity | `false` | +| `external-spire-server.controllerManager.identities.clusterSPIFFEIDs.test-keys.enabled` | Enable the test-keys identity | `false` | +| `external-spire-server.externalControllerManagers.enabled` | Flag to enable external controller managers | `true` | +| `external-spire-server.upstreamAuthority.spire.enabled` | Enable upstream SPIRE server | `true` | +| `external-spire-server.upstreamAuthority.spire.upstreamDriver` | Use an upstream driver for authentication | `upstream.csi.spiffe.io` | +| `external-spire-server.upstreamAuthority.spire.server.nameOverride` | The name override setting of the root SPIRE server | `root-server` | +| `external-spire-server.notifier.k8sbundle.enabled` | Enable local k8s bundle uploader | `false` | +| `external-spire-server.nodeAttestor.k8sPsat.enabled` | Enable Psat k8s nodeattestor | `false` | +| `external-spire-server.nodeAttestor.joinToken.enabled` | Enable the join_token nodeattestor | `true` | diff --git a/charts/spire-nested/templates/namespaces.yaml b/charts/spire-nested/templates/namespaces.yaml new file mode 100644 index 000000000..01af6cdeb --- /dev/null +++ b/charts/spire-nested/templates/namespaces.yaml @@ -0,0 +1,3 @@ +{{- include "spire-lib.namespace.system" . }} +--- +{{- include "spire-lib.namespace.server" . }} diff --git a/charts/spire-nested/values.yaml b/charts/spire-nested/values.yaml new file mode 100644 index 000000000..00bc8059b --- /dev/null +++ b/charts/spire-nested/values.yaml @@ -0,0 +1,388 @@ +# Default configuration for Spire chart +# SPDX-License-Identifier: APACHE-2.0 + +## @section Global parameters +## Note: the parameter values specified here will override the chart level values for these parameters. +## +global: + k8s: + ## @param global.k8s.clusterDomain Cluster domain name configured for Spire install + clusterDomain: cluster.local + + spire: + ## @param global.spire.clusterName The name of the k8s cluster for Spire install + clusterName: example-cluster + ## @param global.spire.jwtIssuer The issuer for Spire JWT tokens. Defaults to oidc-discovery.$trustDomain if unset + jwtIssuer: "" + ## @param global.spire.trustDomain The trust domain for Spire install + trustDomain: example.org + + ## @param global.spire.caSubject.country Country for Spire server CA + ## @param global.spire.caSubject.organization Organization for Spire server CA + ## @param global.spire.caSubject.commonName Common Name for Spire server CA + caSubject: + country: "" + organization: "" + commonName: "" + + ## @param global.spire.recommendations.enabled Use recommended settings for production deployments. Default is off. + ## @param global.spire.recommendations.namespaceLayout Set to true to use recommended values for installing across namespaces + ## @param global.spire.recommendations.namespacePSS When chart namespace creation is enabled, label them with preffered Pod Security Standard labels + ## @param global.spire.recommendations.priorityClassName Set to true to use recommended values for Pod Priority Class Names + ## @param global.spire.recommendations.strictMode Check values, such as trustDomain, are overridden with a suitable value for production. + ## @param global.spire.recommendations.securityContexts Set to true to use recommended values for Pod and Container Security Contexts + ## @param global.spire.recommendations.prometheus Enable prometheus exporters for monitoring + recommendations: + enabled: false + namespaceLayout: true + namespacePSS: true + priorityClassName: true + strictMode: true + securityContexts: true + prometheus: true + + image: + ## @param global.spire.image.registry Override all Spire image registries at once + registry: "" + + namespaces: + ## @param global.spire.namespaces.create Set to true to Create all namespaces. If this or either of the namespace specific create flags is set, the namespace will be created. + create: false + system: + ## @param global.spire.namespaces.system.name Name of the Spire system Namespace. + name: "spire-system" + ## @param global.spire.namespaces.system.create Create a Namespace for Spire system resources. + create: false + ## @param global.spire.namespaces.system.annotations [object] Annotations to apply to the Spire system Namespace. + annotations: {} + ## @param global.spire.namespaces.system.labels [object] Labels to apply to the Spire system Namespace. + labels: {} + server: + ## @param global.spire.namespaces.server.name Name of the Spire server Namespace. + name: "spire-server" + ## @param global.spire.namespaces.server.create Create a Namespace for Spire server resources. + create: false + ## @param global.spire.namespaces.server.annotations [object] Annotations to apply to the Spire server Namespace. + annotations: {} + ## @param global.spire.namespaces.server.labels [object] Labels to apply to the Spire server Namespace. + labels: {} + + ## @param global.spire.strictMode Check values, such as trustDomain, are overridden with a suitable value for production. + strictMode: false + + ## @param global.spire.ingressControllerType Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. + ingressControllerType: "" + + tools: + kubectl: + ## @param global.spire.tools.kubectl.tag Set to force the tag to use for all kubectl instances + tag: "" + + installAndUpgradeHooks: + ## @param global.installAndUpgradeHooks.enabled Enable Helm hooks to autofix common install/upgrade issues (should be disabled when using `helm template`) + enabled: true + deleteHooks: + ## @param global.deleteHooks.enabled Enable Helm hooks to autofix common delete issues (should be disabled when using `helm template`) + enabled: true + +# telemetry: +# prometheus: +# enabled: true +# podMonitor: +# enabled: true +# # -- Allows to install the PodMonitor in another namespace then the spire components are installed into. +# namespace: "kube-prometheus-system" +# labels: {} + +tags: + ## @param tags.nestedRoot Set the chart architecture to root nested + nestedRoot: false + ## @param tags.nestedChildFull Set the chart mode to a child cluster with its own nested server + nestedChildFull: false + ## @param tags.nestedChildSecurity Set the chart mode to a child cluster for use with a security cluster + nestedChildSecurity: false + +## subcharts + +## @section Spire agent parameters +## Parameter values for Spire agent +## +# Used with tags [nestedRoot, nestedChildFull] +downstream-spire-agent-full: + # enabled: true + ## @param downstream-spire-agent-full.nameOverride Overrides the name of Spire agent pods + nameOverride: agent-downstream + server: + ## @param downstream-spire-agent-full.server.nameOverride The name override setting of the internal SPIRE server + nameOverride: internal-server + ## @param downstream-spire-agent-full.bundleConfigMap The name of the configmap that contains the downstream bundle + bundleConfigMap: spire-bundle-downstream + +## @section Spire agent parameters +## Parameter values for Spire agent +## +# Used with tags [nestedChildSecurity] +downstream-spire-agent-security: + # enabled: true + ## @param downstream-spire-agent-security.nameOverride Overrides the name of Spire agent pods + nameOverride: agent-downstream + ## @param downstream-spire-agent-security.bundleConfigMap The name of the configmap that contains the downstream bundle + bundleConfigMap: spire-bundle-upstream + serviceAccount: + ## @param downstream-spire-agent-security.serviceAccount.name The name of the service account to use + name: spire-agent-upstream + +## @section Upstream Spire agent parameters +## Parameter values for upstream Spire agent +## +# Used with tags [nestedRoot, nestedChildFull] +upstream-spire-agent: + # enabled: true + ## @param upstream-spire-agent.upstream Flag for enabling upstream Spire agent + upstream: true + ## @param upstream-spire-agent.nameOverride Name override for upstream Spire agent + nameOverride: agent-upstream + ## @param upstream-spire-agent.bundleConfigMap The configmap name for upstream Spire agent bundle + bundleConfigMap: spire-bundle-upstream + ## @param upstream-spire-agent.socketPath Socket path where Spire agent socket is mounted + socketPath: /run/spire/agent-sockets-upstream/spire-agent.sock + serviceAccount: + ## @param upstream-spire-agent.serviceAccount.name Service account name for upstream Spire agent + name: spire-agent-upstream + healthChecks: + ## @param upstream-spire-agent.healthChecks.port Health check port number for upstream Spire agent + port: 9981 + telemetry: + prometheus: + ## @param upstream-spire-agent.telemetry.prometheus.port The port where prometheus metrics are available + port: 9989 + server: + ## @param upstream-spire-agent.server.nameOverride The name override setting of the root SPIRE server + nameOverride: root-server + +## @section SPIFFE CSI Driver parameters +## Parameter values for spiffe-csi-driver +## +# Used with tags [nestedRoot, nestedChildFull, nestedChildSecurity] +downstream-spiffe-csi-driver: + # enabled: true + ## @param downstream-spiffe-csi-driver.fullnameOverride Fullname override + fullnameOverride: spiffe-csi-driver-downstream + +## @section Upstream SPIFFE CSI Driver parameters +## Parameter values for upstream spiffe-csi-driver +## +# Used with tags [nestedRoot, nestedChildFull] +upstream-spiffe-csi-driver: + # enabled: true + ## @param upstream-spiffe-csi-driver.fullnameOverride Fullname override + fullnameOverride: spiffe-csi-driver-upstream + ## @param upstream-spiffe-csi-driver.pluginName The plugin name for configuring upstream Spiffe CSI driver + pluginName: upstream.csi.spiffe.io + ## @param upstream-spiffe-csi-driver.agentSocketPath The socket path where Spiffe CSI driver mounts agent socket + agentSocketPath: /run/spire/agent-sockets-upstream/spire-agent.sock + healthChecks: + ## @param upstream-spiffe-csi-driver.healthChecks.port The port where Spiffe CSI driver health checks are exposed + port: 9810 + +## @section SPIFFE oidc discovery provider parameters +## Parameter values for spiffe-oidc-discovery-provider +## +# Used with tags [nestedRoot, nestedChildFull, nestedChildSecurity] +spiffe-oidc-discovery-provider: + # enabled: true + ## @param spiffe-oidc-discovery-provider.fullnameOverride Fullname override + fullnameOverride: spiffe-oidc-discovery-provider + +## @section Tornjak frontend parameters +## Parameter values for Tornjak frontend +## +tornjak-frontend: + ## @param tornjak-frontend.enabled Enables deployment of Tornjak frontend/UI (Not for production) + enabled: false + +# Used with tags [nestedRoot] +root-spire-server: + # enabled: true + ## @param root-spire-server.nameOverride Name override + nameOverride: root-server + ## @param root-spire-server.crNameOverride Custom Resource name override + crNameOverride: root + controllerManager: + ## @param root-spire-server.controllerManager.enabled Enable controller manager and provision CRD's + enabled: true + externalControllerManagers: + ## @param root-spire-server.controllerManager.externalControllerManagers.enabled Flag to enable external controller managers + enabled: true + validatingWebhookConfiguration: + ## @param root-spire-server.controllerManager.validatingWebhookConfiguration.enabled Disable only when you have another instance on the k8s cluster with webhooks enabled. + enabled: false + ## @param root-spire-server.controllerManager.className specify to use an explicit class name. + className: spire-mgmt-root-server + identities: + clusterSPIFFEIDs: + child-servers: + ## @param root-spire-server.controllerManager.identities.clusterSPIFFEIDs.child-servers.enabled Enable child servers + enabled: true + default: + ## @param root-spire-server.controllerManager.identities.clusterSPIFFEIDs.default.enabled Enable the default cluster spiffe id + enabled: false + oidc-discovery-provider: + ## @param root-spire-server.controllerManager.identities.clusterSPIFFEIDs.oidc-discovery-provider.enabled Enable the test-keys identity + enabled: false + test-keys: + ## @param root-spire-server.controllerManager.identities.clusterSPIFFEIDs.test-keys.enabled Enable the test-keys identity + enabled: false + externalControllerManagers: + ## @param root-spire-server.externalControllerManagers.enabled Flag to enable external controller managers + enabled: true + nodeAttestor: + k8sPsat: + ## @param root-spire-server.nodeAttestor.k8sPsat.serviceAccountAllowList [array] Allowed service accounts for Psat nodeattestor + serviceAccountAllowList: + - spire-agent-upstream + ## @param root-spire-server.bundleConfigMap The name of the configmap to store the upstream bundle + bundleConfigMap: spire-bundle-upstream + +# Used with tags [nestedChildFull] +external-root-spire-server-full: + ## @param external-root-spire-server-full.externalServer Set to true to setup the bundle configmap, rbac rules, and identity documents but doesn't deploy the server locally. Useful for external servers. + externalServer: true + ## @param external-root-spire-server-full.nameOverride Name override + nameOverride: root-server + ## @param external-root-spire-server-full.crNameOverride Custom Resource name override + crNameOverride: root + controllerManager: + ## @param external-root-spire-server-full.controllerManager.enabled Enable controller manager and provision CRD's + enabled: true + validatingWebhookConfiguration: + ## @param external-root-spire-server-full.controllerManager.validatingWebhookConfiguration.enabled Disable only when you have another instance on the k8s cluster with webhooks enabled. + enabled: false + ## @param external-root-spire-server-full.controllerManager.className specify to use an explicit class name. + className: spire-mgmt-external-server + identities: + clusterSPIFFEIDs: + child-servers: + ## @param external-root-spire-server-full.controllerManager.identities.clusterSPIFFEIDs.child-servers.enabled Enable child servers + enabled: true + default: + ## @param external-root-spire-server-full.controllerManager.identities.clusterSPIFFEIDs.default.enabled Enable the default cluster spiffe id + enabled: false + oidc-discovery-provider: + ## @param external-root-spire-server-full.controllerManager.identities.clusterSPIFFEIDs.oidc-discovery-provider.enabled Enable the test-keys identity + enabled: false + test-keys: + ## @param external-root-spire-server-full.controllerManager.identities.clusterSPIFFEIDs.test-keys.enabled Enable the test-keys identity + enabled: false + nodeAttestor: + k8sPsat: + ## @param external-root-spire-server-full.nodeAttestor.k8sPsat.serviceAccountAllowList [array] Allowed service accounts for Psat nodeattestor + serviceAccountAllowList: + - spire-agent-upstream + ## @param external-root-spire-server-full.bundleConfigMap The name of the configmap to store the upstream bundle + bundleConfigMap: spire-bundle-upstream + +# Used with tags [nestedChildSecurity] +external-root-spire-server-security: + ## @param external-root-spire-server-security.externalServer Set to true to setup the bundle configmap, rbac rules, and identity documents but doesn't deploy the server locally. Useful for external servers. + externalServer: true + ## @param external-root-spire-server-security.nameOverride Name override + nameOverride: root-server + ## @param external-root-spire-server-security.crNameOverride Custom Resource name override + crNameOverride: root + controllerManager: + ## @param external-root-spire-server-security.controllerManager.enabled Enable controller manager and provision CRD's + enabled: true + validatingWebhookConfiguration: + ## @param external-root-spire-server-security.controllerManager.validatingWebhookConfiguration.enabled Disable only when you have another instance on the k8s cluster with webhooks enabled. + enabled: false + ## @param external-root-spire-server-security.controllerManager.className specify to use an explicit class name. + className: spire-mgmt-external-server + nodeAttestor: + k8sPsat: + ## @param external-root-spire-server-security.nodeAttestor.k8sPsat.serviceAccountAllowList [array] Allowed service accounts for Psat nodeattestor + serviceAccountAllowList: + - spire-agent-upstream + ## @param external-root-spire-server-security.bundleConfigMap The name of the configmap to store the upstream bundle + bundleConfigMap: spire-bundle-upstream + +## @section Spire server parameters +## Parameter values for Spire server +## +# Used with tags [nestedRoot, nestedChildFull] +internal-spire-server: + # enabled: true + ## @param internal-spire-server.nameOverride Overrides the name of Spire server pods + nameOverride: internal-server + controllerManager: + ## @param internal-spire-server.controllerManager.enabled Enable controller manager and provision CRD's + enabled: true + identities: + clusterSPIFFEIDs: + oidc-discovery-provider: + ## @param internal-spire-server.controllerManager.identities.clusterSPIFFEIDs.oidc-discovery-provider.autoPopulateDNSNames Auto populate dns entries + autoPopulateDNSNames: false + externalControllerManagers: + ## @param internal-spire-server.externalControllerManagers.enabled Flag to enable external controller managers + enabled: true + upstreamAuthority: + spire: + ## @param internal-spire-server.upstreamAuthority.spire.enabled Enable upstream SPIRE server + enabled: true + ## @param internal-spire-server.upstreamAuthority.spire.upstreamDriver Use an upstream driver for authentication + upstreamDriver: upstream.csi.spiffe.io + server: + ## @param internal-spire-server.upstreamAuthority.spire.server.nameOverride The name override setting of the root SPIRE server + nameOverride: root-server + ## @param internal-spire-server.bundleConfigMap The name of the configmap to store the downstream bundle + bundleConfigMap: spire-bundle-downstream + +# Used with tags [nestedRoot] +external-spire-server: + # enabled: true + ## @param external-spire-server.nameOverride Overrides the name of Spire server pods + nameOverride: external-server + ## @param external-spire-server.crNameOverride Custom Resource name override + crNameOverride: external + controllerManager: + ## @param external-spire-server.controllerManager.enabled Enable controller manager and provision CRD's + enabled: true + validatingWebhookConfiguration: + ## @param external-spire-server.controllerManager.validatingWebhookConfiguration.enabled Disable only when you have another instance on the k8s cluster with webhooks enabled. + enabled: false + ## @param external-spire-server.controllerManager.className specify to use an explicit class name. + className: spire-mgmt-external-server + identities: + clusterSPIFFEIDs: + default: + ## @param external-spire-server.controllerManager.identities.clusterSPIFFEIDs.default.enabled Enable the default identity + enabled: false + oidc-discovery-provider: + ## @param external-spire-server.controllerManager.identities.clusterSPIFFEIDs.oidc-discovery-provider.enabled Enable the oidc-discovery-provider identity + enabled: false + test-keys: + ## @param external-spire-server.controllerManager.identities.clusterSPIFFEIDs.test-keys.enabled Enable the test-keys identity + enabled: false + externalControllerManagers: + ## @param external-spire-server.externalControllerManagers.enabled Flag to enable external controller managers + enabled: true + upstreamAuthority: + spire: + ## @param external-spire-server.upstreamAuthority.spire.enabled Enable upstream SPIRE server + enabled: true + ## @param external-spire-server.upstreamAuthority.spire.upstreamDriver Use an upstream driver for authentication + upstreamDriver: upstream.csi.spiffe.io + server: + ## @param external-spire-server.upstreamAuthority.spire.server.nameOverride The name override setting of the root SPIRE server + nameOverride: root-server + notifier: + k8sbundle: + ## @param external-spire-server.notifier.k8sbundle.enabled Enable local k8s bundle uploader + enabled: false + nodeAttestor: + k8sPsat: + ## @param external-spire-server.nodeAttestor.k8sPsat.enabled Enable Psat k8s nodeattestor + enabled: false + joinToken: + ## @param external-spire-server.nodeAttestor.joinToken.enabled Enable the join_token nodeattestor + enabled: true diff --git a/tests/integration/psat/child-kind-config.yaml b/examples/nested-full/.test-files/child-kind-config.yaml similarity index 100% rename from tests/integration/psat/child-kind-config.yaml rename to examples/nested-full/.test-files/child-kind-config.yaml diff --git a/tests/integration/psat/other-kind-config.yaml b/examples/nested-full/.test-files/other-kind-config.yaml similarity index 100% rename from tests/integration/psat/other-kind-config.yaml rename to examples/nested-full/.test-files/other-kind-config.yaml diff --git a/examples/nested-full/child-values.yaml b/examples/nested-full/child-values.yaml new file mode 100644 index 000000000..0fb62ae1e --- /dev/null +++ b/examples/nested-full/child-values.yaml @@ -0,0 +1,8 @@ +#global: +# spire: +# clusterName: changeme +# upstreamSpireAddress: spire-server.changeme + +tags: + nestedChildFull: true + diff --git a/examples/nested-full/root-values.yaml b/examples/nested-full/root-values.yaml new file mode 100644 index 000000000..d9066ece3 --- /dev/null +++ b/examples/nested-full/root-values.yaml @@ -0,0 +1,10 @@ +tags: + nestedRoot: true + +spiffe-oidc-discovery-provider: + ingress: + enabled: true + +external-spire-server: + ingress: + enabled: true diff --git a/examples/nested-full/run-tests.sh b/examples/nested-full/run-tests.sh new file mode 100755 index 000000000..6d51cbcdb --- /dev/null +++ b/examples/nested-full/run-tests.sh @@ -0,0 +1,137 @@ +#!/usr/bin/env bash + +set -xe + +SCRIPT="$(readlink -f "$0")" +SCRIPTPATH="$(dirname "${SCRIPT}")" +TESTDIR="${SCRIPTPATH}/../../.github/tests" +#DEPS="${TESTDIR}/dependencies" + +# shellcheck source=/dev/null +source "${SCRIPTPATH}/../../.github/scripts/parse-versions.sh" +# shellcheck source=/dev/null +source "${TESTDIR}/common.sh" + +CLEANUP=1 + +for i in "$@"; do + case $i in + -c) + CLEANUP=0 + shift # past argument=value + ;; + esac +done + +teardown() { + print_helm_releases + print_spire_workload_status spire-root-server + print_spire_workload_status spire-server spire-system + + if [[ "$1" -ne 0 ]]; then + get_namespace_details spire-root-server + get_namespace_details spire-server spire-system + fi + + if [ "${CLEANUP}" -eq 1 ]; then + helm uninstall --namespace spire-server spire 2>/dev/null || true + kubectl delete ns spire-server 2>/dev/null || true + kubectl delete ns spire-system 2>/dev/null || true + + helm uninstall --namespace mysql spire-root-server 2>/dev/null || true + kubectl delete ns spire-root-server 2>/dev/null || true + fi +} + +trap 'EC=$? && trap - SIGTERM && teardown $EC' SIGINT SIGTERM EXIT + +# Update deps +helm dep up charts/spire-nested + +# List nodes +kubectl get nodes + +# Deploy an ingress controller +IP=$(kubectl get nodes chart-testing-control-plane -o go-template='{{ range .status.addresses }}{{ if eq .type "InternalIP" }}{{ .address }}{{ end }}{{ end }}') +helm upgrade --install ingress-nginx ingress-nginx --version "$VERSION_INGRESS_NGINX" --repo "$HELM_REPO_INGRESS_NGINX" \ + --namespace ingress-nginx \ + --create-namespace \ + --set "controller.extraArgs.enable-ssl-passthrough=,controller.admissionWebhooks.enabled=false,controller.service.type=ClusterIP,controller.service.externalIPs[0]=$IP" \ + --set controller.ingressClassResource.default=true \ + --wait + +# Test the ingress controller. Should 404 as there is no services yet. +curl "$IP" + +kubectl get configmap -n kube-system coredns -o yaml | grep hosts || kubectl get configmap -n kube-system coredns -o yaml | sed "/ready/a\ hosts {\n fallthrough\n }" | kubectl apply -f - +kubectl get configmap -n kube-system coredns -o yaml | grep production.other || kubectl get configmap -n kube-system coredns -o yaml | sed "/hosts/a\ $IP oidc-discovery.production.other\n $IP spire-server.production.other\n" | kubectl apply -f - +kubectl rollout restart -n kube-system deployment/coredns +kubectl rollout status -n kube-system -w --timeout=1m deploy/coredns + +for cluster in child other; do + KC="${SCRIPTPATH}/kubeconfig-${cluster}" + + kind create cluster --name "${cluster}" --kubeconfig "${SCRIPTPATH}/kubeconfig-${cluster}" --config "${SCRIPTPATH}/.test-files/${cluster}-kind-config.yaml" + md5sum "${KC}" + wc -l "${KC}" + + helm upgrade --kubeconfig "${KC}" --install --create-namespace --namespace spire-mgmt spire-crds charts/spire-crds + helm upgrade --kubeconfig "${KC}" --install --namespace spire-mgmt --values "${COMMON_TEST_YOUR_VALUES},${SCRIPTPATH}/child-values.yaml" \ + --set "global.spire.upstreamSpireAddress=spire-server.production.other" \ + --set "global.spire.namespaces.create=true" \ + --set "global.spire.clusterName=${cluster}" \ + spire charts/spire-nested + + kubectl get configmap --kubeconfig "${KC}" -n kube-system coredns -o yaml | grep hosts || kubectl get configmap --kubeconfig "${KC}" -n kube-system coredns -o yaml | sed "/ready/a\ hosts {\n fallthrough\n }" | kubectl apply --kubeconfig "${KC}" -f - + kubectl get configmap --kubeconfig "${KC}" -n kube-system coredns -o yaml | grep production.other || kubectl get configmap --kubeconfig "${KC}" -n kube-system coredns -o yaml | sed "/hosts/a\ $IP spire-server.production.other\n $IP spire-server.production.other\n" | kubectl apply --kubeconfig "${KC}" -f - + kubectl rollout restart --kubeconfig "${KC}" -n kube-system deployment/coredns + kubectl rollout status --kubeconfig "${KC}" -n kube-system -w --timeout=1m deploy/coredns +done + +docker exec -i child-control-plane bash -c 'kubeadm kubeconfig user --client-name=spire-root' > "${SCRIPTPATH}/child-spire-root.kubeconfig" +docker exec -i other-control-plane bash -c 'kubeadm kubeconfig user --client-name=spire-root' > "${SCRIPTPATH}/other-spire-root.kubeconfig" +CHILD_KCB64="$(base64 < "${SCRIPTPATH}/child-spire-root.kubeconfig" | tr '\n' ' ' | sed 's/ //g')" +OTHER_KCB64="$(base64 < "${SCRIPTPATH}/other-spire-root.kubeconfig" | tr '\n' ' ' | sed 's/ //g')" + +helm upgrade --install --create-namespace --namespace spire-mgmt --values "${COMMON_TEST_YOUR_VALUES},${SCRIPTPATH}/root-values.yaml" \ + --wait spire charts/spire-nested \ + --set "global.spire.namespaces.create=true" \ + --set "global.spire.ingressControllerType=ingress-nginx" \ + --set "external-spire-server.kubeConfigs.child.kubeConfigBase64=${CHILD_KCB64}" \ + --set "external-spire-server.kubeConfigs.other.kubeConfigBase64=${OTHER_KCB64}" + +for cluster in child other; do + KC="${SCRIPTPATH}/kubeconfig-${cluster}" + kubectl --kubeconfig "${KC}" get configmap -n spire-system spire-bundle-upstream -o yaml + kubectl --kubeconfig "${KC}" rollout restart daemonset spire-agent-upstream -n spire-system + kubectl --kubeconfig "${KC}" rollout restart statefulset spire-internal-server -n spire-server + kubectl --kubeconfig "${KC}" rollout restart daemonset spire-agent-downstream -n spire-system + kubectl --kubeconfig "${KC}" rollout restart deployment spiffe-oidc-discovery-provider -n spire-server + kubectl --kubeconfig "${KC}" rollout status daemonset spire-agent-upstream -n spire-system --timeout 60s || kubectl logs --kubeconfig "${KC}" daemonset/spire-agent-upstream -n spire-system --prefix --all-containers=true + kubectl --kubeconfig "${KC}" rollout status statefulset spire-internal-server -n spire-server --timeout 60s || kubectl logs --kubeconfig "${KC}" statefulset/spire-internal-server -n spire-server --prefix --all-containers=true + kubectl --kubeconfig "${KC}" rollout status daemonset spire-agent-downstream -n spire-system --timeout 60s || kubectl logs --kubeconfig "${KC}" daemonset/spire-agent-downstream -n spire-system --prefix --all-containers=true + kubectl --kubeconfig "${KC}" rollout status deployment spiffe-oidc-discovery-provider -n spire-server --timeout 60s || kubectl logs --kubeconfig "${KC}" deployment/spiffe-oidc-discovery-provider -n spire-server --prefix --all-containers=true + + echo Pods on "${cluster}" + kubectl --kubeconfig "${KC}" get pods -A + + ENTRIES="$(kubectl --kubeconfig "${KC}" exec -i -n spire-server spire-internal-server-0 -- spire-server entry show)" + + if [[ "${ENTRIES}" == "Found 0 entries" ]]; then + echo "${ENTRIES}" + exit 1 + fi +done + +ENTRIES="$(kubectl exec -i -n spire-server spire-external-server-0 -- spire-server entry show)" + +if [[ "${ENTRIES}" == "Found 0 entries" ]]; then + echo "${ENTRIES}" + exit 1 +fi + +helm test --namespace spire-mgmt spire + +helm test --kubeconfig "${SCRIPTPATH}/kubeconfig-child" --namespace spire-mgmt spire +helm test --kubeconfig "${SCRIPTPATH}/kubeconfig-other" --namespace spire-mgmt spire + diff --git a/examples/nested-security/.test-files/child-kind-config.yaml b/examples/nested-security/.test-files/child-kind-config.yaml new file mode 100644 index 000000000..086e96bf0 --- /dev/null +++ b/examples/nested-security/.test-files/child-kind-config.yaml @@ -0,0 +1,7 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +networking: + apiServerAddress: "172.17.0.1" + apiServerPort: 7443 + podSubnet: "10.245.0.0/16" + serviceSubnet: "10.97.0.0/16" diff --git a/examples/nested-security/.test-files/other-kind-config.yaml b/examples/nested-security/.test-files/other-kind-config.yaml new file mode 100644 index 000000000..0204e7529 --- /dev/null +++ b/examples/nested-security/.test-files/other-kind-config.yaml @@ -0,0 +1,7 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +networking: + apiServerAddress: "172.17.0.1" + apiServerPort: 5443 + podSubnet: "10.246.0.0/16" + serviceSubnet: "10.98.0.0/16" diff --git a/examples/nested-security/child-values.yaml b/examples/nested-security/child-values.yaml new file mode 100644 index 000000000..6c5d27db1 --- /dev/null +++ b/examples/nested-security/child-values.yaml @@ -0,0 +1,12 @@ +# global: +# spire: +# clusterName: changeme + +tags: + nestedChildSecurity: true + +# downstream-spire-agent-security: +# serviceAccount: +# server: +# address: spire-server.changeme + diff --git a/examples/nested-security/root-values.yaml b/examples/nested-security/root-values.yaml new file mode 100644 index 000000000..d9066ece3 --- /dev/null +++ b/examples/nested-security/root-values.yaml @@ -0,0 +1,10 @@ +tags: + nestedRoot: true + +spiffe-oidc-discovery-provider: + ingress: + enabled: true + +external-spire-server: + ingress: + enabled: true diff --git a/examples/nested-security/run-tests.sh b/examples/nested-security/run-tests.sh new file mode 100755 index 000000000..ba139c0eb --- /dev/null +++ b/examples/nested-security/run-tests.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash + +set -xe + +SCRIPT="$(readlink -f "$0")" +SCRIPTPATH="$(dirname "${SCRIPT}")" +TESTDIR="${SCRIPTPATH}/../../.github/tests" +#DEPS="${TESTDIR}/dependencies" + +# shellcheck source=/dev/null +source "${SCRIPTPATH}/../../.github/scripts/parse-versions.sh" +# shellcheck source=/dev/null +source "${TESTDIR}/common.sh" + +CLEANUP=1 + +for i in "$@"; do + case $i in + -c) + CLEANUP=0 + shift # past argument=value + ;; + esac +done + +teardown() { + print_helm_releases + print_spire_workload_status spire-root-server + print_spire_workload_status spire-server spire-system + + if [[ "$1" -ne 0 ]]; then + get_namespace_details spire-root-server + get_namespace_details spire-server spire-system + fi + + if [ "${CLEANUP}" -eq 1 ]; then + helm uninstall --namespace spire-server spire 2>/dev/null || true + kubectl delete ns spire-server 2>/dev/null || true + kubectl delete ns spire-system 2>/dev/null || true + + helm uninstall --namespace mysql spire-root-server 2>/dev/null || true + kubectl delete ns spire-root-server 2>/dev/null || true + fi +} + +trap 'EC=$? && trap - SIGTERM && teardown $EC' SIGINT SIGTERM EXIT + +# Update deps +helm dep up charts/spire-nested + +# List nodes +kubectl get nodes + +# Deploy an ingress controller +IP=$(kubectl get nodes chart-testing-control-plane -o go-template='{{ range .status.addresses }}{{ if eq .type "InternalIP" }}{{ .address }}{{ end }}{{ end }}') +helm upgrade --install ingress-nginx ingress-nginx --version "$VERSION_INGRESS_NGINX" --repo "$HELM_REPO_INGRESS_NGINX" \ + --namespace ingress-nginx \ + --create-namespace \ + --set "controller.extraArgs.enable-ssl-passthrough=,controller.admissionWebhooks.enabled=false,controller.service.type=ClusterIP,controller.service.externalIPs[0]=$IP" \ + --set controller.ingressClassResource.default=true \ + --wait + +# Test the ingress controller. Should 404 as there is no services yet. +curl "$IP" + +kubectl get configmap -n kube-system coredns -o yaml | grep hosts || kubectl get configmap -n kube-system coredns -o yaml | sed "/ready/a\ hosts {\n fallthrough\n }" | kubectl apply -f - +kubectl get configmap -n kube-system coredns -o yaml | grep production.other || kubectl get configmap -n kube-system coredns -o yaml | sed "/hosts/a\ $IP oidc-discovery.production.other\n $IP spire-server.production.other\n" | kubectl apply -f - +kubectl rollout restart -n kube-system deployment/coredns +kubectl rollout status -n kube-system -w --timeout=1m deploy/coredns + +# The check is being too pedantic. +# shellcheck shell=bash disable=SC2043 +for cluster in child; do + KC="${SCRIPTPATH}/kubeconfig-${cluster}" + + kind create cluster --name "${cluster}" --kubeconfig "${SCRIPTPATH}/kubeconfig-${cluster}" --config "${SCRIPTPATH}/.test-files/${cluster}-kind-config.yaml" + md5sum "${KC}" + wc -l "${KC}" + + helm upgrade --kubeconfig "${KC}" --install --create-namespace --namespace spire-mgmt spire-crds charts/spire-crds + helm upgrade --kubeconfig "${KC}" --install --namespace spire-mgmt --values "${COMMON_TEST_YOUR_VALUES},${SCRIPTPATH}/child-values.yaml" \ + --set "downstream-spire-agent-security.server.address=spire-server.production.other" \ + --set "global.spire.namespaces.create=true" \ + --set "global.spire.clusterName=${cluster}" \ + spire charts/spire-nested + + kubectl get configmap --kubeconfig "${KC}" -n kube-system coredns -o yaml | grep hosts || kubectl get configmap --kubeconfig "${KC}" -n kube-system coredns -o yaml | sed "/ready/a\ hosts {\n fallthrough\n }" | kubectl apply --kubeconfig "${KC}" -f - + kubectl get configmap --kubeconfig "${KC}" -n kube-system coredns -o yaml | grep production.other || kubectl get configmap --kubeconfig "${KC}" -n kube-system coredns -o yaml | sed "/hosts/a\ $IP spire-server.production.other\n $IP spire-server.production.other\n" | kubectl apply --kubeconfig "${KC}" -f - + kubectl rollout restart --kubeconfig "${KC}" -n kube-system deployment/coredns + kubectl rollout status --kubeconfig "${KC}" -n kube-system -w --timeout=1m deploy/coredns +done + +docker exec -i child-control-plane bash -c 'kubeadm kubeconfig user --client-name=spire-root' > "${SCRIPTPATH}/child-spire-root.kubeconfig" +CHILD_KCB64="$(base64 < "${SCRIPTPATH}/child-spire-root.kubeconfig" | tr '\n' ' ' | sed 's/ //g')" + +helm upgrade --install --create-namespace --namespace spire-mgmt --values "${COMMON_TEST_YOUR_VALUES},${SCRIPTPATH}/root-values.yaml" \ + --wait spire charts/spire-nested \ + --set "global.spire.namespaces.create=true" \ + --set "global.spire.ingressControllerType=ingress-nginx" \ + --set "external-spire-server.kubeConfigs.child.kubeConfigBase64=${CHILD_KCB64}" + +# The check is being too pedantic. +# shellcheck shell=bash disable=SC2043 +for cluster in child; do + KC="${SCRIPTPATH}/kubeconfig-${cluster}" + kubectl --kubeconfig "${KC}" get configmap -n spire-system spire-bundle-upstream -o yaml + kubectl --kubeconfig "${KC}" rollout restart daemonset spire-agent-downstream -n spire-system + kubectl --kubeconfig "${KC}" rollout restart deployment spiffe-oidc-discovery-provider -n spire-server + kubectl --kubeconfig "${KC}" rollout status daemonset spire-agent-downstream -n spire-system --timeout 60s || kubectl logs --kubeconfig "${KC}" daemonset/spire-agent-downstream -n spire-system --prefix --all-containers=true + kubectl --kubeconfig "${KC}" rollout status deployment spiffe-oidc-discovery-provider -n spire-server --timeout 60s || kubectl logs --kubeconfig "${KC}" deployment/spiffe-oidc-discovery-provider -n spire-server --prefix --all-containers=true + + echo Pods on "${cluster}" + kubectl --kubeconfig "${KC}" get pods -A +done + +ENTRIES="$(kubectl exec -i -n spire-server spire-external-server-0 -- spire-server entry show)" + +if [[ "${ENTRIES}" == "Found 0 entries" ]]; then + echo "${ENTRIES}" + exit 1 +fi + +helm test --namespace spire-mgmt spire + +helm test --kubeconfig "${SCRIPTPATH}/kubeconfig-child" --namespace spire-mgmt spire + diff --git a/examples/nested/README.md b/examples/nested/README.md deleted file mode 100644 index 2b2986eff..000000000 --- a/examples/nested/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Possible Nesting Configurations - -There are multiple ways of configuring the chart depending on what you want to use nesting for. - -## Nesting across Kubernetes clusters: -![Multiple Kubernetes Cluster](./multicluster.png) - -## Nesting within a Kubernetes cluster: -![Single Hardened](./singlehardened.png) - diff --git a/examples/nested/multicluster.dot b/examples/nested/multicluster.dot deleted file mode 100644 index fb9283339..000000000 --- a/examples/nested/multicluster.dot +++ /dev/null @@ -1,61 +0,0 @@ -digraph G { - subgraph cluster_root { - label="Cluster: Root K8S"; - subgraph cluster_root_release { - label="Helm Release: Namespace=spire-root Name=spire" - spireRoot [label="Root Spire Server"]; - } - } - subgraph cluster_nested1 { - label="Cluster: K8S Workload 1"; - subgraph cluster_nested1_release { - label="Helm Release: Namespace=spire-server Name=spire" - subgraph cluster_nested1_ns1 { - label="Namespace: spire-system" - spireUpstreamAgent1 [label="Upstream Spire Agent/CSI"]; - } - subgraph cluster_nested1_ns2 { - label="Namespace: spire-server" - spireServerNested1 [label="Nested Spire Server"]; - } - subgraph cluster_nested1_ns3 { - label="Namespace: spire-system" - spireDownstreamAgent1 [label="Downstream Spire Agent/CSI"]; - } - } - subgraph cluster_nested1_user { - label="Namespace: user" - userWorkload1 [label="User Workload"]; - } - } - subgraph cluster_nested2 { - label="Cluster: K8S Workload 2"; - subgraph cluster_nested2_release { - label="Helm Release: Namespace=spire-server Name=spire" - subgraph cluster_nested2_ns1 { - label="Namespace: spire-system" - spireUpstreamAgent2 [label="Upstream Spire Agent/CSI"]; - } - subgraph cluster_nested2_ns2 { - label="Namespace: spire-server" - spireServerNested2 [label="Nested Spire Server"]; - } - subgraph cluster_nested2_ns3 { - label="Namespace: spire-system" - spireDownstreamAgent2 [label="Downstream Spire Agent/CSI"]; - } - } - subgraph cluster_nested2_user { - label="Namespace: user" - userWorkload2 [label="Other User Workload"]; - } - } - spireRoot -> spireUpstreamAgent1; - spireRoot -> spireUpstreamAgent2; - spireUpstreamAgent1 -> spireServerNested1; - spireServerNested1 -> spireDownstreamAgent1; - spireDownstreamAgent1 -> userWorkload1; - spireUpstreamAgent2 -> spireServerNested2; - spireServerNested2 -> spireDownstreamAgent2; - spireDownstreamAgent2 -> userWorkload2; -} diff --git a/examples/nested/multicluster.png b/examples/nested/multicluster.png deleted file mode 100644 index a0ffd8d3d2e7089ce1988a31d967fae3b29864e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 129722 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 diff --git a/examples/nested/run-tests.sh b/examples/nested/run-tests.sh deleted file mode 100755 index ee0dbe549..000000000 --- a/examples/nested/run-tests.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env bash - -set -xe - -SCRIPT="$(readlink -f "$0")" -SCRIPTPATH="$(dirname "${SCRIPT}")" -TESTDIR="${SCRIPTPATH}/../../.github/tests" -DEPS="${TESTDIR}/dependencies" - -# shellcheck source=/dev/null -source "${SCRIPTPATH}/../../.github/scripts/parse-versions.sh" -# shellcheck source=/dev/null -source "${TESTDIR}/common.sh" - -CLEANUP=1 - -for i in "$@"; do - case $i in - -c) - CLEANUP=0 - shift # past argument=value - ;; - esac -done - -teardown() { - print_helm_releases - print_spire_workload_status spire-root-server - print_spire_workload_status spire-server spire-system - - if [[ "$1" -ne 0 ]]; then - get_namespace_details spire-root-server - get_namespace_details spire-server spire-system - fi - - if [ "${CLEANUP}" -eq 1 ]; then - helm uninstall --namespace spire-server spire 2>/dev/null || true - kubectl delete ns spire-server 2>/dev/null || true - kubectl delete ns spire-system 2>/dev/null || true - - helm uninstall --namespace mysql spire-root-server 2>/dev/null || true - kubectl delete ns spire-root-server 2>/dev/null || true - fi -} - -trap 'EC=$? && trap - SIGTERM && teardown $EC' SIGINT SIGTERM EXIT - -kubectl create namespace spire-system --dry-run=client -o yaml | kubectl apply -f - -kubectl label namespace spire-system pod-security.kubernetes.io/enforce=privileged || true -kubectl create namespace spire-server --dry-run=client -o yaml | kubectl apply -f - -kubectl label namespace spire-server pod-security.kubernetes.io/enforce=restricted || true - -helm upgrade --install --create-namespace spire charts/spire \ - --namespace spire-root-server \ - --values "${DEPS}/spire-root-server-values.yaml" \ - --wait - -helm upgrade --install --create-namespace --namespace spire-server --values "${COMMON_TEST_YOUR_VALUES},${SCRIPTPATH}/values.yaml,${SCRIPTPATH}/../misc/values-node-pod-antiaffinity.yaml" \ - --wait spire charts/spire -helm test --namespace spire-server spire - diff --git a/examples/nested/singlehardened.dot b/examples/nested/singlehardened.dot deleted file mode 100644 index 9f3ac97a6..000000000 --- a/examples/nested/singlehardened.dot +++ /dev/null @@ -1,55 +0,0 @@ -digraph G { - subgraph cluster_baremetal { - label="(Bare Metal|Virtual) Node" - spireDownstreamAgent3 [label="Downstream Spire Agent"]; - userWorkload3 [label="External User Workload"]; - } - subgraph cluster_k8s { - label="Cluster: K8S"; - subgraph cluster_root_release { - label="Helm Release: Namespace=spire-root Name=spire"; - subgraph cluster_ns_root { - label="Namespace: spire-root" - spireRoot [label="Root Spire Server"]; - } - subgraph cluster_ns_1_system { - label="Namespace: spire-system" - spireUpstreamAgent1 [label="Upstream Spire Agent/CSI"]; - } - } - subgraph cluster_nested1_release { - label="Helm Release: Namespace=spire-server Name=spire" - subgraph cluster_ns_nested_server { - label="Namespace: spire-server"; - spireServerNested1 [label="Internal Nested Spire Server"]; - } - subgraph cluster_ns_nested_system { - label="Namespace: spire-system"; - spireDownstreamAgent1 [label="Downstream Spire Agent/CSI"]; - } - } - subgraph cluster_ns_nested_system { - label="Namespace: user"; - userWorkload1 [label="User Workload"]; - } - subgraph cluster_ns_nested2_system { - label="Namespace: user-other"; - userWorkload2 [label="Other User Workload"]; - } - subgraph cluster_nested3_release { - label="Helm Release: Namespace=spire-external Name=spire" - subgraph cluster_ns_nested2_system { - label="Namespace: spire-external"; - spireServerNested2 [label="External Nested Spire Server"]; - } - } - spireRoot -> spireUpstreamAgent1; - spireUpstreamAgent1 -> spireServerNested1; - spireServerNested1 -> spireDownstreamAgent1; - spireDownstreamAgent1 -> userWorkload1; - spireDownstreamAgent1 -> userWorkload2; - spireUpstreamAgent1 -> spireServerNested2; - spireServerNested2 -> spireDownstreamAgent3; - spireDownstreamAgent3 -> userWorkload3; - } -} diff --git a/examples/nested/singlehardened.png b/examples/nested/singlehardened.png deleted file mode 100644 index 7b161bc9f97a29b5ccad6a3c11a7657139ee4fdc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 110148 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፵cydV8TFKR@T%Jw(F1gAw4ER&u)TH?*8RIMGTo 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/dev/null || true - kubectl delete ns spire-server 2>/dev/null || true - kubectl delete ns spire-system 2>/dev/null || true - - helm uninstall --namespace mysql spire-root-server 2>/dev/null || true - kubectl delete ns spire-root-server 2>/dev/null || true - fi -} - -trap 'EC=$? && trap - SIGTERM && teardown $EC' SIGINT SIGTERM EXIT - -#helm upgrade --install --create-namespace spire charts/spire \ -# --namespace spire-root-server \ -# --values "${DEPS}/spire-root-server-values.yaml" \ -# --wait - -kind create cluster --name child --kubeconfig "${SCRIPTPATH}/kubeconfig-child" --config "${SCRIPTPATH}/child-kind-config.yaml" -md5sum "${SCRIPTPATH}/kubeconfig-child" -wc -l "${SCRIPTPATH}/kubeconfig-child" -CHILD_KCB64="$(base64 < "${SCRIPTPATH}/kubeconfig-child" | tr '\n' ' ' | sed 's/ //g')" - -helm upgrade --kubeconfig "${SCRIPTPATH}/kubeconfig-child" --install --create-namespace --namespace spire-mgmt spire-crds charts/spire-crds -kubectl --kubeconfig "${SCRIPTPATH}/kubeconfig-child" apply -f "${SCRIPTPATH}/sodp-clusterspiffeid.yaml" -helm upgrade --kubeconfig "${SCRIPTPATH}/kubeconfig-child" --install --namespace spire-mgmt --values "${SCRIPTPATH}/child-values.yaml" \ - spire charts/spire -kubectl --kubeconfig "${SCRIPTPATH}/kubeconfig-child" create configmap -n spire-system spire-bundle-upstream - -kind create cluster --name other --kubeconfig "${SCRIPTPATH}/kubeconfig-other" --config "${SCRIPTPATH}/other-kind-config.yaml" -md5sum "${SCRIPTPATH}/kubeconfig-other" -wc -l "${SCRIPTPATH}/kubeconfig-other" -OTHER_KCB64="$(base64 < "${SCRIPTPATH}/kubeconfig-other" | tr '\n' ' ' | sed 's/ //g')" - -helm upgrade --kubeconfig "${SCRIPTPATH}/kubeconfig-other" --install --create-namespace --namespace spire-mgmt spire-crds charts/spire-crds -kubectl --kubeconfig "${SCRIPTPATH}/kubeconfig-other" apply -f "${SCRIPTPATH}/sodp-clusterspiffeid.yaml" -helm upgrade --kubeconfig "${SCRIPTPATH}/kubeconfig-other" --install --namespace spire-mgmt --values "${SCRIPTPATH}/child-values.yaml" \ - spire charts/spire -kubectl --kubeconfig "${SCRIPTPATH}/kubeconfig-other" create configmap -n spire-system spire-bundle-upstream - -helm upgrade --install --create-namespace --namespace spire-mgmt --values "${SCRIPTPATH}/values.yaml" \ - --wait spire charts/spire \ - --set "spire-server.kubeConfigs.child.kubeConfigBase64=${CHILD_KCB64}" \ - --set "spire-server.kubeConfigs.other.kubeConfigBase64=${OTHER_KCB64}" -helm test --namespace spire-mgmt spire -kubectl --kubeconfig "${SCRIPTPATH}/kubeconfig-child" get configmap -n spire-system spire-bundle-upstream -kubectl --kubeconfig "${SCRIPTPATH}/kubeconfig-other" get configmap -n spire-system spire-bundle-upstream - -ENTRIES="$(kubectl exec -i -n spire-server spire-server-0 -- spire-server entry show)" - -if [[ "${ENTRIES}" == "Found 0 entries" ]]; then - echo "${ENTRIES}" - exit 1 -fi - diff --git a/tests/integration/psat/sodp-clusterspiffeid.yaml b/tests/integration/psat/sodp-clusterspiffeid.yaml deleted file mode 100644 index 2e3de5f1e..000000000 --- a/tests/integration/psat/sodp-clusterspiffeid.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: spire.spiffe.io/v1alpha1 -kind: ClusterSPIFFEID -metadata: - name: spire-mgmt-spire-oidc-discovery-provider -spec: - autoPopulateDNSNames: true - className: spire-mgmt-spire - dnsNameTemplates: - - oidc-discovery.{{ .TrustDomain }} - namespaceSelector: - matchExpressions: - - key: kubernetes.io/metadata.name - operator: In - values: - - spire-mgmt - - spire-server - - spire-system - podSelector: - matchLabels: - component: oidc-discovery-provider - release: spire - release-namespace: spire-mgmt - spiffeIDTemplate: spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }} diff --git a/tests/integration/psat/values.yaml b/tests/integration/psat/values.yaml deleted file mode 100644 index 1144d71ca..000000000 --- a/tests/integration/psat/values.yaml +++ /dev/null @@ -1,30 +0,0 @@ -global: - spire: - recommendations: - enabled: true - namespaces: - create: true - clusterName: production - trustDomain: production.other - caSubject: - country: US - organization: Production - commonName: production.other - -spire-server: - controllerManager: - reconcile: - clusterSPIFFEIDs: false - clusterStaticEntries: true - clusterFederatedTrustDomains: true - identities: - clusterSPIFFEIDs: - default: - enabled: false - oidc-discovery-provider: - enabled: false - test-keys: - enabled: false - -spiffe-oidc-discovery-provider: - enabled: false From f613d1ad5ba7748e391c3fb9a5f644cdd85d74d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 May 2024 00:15:28 -0700 Subject: [PATCH 29/37] --- (#360) --- tests/go.mod | 14 +++++++------- tests/go.sum | 28 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/tests/go.mod b/tests/go.mod index 3aac27959..b25ba7d8e 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -4,7 +4,7 @@ go 1.21 toolchain go1.22.2 require ( - github.com/onsi/ginkgo/v2 v2.17.3 + github.com/onsi/ginkgo/v2 v2.18.0 github.com/onsi/gomega v1.33.1 helm.sh/helm/v3 v3.15.0 ) @@ -46,14 +46,14 @@ require ( github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect - golang.org/x/crypto v0.22.0 // indirect - golang.org/x/net v0.24.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/net v0.25.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/term v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/term v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.20.0 // indirect + golang.org/x/tools v0.21.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/tests/go.sum b/tests/go.sum index aa29cb137..da82bc4b2 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -83,8 +83,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.17.3 h1:oJcvKpIb7/8uLpDDtnQuf18xVnwKp8DTD7DQ6gTd/MU= -github.com/onsi/ginkgo/v2 v2.17.3/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= +github.com/onsi/ginkgo/v2 v2.18.0 h1:W9Y7IWXxPUpAit9ieMOLI7PJZGaW22DTKgiVAuhDTLc= +github.com/onsi/ginkgo/v2 v2.18.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -127,8 +127,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -140,8 +140,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -156,20 +156,20 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= -golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -177,8 +177,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= -golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= +golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From d2361549dbb983ca7db67d3792fdc36d86544c72 Mon Sep 17 00:00:00 2001 From: kfox1111 Date: Thu, 23 May 2024 09:43:19 -0700 Subject: [PATCH 30/37] Fix upstream ca name suffix issue (#361) * Fix upstream ca name suffix issue Signed-off-by: Kevin Fox * Fix quoting Signed-off-by: Kevin Fox --------- Signed-off-by: Kevin Fox --- charts/spire/README.md | 5 +++++ charts/spire/charts/spire-server/templates/configmap.yaml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/charts/spire/README.md b/charts/spire/README.md index bd0a3006e..b96d91038 100644 --- a/charts/spire/README.md +++ b/charts/spire/README.md @@ -75,6 +75,11 @@ kubectl delete crds clusterfederatedtrustdomains.spire.spiffe.io clusterspiffeid We only support upgrading one major version at a time. Version skipping isn't supported. +### 0.21.X + +- In previous versions, spire-server.upstreamAuthority.certManager.issuer_name would incorrectly have '-ca' appended. Starting with this version, that is no longer the case. If you previously set this +value, you likely want to update your value to include the '-ca' suffix in the value to have your deployment continue to function properly. + ### 0.20.X - The default service port for the spire-server was changed to be port 443 to allow easier switching between internal access and external access through an ingress controller. For most users, this will be a transparent diff --git a/charts/spire/charts/spire-server/templates/configmap.yaml b/charts/spire/charts/spire-server/templates/configmap.yaml index dc1210867..bdd9d8fc0 100644 --- a/charts/spire/charts/spire-server/templates/configmap.yaml +++ b/charts/spire/charts/spire-server/templates/configmap.yaml @@ -269,7 +269,7 @@ plugins: UpstreamAuthority: cert-manager: plugin_data: - issuer_name: {{ default (include "spire-server.fullname" $root) .issuer_name }}-ca + issuer_name: {{ default (printf "%s-ca" (include "spire-server.fullname" $root)) .issuer_name }} issuer_kind: {{ .issuer_kind | quote }} issuer_group: {{ .issuer_group | quote }} namespace: {{ default $root.Release.Namespace .namespace | quote }} From db177d4b85375eba7cfc39957ee9bbed562cf873 Mon Sep 17 00:00:00 2001 From: Mariusz Sabath Date: Thu, 23 May 2024 15:26:44 -0400 Subject: [PATCH 31/37] Fix spelling error in Controller Manager config (#362) Signed-off-by: Mariusz Sabath --- .../spire-server/templates/controller-manager-configmap.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/spire/charts/spire-server/templates/controller-manager-configmap.yaml b/charts/spire/charts/spire-server/templates/controller-manager-configmap.yaml index bcfa7ffbf..f5bad18bf 100644 --- a/charts/spire/charts/spire-server/templates/controller-manager-configmap.yaml +++ b/charts/spire/charts/spire-server/templates/controller-manager-configmap.yaml @@ -70,7 +70,7 @@ clusterName: {{ .clusterName }} trustDomain: {{ include "spire-lib.trust-domain" . }} {{- $ignoreNamespaces := .defaults.ignoreNamespaces }} {{- if hasKey .settings "ignoreNamespaces" }} -{{- $ignoreNamespaces = .settings.ingoreNamespaces }} +{{- $ignoreNamespaces = .settings.ignoreNamespaces }} {{- end }} {{- with $ignoreNamespaces }} ignoreNamespaces: From 7dabbf16d37eda6c73f63b98d628d02641f40308 Mon Sep 17 00:00:00 2001 From: Mariusz Sabath Date: Sun, 26 May 2024 10:55:26 -0400 Subject: [PATCH 32/37] Add Openshift ignore namespaces to Controller Manager (#363) * Fix spelling error in Controller Manager config Signed-off-by: Mariusz Sabath * Add ignoreNamespaces to ControllerManager for Openshift Signed-off-by: Mariusz Sabath --------- Signed-off-by: Mariusz Sabath --- charts/spire/charts/spire-server/values.yaml | 50 ++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index 4f0d0e399..67b0a4148 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -540,6 +540,31 @@ controllerManager: - kube-system - kube-public - local-path-storage + # openshift related namespaces that should be typically ignored + - openshift-cluster-node-tuning-operator + - openshift-cluster-samples-operator + - openshift-cluster-storage-operator + - openshift-console-operator + - openshift-console + - openshift-dns + - openshift-dns-operator + - openshift-image-registry + - openshift-ingress + - openshift-kube-storage-version-migrator + - openshift-kube-storage-version-migrator-operator + - openshift-kube-proxy + - openshift-marketplace + - openshift-monitoring + - openshift-multus + - openshift-network-diagnostics + - openshift-network-operator + - openshift-operator-lifecycle-manager + - openshift-roks-metrics + - openshift-service-ca-operator + - openshift-service-ca + # ibmcloud specific namespaces + - ibm-odf-validation-webhook + - ibm-system ## @param controllerManager.reconcile.clusterSPIFFEIDs Enable reconciliation of clusterSPIFFEIDs from K8s to the SPIRE server ## @param controllerManager.reconcile.clusterStaticEntries Enable reconciliation of clusterStaticEntries from K8s to the SPIRE server @@ -693,6 +718,31 @@ externalControllerManagers: - kube-system - kube-public - local-path-storage + # openshift related namespaces that should be typically ignored + - openshift-cluster-node-tuning-operator + - openshift-cluster-samples-operator + - openshift-cluster-storage-operator + - openshift-console-operator + - openshift-console + - openshift-dns + - openshift-dns-operator + - openshift-image-registry + - openshift-ingress + - openshift-kube-storage-version-migrator + - openshift-kube-storage-version-migrator-operator + - openshift-kube-proxy + - openshift-marketplace + - openshift-monitoring + - openshift-multus + - openshift-network-diagnostics + - openshift-network-operator + - openshift-operator-lifecycle-manager + - openshift-roks-metrics + - openshift-service-ca-operator + - openshift-service-ca + # ibmcloud specific namespaces + - ibm-odf-validation-webhook + - ibm-system ## @param externalControllerManagers.defaults.cacheNamespaces [object] If specified restricts the manager's cache to watch objects in the desired namespaces. Defaults to all namespaces. cacheNamespaces: {} From 6ff84393fc5538f837b4b0c863c1df4fe37db553 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 26 May 2024 15:03:39 +0000 Subject: [PATCH 33/37] Bump helm.sh/helm/v3 from 3.15.0 to 3.15.1 in /tests (#365) Bumps [helm.sh/helm/v3](https://github.com/helm/helm) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/helm/helm/releases) - [Commits](https://github.com/helm/helm/compare/v3.15.0...v3.15.1) --- updated-dependencies: - dependency-name: helm.sh/helm/v3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/go.mod | 2 +- tests/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/go.mod b/tests/go.mod index b25ba7d8e..94cb61004 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -6,7 +6,7 @@ toolchain go1.22.2 require ( github.com/onsi/ginkgo/v2 v2.18.0 github.com/onsi/gomega v1.33.1 - helm.sh/helm/v3 v3.15.0 + helm.sh/helm/v3 v3.15.1 ) require ( diff --git a/tests/go.sum b/tests/go.sum index da82bc4b2..334aac47a 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -201,8 +201,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -helm.sh/helm/v3 v3.15.0 h1:gcLxHeFp0Hfo7lYi6KIZ84ZyvlAnfFRSJ8lTL3zvG5U= -helm.sh/helm/v3 v3.15.0/go.mod h1:fvfoRcB8UKRUV5jrIfOTaN/pG1TPhuqSb56fjYdTKXg= +helm.sh/helm/v3 v3.15.1 h1:22ztacHz4gMqhXNqCQ9NAg6BFWoRUryNLvnkz6OVyw0= +helm.sh/helm/v3 v3.15.1/go.mod h1:fvfoRcB8UKRUV5jrIfOTaN/pG1TPhuqSb56fjYdTKXg= k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA= k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE= k8s.io/apiextensions-apiserver v0.30.0 h1:jcZFKMqnICJfRxTgnC4E+Hpcq8UEhT8B2lhBcQ+6uAs= From 39084afa7139d6516cb8b764a35658fd385f002f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 May 2024 03:42:08 +0000 Subject: [PATCH 34/37] Bump github.com/onsi/ginkgo/v2 from 2.18.0 to 2.19.0 in /tests Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.18.0 to 2.19.0. - [Release notes](https://github.com/onsi/ginkgo/releases) - [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/ginkgo/compare/v2.18.0...v2.19.0) --- updated-dependencies: - dependency-name: github.com/onsi/ginkgo/v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- tests/go.mod | 2 +- tests/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/go.mod b/tests/go.mod index 94cb61004..e292ca58d 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -4,7 +4,7 @@ go 1.21 toolchain go1.22.2 require ( - github.com/onsi/ginkgo/v2 v2.18.0 + github.com/onsi/ginkgo/v2 v2.19.0 github.com/onsi/gomega v1.33.1 helm.sh/helm/v3 v3.15.1 ) diff --git a/tests/go.sum b/tests/go.sum index 334aac47a..d405560e9 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -83,8 +83,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.18.0 h1:W9Y7IWXxPUpAit9ieMOLI7PJZGaW22DTKgiVAuhDTLc= -github.com/onsi/ginkgo/v2 v2.18.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= +github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= +github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= From d29ad0649f7a05cff5db813a4cc440a7e7cf0913 Mon Sep 17 00:00:00 2001 From: "spire-helm-version-checker[bot]" <161522935+spire-helm-version-checker[bot]@users.noreply.github.com> Date: Mon, 27 May 2024 06:36:36 -0700 Subject: [PATCH 35/37] Bump test chart dependencies (#369) * Bump test chart dependencies Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Remove workaround as they fixed curl Signed-off-by: Kevin Fox --------- Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Signed-off-by: Kevin Fox Co-authored-by: marcofranssen <694733+marcofranssen@users.noreply.github.com> Co-authored-by: Kevin Fox --- .github/tests/charts.json | 6 +++--- .../spire/charts/spiffe-oidc-discovery-provider/README.md | 4 ++-- .../spire/charts/spiffe-oidc-discovery-provider/values.yaml | 4 ++-- charts/spire/charts/spire-agent/README.md | 4 ++-- charts/spire/charts/spire-agent/values.yaml | 4 ++-- charts/spire/charts/spire-server/README.md | 2 +- .../spire-server/templates/tests/test-connection.yaml | 4 ++-- charts/spire/charts/spire-server/values.yaml | 2 +- charts/spire/charts/tornjak-frontend/README.md | 2 +- charts/spire/charts/tornjak-frontend/values.yaml | 2 +- 10 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/tests/charts.json b/.github/tests/charts.json index 03c3018c2..da053ad9c 100644 --- a/.github/tests/charts.json +++ b/.github/tests/charts.json @@ -2,7 +2,7 @@ { "name": "kube-prometheus-stack", "repo": "https://prometheus-community.github.io/helm-charts", - "version": "58.6.0" + "version": "58.7.2" }, { "name": "cert-manager", @@ -17,11 +17,11 @@ { "name": "mysql", "repo": "https://charts.bitnami.com/bitnami", - "version": "10.2.4" + "version": "11.0.0" }, { "name": "postgresql", "repo": "https://charts.bitnami.com/bitnami", - "version": "15.3.3" + "version": "15.4.0" } ] diff --git a/charts/spire/charts/spiffe-oidc-discovery-provider/README.md b/charts/spire/charts/spiffe-oidc-discovery-provider/README.md index 26c82dae4..ed121b9c6 100644 --- a/charts/spire/charts/spiffe-oidc-discovery-provider/README.md +++ b/charts/spire/charts/spiffe-oidc-discovery-provider/README.md @@ -115,11 +115,11 @@ A Helm chart to install the SPIFFE OIDC discovery provider. | `tests.bash.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.bash.image.repository` | The repository within the registry | `chainguard/bash` | | `tests.bash.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a996ae2a4dc5601e29821e8f1bfc3ea886f3e544204042b0ed103be697a2b0eb` | +| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:8c9e5cbb641ced8112c637eb3611dab29bf65448a9d884a03938baf1b352dc4d` | | `tests.toolkit.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.toolkit.image.repository` | The repository within the registry | `chainguard/min-toolkit-debug` | | `tests.toolkit.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.toolkit.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:c1f66766ac8d0a5bd1f8d1ed741e3c6584019fa9537697a97ac003d8d604493f` | +| `tests.toolkit.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:d94454739d8be0239cfe93453df79c88d25d38b7a97084d81a49e9403a90d07c` | | `tests.step.image.registry` | The OCI registry to pull the image from | `docker.io` | | `tests.step.image.repository` | The repository within the registry | `smallstep/step-cli` | | `tests.step.image.pullPolicy` | The image pull policy | `IfNotPresent` | diff --git a/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml b/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml index 6523bb5d4..b43b3df35 100644 --- a/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml +++ b/charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml @@ -328,7 +328,7 @@ tests: registry: cgr.dev repository: chainguard/bash pullPolicy: IfNotPresent - tag: latest@sha256:a996ae2a4dc5601e29821e8f1bfc3ea886f3e544204042b0ed103be697a2b0eb + tag: latest@sha256:8c9e5cbb641ced8112c637eb3611dab29bf65448a9d884a03938baf1b352dc4d toolkit: ## @param tests.toolkit.image.registry The OCI registry to pull the image from @@ -340,7 +340,7 @@ tests: registry: cgr.dev repository: chainguard/min-toolkit-debug pullPolicy: IfNotPresent - tag: latest@sha256:c1f66766ac8d0a5bd1f8d1ed741e3c6584019fa9537697a97ac003d8d604493f + tag: latest@sha256:d94454739d8be0239cfe93453df79c88d25d38b7a97084d81a49e9403a90d07c step: ## @param tests.step.image.registry The OCI registry to pull the image from diff --git a/charts/spire/charts/spire-agent/README.md b/charts/spire/charts/spire-agent/README.md index 5b84acc09..b609866e1 100644 --- a/charts/spire/charts/spire-agent/README.md +++ b/charts/spire/charts/spire-agent/README.md @@ -70,7 +70,7 @@ A Helm chart to install the SPIRE agent. | `fsGroupFix.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `fsGroupFix.image.repository` | The repository within the registry | `chainguard/bash` | | `fsGroupFix.image.pullPolicy` | The image pull policy | `Always` | -| `fsGroupFix.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a996ae2a4dc5601e29821e8f1bfc3ea886f3e544204042b0ed103be697a2b0eb` | +| `fsGroupFix.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:8c9e5cbb641ced8112c637eb3611dab29bf65448a9d884a03938baf1b352dc4d` | | `fsGroupFix.resources` | Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | `{}` | | `keyManager.memory.enabled` | Enable the memory based Key Manager | `true` | | `nodeAttestor.k8sPsat.enabled` | Enable Psat k8s Node Attestor | `true` | @@ -108,7 +108,7 @@ A Helm chart to install the SPIRE agent. | `socketAlternate.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `socketAlternate.image.repository` | The repository within the registry | `chainguard/bash` | | `socketAlternate.image.pullPolicy` | The image pull policy | `Always` | -| `socketAlternate.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a996ae2a4dc5601e29821e8f1bfc3ea886f3e544204042b0ed103be697a2b0eb` | +| `socketAlternate.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:8c9e5cbb641ced8112c637eb3611dab29bf65448a9d884a03938baf1b352dc4d` | | `socketAlternate.resources` | Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | `{}` | | `priorityClassName` | Priority class assigned to daemonset pods. Can be auto set with global.recommendations.priorityClassName. | `""` | | `extraEnvVars` | Extra environment variables to be added to the Spire Agent container | `[]` | diff --git a/charts/spire/charts/spire-agent/values.yaml b/charts/spire/charts/spire-agent/values.yaml index 9d25c7edf..d7f962e5d 100644 --- a/charts/spire/charts/spire-agent/values.yaml +++ b/charts/spire/charts/spire-agent/values.yaml @@ -154,7 +154,7 @@ fsGroupFix: registry: cgr.dev repository: chainguard/bash pullPolicy: Always - tag: latest@sha256:a996ae2a4dc5601e29821e8f1bfc3ea886f3e544204042b0ed103be697a2b0eb + tag: latest@sha256:8c9e5cbb641ced8112c637eb3611dab29bf65448a9d884a03938baf1b352dc4d ## @param fsGroupFix.resources Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ resources: {} @@ -266,7 +266,7 @@ socketAlternate: registry: cgr.dev repository: chainguard/bash pullPolicy: Always - tag: latest@sha256:a996ae2a4dc5601e29821e8f1bfc3ea886f3e544204042b0ed103be697a2b0eb + tag: latest@sha256:8c9e5cbb641ced8112c637eb3611dab29bf65448a9d884a03938baf1b352dc4d ## @param socketAlternate.resources Specify resource needs as per https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ resources: {} diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index cecdcd68d..6ec0c04a0 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -404,5 +404,5 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `tests.bash.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.bash.image.repository` | The repository within the registry | `chainguard/bash` | | `tests.bash.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a996ae2a4dc5601e29821e8f1bfc3ea886f3e544204042b0ed103be697a2b0eb` | +| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:8c9e5cbb641ced8112c637eb3611dab29bf65448a9d884a03938baf1b352dc4d` | | `kubeConfigs` | Manage additional kubeconfig files to talk to external Kubernetes clusters | `{}` | diff --git a/charts/spire/charts/spire-server/templates/tests/test-connection.yaml b/charts/spire/charts/spire-server/templates/tests/test-connection.yaml index e4f33b0ef..57b4434ba 100644 --- a/charts/spire/charts/spire-server/templates/tests/test-connection.yaml +++ b/charts/spire/charts/spire-server/templates/tests/test-connection.yaml @@ -25,8 +25,8 @@ spec: curl -k -f -s 'https://{{ include "spire-server.fullname" . }}:{{ .Values.service.port }}' IGNORECA=$? echo $NOCA $IGNORECA - if [ $NOCA -eq 60 -a $IGNORECA -eq 56 ]; then - # We were able to connect to the server but didn't recognize the ca (60) and the page not found (56) because we're not using grpc + if [ $NOCA -eq 60 -a $IGNORECA -eq 22 ]; then + # We were able to connect to the server but didn't recognize the ca (60) and the page not found (22) because we're not using grpc exit 0 fi exit 1 diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index 67b0a4148..427be889e 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -1063,7 +1063,7 @@ tests: registry: cgr.dev repository: chainguard/bash pullPolicy: IfNotPresent - tag: latest@sha256:a996ae2a4dc5601e29821e8f1bfc3ea886f3e544204042b0ed103be697a2b0eb + tag: latest@sha256:8c9e5cbb641ced8112c637eb3611dab29bf65448a9d884a03938baf1b352dc4d ## @param kubeConfigs [object] Manage additional kubeconfig files to talk to external Kubernetes clusters kubeConfigs: {} diff --git a/charts/spire/charts/tornjak-frontend/README.md b/charts/spire/charts/tornjak-frontend/README.md index 7f2e6db3c..0b8a46f5f 100644 --- a/charts/spire/charts/tornjak-frontend/README.md +++ b/charts/spire/charts/tornjak-frontend/README.md @@ -101,4 +101,4 @@ port forwarding. See the chart NOTES output for more details. | `tests.bash.image.registry` | The OCI registry to pull the image from | `cgr.dev` | | `tests.bash.image.repository` | The repository within the registry | `chainguard/bash` | | `tests.bash.image.pullPolicy` | The image pull policy | `IfNotPresent` | -| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:a996ae2a4dc5601e29821e8f1bfc3ea886f3e544204042b0ed103be697a2b0eb` | +| `tests.bash.image.tag` | Overrides the image tag whose default is the chart appVersion | `latest@sha256:8c9e5cbb641ced8112c637eb3611dab29bf65448a9d884a03938baf1b352dc4d` | diff --git a/charts/spire/charts/tornjak-frontend/values.yaml b/charts/spire/charts/tornjak-frontend/values.yaml index 25a8b0807..d3f71c59f 100644 --- a/charts/spire/charts/tornjak-frontend/values.yaml +++ b/charts/spire/charts/tornjak-frontend/values.yaml @@ -162,4 +162,4 @@ tests: registry: cgr.dev repository: chainguard/bash pullPolicy: IfNotPresent - tag: latest@sha256:a996ae2a4dc5601e29821e8f1bfc3ea886f3e544204042b0ed103be697a2b0eb + tag: latest@sha256:8c9e5cbb641ced8112c637eb3611dab29bf65448a9d884a03938baf1b352dc4d From 54a2f036bf72c0e9b7cf71dbcf4ce885277b58fd Mon Sep 17 00:00:00 2001 From: kfox1111 Date: Thu, 30 May 2024 12:22:03 -0700 Subject: [PATCH 36/37] Change cleanup default (#349) * Change cleanup default Signed-off-by: Kevin Fox * Update charts/spire/README.md Co-authored-by: Faisal Memon Signed-off-by: kfox1111 * Update charts/spire/README.md Signed-off-by: kfox1111 * Update charts/spire/README.md Co-authored-by: Faisal Memon Signed-off-by: kfox1111 * Fix merge issue and incorperate feedback. Signed-off-by: Kevin Fox --------- Signed-off-by: Kevin Fox Signed-off-by: kfox1111 Co-authored-by: Faisal Memon --- charts/spire/README.md | 4 +++- charts/spire/charts/spire-server/README.md | 2 +- charts/spire/charts/spire-server/values.yaml | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/charts/spire/README.md b/charts/spire/README.md index b96d91038..a7830d79e 100644 --- a/charts/spire/README.md +++ b/charts/spire/README.md @@ -73,13 +73,15 @@ kubectl delete crds clusterfederatedtrustdomains.spire.spiffe.io clusterspiffeid ## Upgrade notes -We only support upgrading one major version at a time. Version skipping isn't supported. +We only support upgrading one major/minor version at a time. Version skipping isn't supported. Please see https://spiffe.io/docs/latest/spire-helm-charts-hardened-about/upgrading/ for details. ### 0.21.X - In previous versions, spire-server.upstreamAuthority.certManager.issuer_name would incorrectly have '-ca' appended. Starting with this version, that is no longer the case. If you previously set this value, you likely want to update your value to include the '-ca' suffix in the value to have your deployment continue to function properly. +- The default value of spire-server.controllerManager.entryIDPrefixCleanup changed from "" to false. Prior to this release upgrades cleaned up old entries in the database. After upgrading to 0.21.X, manual entries will not be overridden by the spire-controller-manager. Skipping over chart releases (unsupported), requires manual setting of this value to "" to trigger the cleanup. + ### 0.20.X - The default service port for the spire-server was changed to be port 443 to allow easier switching between internal access and external access through an ingress controller. For most users, this will be a transparent diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index 6ec0c04a0..4536103c1 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -240,7 +240,7 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `controllerManager.enabled` | Flag to enable controller manager | `false` | | `controllerManager.className` | specify to use an explicit class name. If empty, it will be automatically set to Release.Namespace-Release.Name to not conflict with other installs, enabling parallel installs. | `""` | | `controllerManager.watchClassless` | specify to process custom resources without class name specified. Useful to slowly migrate to class names from classless installs. Do not have two installs on the same k8s cluster both set to true. | `false` | -| `controllerManager.entryIDPrefixCleanup` | Sets which entry prefixes to remove for migrations. Consult the spiffe.io docs about this option before changing. Its unlikely you will need to ever change it. | `""` | +| `controllerManager.entryIDPrefixCleanup` | Sets which entry prefixes to remove for migrations. Consult the spiffe.io docs about this option before changing. Its unlikely you will need to ever change it. | `false` | | `controllerManager.parentIDTemplate` | The template that is used to register workloads. | `spiffe://{{ .TrustDomain }}/spire/agent/k8s_psat/{{ .ClusterName }}/{{ .NodeMeta.UID }}` | | `controllerManager.expandEnv` | Set to true to enable environment variable substitution of config file options | `false` | | `controllerManager.extraEnv` | Extra environment variables to add to the controller manager | `[]` | diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index 427be889e..da3b8a0de 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -471,7 +471,7 @@ controllerManager: watchClassless: false ## @param controllerManager.entryIDPrefixCleanup Sets which entry prefixes to remove for migrations. Consult the spiffe.io docs about this option before changing. Its unlikely you will need to ever change it. - entryIDPrefixCleanup: "" + entryIDPrefixCleanup: false ## @param controllerManager.parentIDTemplate The template that is used to register workloads. parentIDTemplate: "spiffe://{{ .TrustDomain }}/spire/agent/k8s_psat/{{ .ClusterName }}/{{ .NodeMeta.UID }}" From 63853f54947a3793519f889d52a5c9f7ae5070ce Mon Sep 17 00:00:00 2001 From: Faisal Memon Date: Thu, 30 May 2024 15:00:00 -0700 Subject: [PATCH 37/37] Bump spire Helm Chart version from 0.20.0 to 0.21.0 (#373) * 54a2f03 Change cleanup default (#349) * d29ad06 Bump test chart dependencies (#369) * 7dabbf1 Add Openshift ignore namespaces to Controller Manager (#363) * db177d4 Fix spelling error in Controller Manager config (#362) * d236154 Fix upstream ca name suffix issue (#361) * bfcf418 Bump test chart dependencies (#359) * c31a2e9 Bump up spire to 1.9.6 (#356) * 2c5dfa0 Improve Tornjak NOTES. Fixes #132 (#354) * a453a2c Bump test chart dependencies (#355) * b6575c1 Update Tornjak deployment docs (#288) * be560d9 Check for a misconfiguration of bundle endpoint profiles (#348) * b2e9f40 Bump spire version (#352) * 7165b20 Bump test chart dependencies (#350) * da4ebdf Fix federation certificate name when upstream enabled (#347) * c37de1e Fix Tornjak logsDir for Openshift (#344) * 8fef1bd Add external spire-controller-managers (#284) * ee12404 set refresh hint to 1/3 of default CA TTL value fixes #335 (#343) * 2d9866a Bump test chart dependencies (#338) * 6de23d3 Don't create role/binding when bundle disabled (#336) * c132cc4 Add support for externalServer=true (#303) * a2494ee Add auth option for Tornjak (#259) * f679a0d Bump test chart dependencies (#333) * 5149256 Work around curl change * 3d2ac16 Bump test chart dependencies * 08f699b Add spire-lib chart (#289) * 260b02f Add an easy to use identity for child servers (#302) Signed-off-by: Faisal Memon --- charts/spire/Chart.yaml | 2 +- charts/spire/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/spire/Chart.yaml b/charts/spire/Chart.yaml index a2e052c44..54c80a890 100644 --- a/charts/spire/Chart.yaml +++ b/charts/spire/Chart.yaml @@ -3,7 +3,7 @@ name: spire description: > A Helm chart for deploying the complete Spire stack including: spire-server, spire-agent, spiffe-csi-driver, spiffe-oidc-discovery-provider and spire-controller-manager. type: application -version: 0.20.0 +version: 0.21.0 appVersion: "1.9.6" keywords: ["spiffe", "spire", "spire-server", "spire-agent", "oidc", "spire-controller-manager"] home: https://github.com/spiffe/helm-charts-hardened/tree/main/charts/spire diff --git a/charts/spire/README.md b/charts/spire/README.md index a7830d79e..d16af86b0 100644 --- a/charts/spire/README.md +++ b/charts/spire/README.md @@ -1,6 +1,6 @@ # spire -![Version: 0.20.0](https://img.shields.io/badge/Version-0.20.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.9.6](https://img.shields.io/badge/AppVersion-1.9.6-informational?style=flat-square) +![Version: 0.21.0](https://img.shields.io/badge/Version-0.21.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.9.6](https://img.shields.io/badge/AppVersion-1.9.6-informational?style=flat-square) [![Development Phase](https://github.com/spiffe/spiffe/blob/main/.img/maturity/dev.svg)](https://github.com/spiffe/spiffe/blob/main/MATURITY.md#development) A Helm chart for deploying the complete Spire stack including: spire-server, spire-agent, spiffe-csi-driver, spiffe-oidc-discovery-provider and spire-controller-manager.