From 6b09388ccb2b6cbf4d1413506f9ce7b4ddbab72a Mon Sep 17 00:00:00 2001 From: OpenShift Helm Charts Bot <83200018+openshift-helm-charts-bot@users.noreply.github.com> Date: Wed, 18 Oct 2023 15:57:29 -0500 Subject: [PATCH] Auto-Release-1.6.0 (#281) --- .../hashicorp/vault/0.24.1/src/.helmignore | 28 + .../hashicorp/vault/0.24.1/src/CHANGELOG.md | 468 +++++++ .../vault/0.24.1/src/CONTRIBUTING.md | 247 ++++ .../hashicorp/vault/0.24.1/src/Chart.yaml | 23 + .../hashicorp/vault/0.24.1/src/LICENSE | 355 +++++ .../hashicorp/vault/0.24.1/src/Makefile | 101 ++ .../hashicorp/vault/0.24.1/src/README.md | 43 + .../vault/0.24.1/src/templates/NOTES.txt | 14 + .../vault/0.24.1/src/templates/_helpers.tpl | 968 ++++++++++++++ .../src/templates/csi-agent-configmap.yaml | 34 + .../0.24.1/src/templates/csi-clusterrole.yaml | 23 + .../src/templates/csi-clusterrolebinding.yaml | 24 + .../0.24.1/src/templates/csi-daemonset.yaml | 155 +++ .../vault/0.24.1/src/templates/csi-role.yaml | 31 + .../0.24.1/src/templates/csi-rolebinding.yaml | 24 + .../src/templates/csi-serviceaccount.yaml | 21 + .../src/templates/injector-certs-secret.yaml | 19 + .../src/templates/injector-clusterrole.yaml | 24 + .../injector-clusterrolebinding.yaml | 24 + .../src/templates/injector-deployment.yaml | 171 +++ .../templates/injector-disruptionbudget.yaml | 25 + .../templates/injector-mutating-webhook.yaml | 44 + .../templates/injector-network-policy.yaml | 29 + .../src/templates/injector-psp-role.yaml | 25 + .../templates/injector-psp-rolebinding.yaml | 26 + .../0.24.1/src/templates/injector-psp.yaml | 51 + .../0.24.1/src/templates/injector-role.yaml | 34 + .../src/templates/injector-rolebinding.yaml | 27 + .../src/templates/injector-service.yaml | 27 + .../templates/injector-serviceaccount.yaml | 18 + .../templates/prometheus-prometheusrules.yaml | 31 + .../templates/prometheus-servicemonitor.yaml | 49 + .../templates/server-clusterrolebinding.yaml | 29 + .../templates/server-config-configmap.yaml | 45 + .../src/templates/server-discovery-role.yaml | 26 + .../server-discovery-rolebinding.yaml | 34 + .../templates/server-disruptionbudget.yaml | 31 + .../templates/server-ha-active-service.yaml | 55 + .../templates/server-ha-standby-service.yaml | 54 + .../templates/server-headless-service.yaml | 39 + .../0.24.1/src/templates/server-ingress.yaml | 69 + .../src/templates/server-network-policy.yaml | 31 + .../0.24.1/src/templates/server-psp-role.yaml | 25 + .../src/templates/server-psp-rolebinding.yaml | 26 + .../0.24.1/src/templates/server-psp.yaml | 54 + .../0.24.1/src/templates/server-route.yaml | 39 + .../0.24.1/src/templates/server-service.yaml | 51 + .../src/templates/server-serviceaccount.yaml | 22 + .../src/templates/server-statefulset.yaml | 217 ++++ .../src/templates/tests/server-test.yaml | 56 + .../0.24.1/src/templates/ui-service.yaml | 42 + .../vault/0.24.1/src/values.openshift.yaml | 21 + .../vault/0.24.1/src/values.schema.json | 1105 ++++++++++++++++ .../hashicorp/vault/0.24.1/src/values.yaml | 1092 ++++++++++++++++ .../hashicorp/vault/0.25.0/src/.helmignore | 28 + .../hashicorp/vault/0.25.0/src/CHANGELOG.md | 484 +++++++ .../hashicorp/vault/0.25.0/src/CODEOWNERS | 1 + .../vault/0.25.0/src/CONTRIBUTING.md | 247 ++++ .../hashicorp/vault/0.25.0/src/Chart.yaml | 23 + .../hashicorp/vault/0.25.0/src/LICENSE | 355 +++++ .../hashicorp/vault/0.25.0/src/Makefile | 101 ++ .../hashicorp/vault/0.25.0/src/README.md | 43 + .../vault/0.25.0/src/templates/NOTES.txt | 14 + .../vault/0.25.0/src/templates/_helpers.tpl | 996 ++++++++++++++ .../src/templates/csi-agent-configmap.yaml | 34 + .../0.25.0/src/templates/csi-clusterrole.yaml | 23 + .../src/templates/csi-clusterrolebinding.yaml | 24 + .../0.25.0/src/templates/csi-daemonset.yaml | 157 +++ .../vault/0.25.0/src/templates/csi-role.yaml | 31 + .../0.25.0/src/templates/csi-rolebinding.yaml | 24 + .../src/templates/csi-serviceaccount.yaml | 21 + .../src/templates/injector-certs-secret.yaml | 19 + .../src/templates/injector-clusterrole.yaml | 24 + .../injector-clusterrolebinding.yaml | 24 + .../src/templates/injector-deployment.yaml | 179 +++ .../templates/injector-disruptionbudget.yaml | 25 + .../templates/injector-mutating-webhook.yaml | 44 + .../templates/injector-network-policy.yaml | 29 + .../src/templates/injector-psp-role.yaml | 25 + .../templates/injector-psp-rolebinding.yaml | 26 + .../0.25.0/src/templates/injector-psp.yaml | 51 + .../0.25.0/src/templates/injector-role.yaml | 34 + .../src/templates/injector-rolebinding.yaml | 27 + .../src/templates/injector-service.yaml | 27 + .../templates/injector-serviceaccount.yaml | 18 + .../templates/prometheus-prometheusrules.yaml | 31 + .../templates/prometheus-servicemonitor.yaml | 49 + .../templates/server-clusterrolebinding.yaml | 29 + .../templates/server-config-configmap.yaml | 45 + .../src/templates/server-discovery-role.yaml | 26 + .../server-discovery-rolebinding.yaml | 34 + .../templates/server-disruptionbudget.yaml | 31 + .../templates/server-ha-active-service.yaml | 55 + .../templates/server-ha-standby-service.yaml | 54 + .../templates/server-headless-service.yaml | 39 + .../0.25.0/src/templates/server-ingress.yaml | 69 + .../src/templates/server-network-policy.yaml | 31 + .../0.25.0/src/templates/server-psp-role.yaml | 25 + .../src/templates/server-psp-rolebinding.yaml | 26 + .../0.25.0/src/templates/server-psp.yaml | 54 + .../0.25.0/src/templates/server-route.yaml | 39 + .../0.25.0/src/templates/server-service.yaml | 51 + .../src/templates/server-serviceaccount.yaml | 22 + .../src/templates/server-statefulset.yaml | 217 ++++ .../src/templates/tests/server-test.yaml | 56 + .../0.25.0/src/templates/ui-service.yaml | 42 + .../vault/0.25.0/src/values.openshift.yaml | 21 + .../vault/0.25.0/src/values.schema.json | 1144 +++++++++++++++++ .../hashicorp/vault/0.25.0/src/values.yaml | 1106 ++++++++++++++++ .../partners/ntest/test-helm-project1/OWNERS | 9 + .../0.2.0/developer-hub-0.2.0.tgz | Bin 0 -> 138746 bytes .../redhat/trusted-artifact-signer/OWNERS | 13 + 112 files changed, 12822 insertions(+) create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/.helmignore create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/CHANGELOG.md create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/CONTRIBUTING.md create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/Chart.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/LICENSE create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/Makefile create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/README.md create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/NOTES.txt create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/_helpers.tpl create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/csi-agent-configmap.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/csi-clusterrole.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/csi-clusterrolebinding.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/csi-daemonset.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/csi-role.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/csi-rolebinding.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/csi-serviceaccount.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/injector-certs-secret.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/injector-clusterrole.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/injector-clusterrolebinding.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/injector-deployment.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/injector-disruptionbudget.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/injector-mutating-webhook.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/injector-network-policy.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/injector-psp-role.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/injector-psp-rolebinding.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/injector-psp.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/injector-role.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/injector-rolebinding.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/injector-service.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/injector-serviceaccount.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/prometheus-prometheusrules.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/prometheus-servicemonitor.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/server-clusterrolebinding.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/server-config-configmap.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/server-discovery-role.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/server-discovery-rolebinding.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/server-disruptionbudget.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/server-ha-active-service.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/server-ha-standby-service.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/server-headless-service.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/server-ingress.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/server-network-policy.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/server-psp-role.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/server-psp-rolebinding.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/server-psp.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/server-route.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/server-service.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/server-serviceaccount.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/server-statefulset.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/tests/server-test.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/templates/ui-service.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/values.openshift.yaml create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/values.schema.json create mode 100644 charts/partners/hashicorp/vault/0.24.1/src/values.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/.helmignore create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/CHANGELOG.md create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/CODEOWNERS create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/CONTRIBUTING.md create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/Chart.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/LICENSE create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/Makefile create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/README.md create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/NOTES.txt create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/_helpers.tpl create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/csi-agent-configmap.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/csi-clusterrole.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/csi-clusterrolebinding.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/csi-daemonset.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/csi-role.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/csi-rolebinding.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/csi-serviceaccount.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/injector-certs-secret.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/injector-clusterrole.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/injector-clusterrolebinding.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/injector-deployment.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/injector-disruptionbudget.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/injector-mutating-webhook.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/injector-network-policy.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/injector-psp-role.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/injector-psp-rolebinding.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/injector-psp.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/injector-role.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/injector-rolebinding.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/injector-service.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/injector-serviceaccount.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/prometheus-prometheusrules.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/prometheus-servicemonitor.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/server-clusterrolebinding.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/server-config-configmap.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/server-discovery-role.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/server-discovery-rolebinding.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/server-disruptionbudget.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/server-ha-active-service.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/server-ha-standby-service.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/server-headless-service.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/server-ingress.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/server-network-policy.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/server-psp-role.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/server-psp-rolebinding.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/server-psp.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/server-route.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/server-service.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/server-serviceaccount.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/server-statefulset.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/tests/server-test.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/templates/ui-service.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/values.openshift.yaml create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/values.schema.json create mode 100644 charts/partners/hashicorp/vault/0.25.0/src/values.yaml create mode 100644 charts/partners/ntest/test-helm-project1/OWNERS create mode 100644 charts/redhat/redhat/developer-hub/0.2.0/developer-hub-0.2.0.tgz create mode 100644 charts/redhat/redhat/trusted-artifact-signer/OWNERS diff --git a/charts/partners/hashicorp/vault/0.24.1/src/.helmignore b/charts/partners/hashicorp/vault/0.24.1/src/.helmignore new file mode 100644 index 00000000..4007e243 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/.helmignore @@ -0,0 +1,28 @@ +# 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 +.terraform/ +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj + +# CI and test +.circleci/ +.github/ +.gitlab-ci.yml +test/ diff --git a/charts/partners/hashicorp/vault/0.24.1/src/CHANGELOG.md b/charts/partners/hashicorp/vault/0.24.1/src/CHANGELOG.md new file mode 100644 index 00000000..741c5ab8 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/CHANGELOG.md @@ -0,0 +1,468 @@ +## Unreleased + +## 0.24.1 (April 17, 2023) + +Bugs: +* csi: Add RBAC required by v1.3.0 to create secret for HMAC key used to generate secret versions [GH-872](https://github.com/hashicorp/vault-helm/pull/872) + +## 0.24.0 (April 6, 2023) + +Changes: +* Earliest Kubernetes version tested is now 1.22 +* `vault` updated to 1.13.1 [GH-863](https://github.com/hashicorp/vault-helm/pull/863) +* `vault-k8s` updated to 1.2.1 [GH-868](https://github.com/hashicorp/vault-helm/pull/868) +* `vault-csi-provider` updated to 1.3.0 [GH-749](https://github.com/hashicorp/vault-helm/pull/749) + +Features: +* server: New `extraPorts` option for adding ports to the Vault server statefulset [GH-841](https://github.com/hashicorp/vault-helm/pull/841) +* server: Add configurable Port Number in readinessProbe and livenessProbe for the server-statefulset [GH-831](https://github.com/hashicorp/vault-helm/pull/831) +* injector: Make livenessProbe and readinessProbe configurable and add configurable startupProbe [GH-852](https://github.com/hashicorp/vault-helm/pull/852) +* csi: Add an Agent sidecar to Vault CSI Provider pods to provide lease caching and renewals [GH-749](https://github.com/hashicorp/vault-helm/pull/749) + +## 0.23.0 (November 28th, 2022) + +Changes: +* `vault` updated to 1.12.1 [GH-814](https://github.com/hashicorp/vault-helm/pull/814) +* `vault-k8s` updated to 1.1.0 [GH-814](https://github.com/hashicorp/vault-helm/pull/814) +* `vault-csi-provider` updated to 1.2.1 [GH-814](https://github.com/hashicorp/vault-helm/pull/814) + +Features: +* server: Add `extraLabels` for Vault server serviceAccount [GH-806](https://github.com/hashicorp/vault-helm/pull/806) +* server: Add `server.service.active.enabled` and `server.service.standby.enabled` options to selectively disable additional services [GH-811](https://github.com/hashicorp/vault-helm/pull/811) +* server: Add `server.serviceAccount.serviceDiscovery.enabled` option to selectively disable a Vault service discovery role and role binding [GH-811](https://github.com/hashicorp/vault-helm/pull/811) +* server: Add `server.service.instanceSelector.enabled` option to allow selecting pods outside the helm chart deployment [GH-813](https://github.com/hashicorp/vault-helm/pull/813) + +Bugs: +* server: Quote `.server.ha.clusterAddr` value [GH-810](https://github.com/hashicorp/vault-helm/pull/810) + +## 0.22.1 (October 26th, 2022) + +Changes: +* `vault` updated to 1.12.0 [GH-803](https://github.com/hashicorp/vault-helm/pull/803) +* `vault-k8s` updated to 1.0.1 [GH-803](https://github.com/hashicorp/vault-helm/pull/803) + +## 0.22.0 (September 8th, 2022) + +Features: +* Add PrometheusOperator support for collecting Vault server metrics. [GH-772](https://github.com/hashicorp/vault-helm/pull/772) + +Changes: +* `vault-k8s` to 1.0.0 [GH-784](https://github.com/hashicorp/vault-helm/pull/784) +* Test against Kubernetes 1.25 [GH-784](https://github.com/hashicorp/vault-helm/pull/784) +* `vault` updated to 1.11.3 [GH-785](https://github.com/hashicorp/vault-helm/pull/785) + +## 0.21.0 (August 10th, 2022) + +CHANGES: +* `vault-k8s` updated to 0.17.0. [GH-771](https://github.com/hashicorp/vault-helm/pull/771) +* `vault-csi-provider` updated to 1.2.0 [GH-771](https://github.com/hashicorp/vault-helm/pull/771) +* `vault` updated to 1.11.2 [GH-771](https://github.com/hashicorp/vault-helm/pull/771) +* Start testing against Kubernetes 1.24. [GH-744](https://github.com/hashicorp/vault-helm/pull/744) +* Deprecated `injector.externalVaultAddr`. Added `global.externalVaultAddr`, which applies to both the Injector and the CSI Provider. [GH-745](https://github.com/hashicorp/vault-helm/pull/745) +* CSI Provider pods now set the `VAULT_ADDR` environment variable to either the internal Vault service or the configured external address. [GH-745](https://github.com/hashicorp/vault-helm/pull/745) + +Features: +* server: Add `server.statefulSet.securityContext` to override pod and container `securityContext`. [GH-767](https://github.com/hashicorp/vault-helm/pull/767) +* csi: Add `csi.daemonSet.securityContext` to override pod and container `securityContext`. [GH-767](https://github.com/hashicorp/vault-helm/pull/767) +* injector: Add `injector.securityContext` to override pod and container `securityContext`. [GH-750](https://github.com/hashicorp/vault-helm/pull/750) and [GH-767](https://github.com/hashicorp/vault-helm/pull/767) +* Add `server.service.activeNodePort` and `server.service.standbyNodePort` to specify the `nodePort` for active and standby services. [GH-610](https://github.com/hashicorp/vault-helm/pull/610) +* Support for setting annotations on the injector's serviceAccount [GH-753](https://github.com/hashicorp/vault-helm/pull/753) + +## 0.20.1 (May 25th, 2022) +CHANGES: +* `vault-k8s` updated to 0.16.1 [GH-739](https://github.com/hashicorp/vault-helm/pull/739) + +Improvements: +* Mutating webhook will no longer target the agent injector pod [GH-736](https://github.com/hashicorp/vault-helm/pull/736) + +Bugs: +* `vault` service account is now created even if the server is set to disabled, as per before 0.20.0 [GH-737](https://github.com/hashicorp/vault-helm/pull/737) + +## 0.20.0 (May 16th, 2022) + +CHANGES: +* `global.enabled` now works as documented, that is, setting `global.enabled` to false will disable everything, with individual components able to be turned on individually [GH-703](https://github.com/hashicorp/vault-helm/pull/703) +* Default value of `-` used for injector and server to indicate that they follow `global.enabled`. [GH-703](https://github.com/hashicorp/vault-helm/pull/703) +* Vault default image to 1.10.3 +* CSI provider default image to 1.1.0 +* Vault K8s default image to 0.16.0 +* Earliest Kubernetes version tested is now 1.16 +* Helm 3.6+ now required + +Features: +* Support topologySpreadConstraints in server and injector. [GH-652](https://github.com/hashicorp/vault-helm/pull/652) + +Improvements: +* CSI: Set `extraLabels` for daemonset, pods, and service account [GH-690](https://github.com/hashicorp/vault-helm/pull/690) +* Add namespace to injector-leader-elector role, rolebinding and secret [GH-683](https://github.com/hashicorp/vault-helm/pull/683) +* Support policy/v1 PodDisruptionBudget in Kubernetes 1.21+ for server and injector [GH-710](https://github.com/hashicorp/vault-helm/pull/710) +* Make the Cluster Address (CLUSTER_ADDR) configurable [GH-629](https://github.com/hashicorp/vault-helm/pull/709) +* server: Make `publishNotReadyAddresses` configurable for services [GH-694](https://github.com/hashicorp/vault-helm/pull/694) +* server: Allow config to be defined as a YAML object in the values file [GH-684](https://github.com/hashicorp/vault-helm/pull/684) +* Maintain default MutatingWebhookConfiguration values from `v1beta1` [GH-692](https://github.com/hashicorp/vault-helm/pull/692) + +## 0.19.0 (January 20th, 2022) + +CHANGES: +* Vault image default 1.9.2 +* Vault K8s image default 0.14.2 + +Features: +* Added configurable podDisruptionBudget for injector [GH-653](https://github.com/hashicorp/vault-helm/pull/653) +* Make terminationGracePeriodSeconds configurable for server [GH-659](https://github.com/hashicorp/vault-helm/pull/659) +* Added configurable update strategy for injector [GH-661](https://github.com/hashicorp/vault-helm/pull/661) +* csi: ability to set priorityClassName for CSI daemonset pods [GH-670](https://github.com/hashicorp/vault-helm/pull/670) + +Improvements: +* Set the namespace on the OpenShift Route [GH-679](https://github.com/hashicorp/vault-helm/pull/679) +* Add volumes and env vars to helm hook test pod [GH-673](https://github.com/hashicorp/vault-helm/pull/673) +* Make TLS configurable for OpenShift routes [GH-686](https://github.com/hashicorp/vault-helm/pull/686) + +## 0.18.0 (November 17th, 2021) + +CHANGES: +* Removed support for deploying a leader-elector container with the [vault-k8s injector](https://github.com/hashicorp/vault-k8s) injector since vault-k8s now uses an internal mechanism to determine leadership [GH-649](https://github.com/hashicorp/vault-helm/pull/649) +* Vault image default 1.9.0 +* Vault K8s image default 0.14.1 + +Improvements: +* Added templateConfig.staticSecretRenderInterval chart option for the injector [GH-621](https://github.com/hashicorp/vault-helm/pull/621) + +## 0.17.1 (October 25th, 2021) + +Improvements: + * Add option for Ingress PathType [GH-634](https://github.com/hashicorp/vault-helm/pull/634) + +## 0.17.0 (October 21st, 2021) + +KNOWN ISSUES: +* The chart will fail to deploy on Kubernetes 1.19+ with `server.ingress.enabled=true` because no `pathType` is set + +CHANGES: +* Vault image default 1.8.4 +* Vault K8s image default 0.14.0 + +Improvements: +* Support Ingress stable networking API [GH-590](https://github.com/hashicorp/vault-helm/pull/590) +* Support setting the `externalTrafficPolicy` for `LoadBalancer` and `NodePort` service types [GH-626](https://github.com/hashicorp/vault-helm/pull/626) +* Support setting ingressClassName on server Ingress [GH-630](https://github.com/hashicorp/vault-helm/pull/630) + +Bugs: +* Ensure `kubeletRootDir` volume path and mounts are the same when `csi.daemonSet.kubeletRootDir` is overridden [GH-628](https://github.com/hashicorp/vault-helm/pull/628) + +## 0.16.1 (September 29th, 2021) + +CHANGES: +* Vault image default 1.8.3 +* Vault K8s image default 0.13.1 + +## 0.16.0 (September 16th, 2021) + +CHANGES: +* Support for deploying a leader-elector container with the [vault-k8s injector](https://github.com/hashicorp/vault-k8s) injector will be removed in version 0.18.0 of this chart since vault-k8s now uses an internal mechanism to determine leadership. To enable the deployment of the leader-elector container for use with vault-k8s 0.12.0 and earlier, set `useContainer=true`. + +Improvements: + * Make CSI provider `hostPaths` configurable via `csi.daemonSet.providersDir` and `csi.daemonSet.kubeletRootDir` [GH-603](https://github.com/hashicorp/vault-helm/pull/603) + * Support vault-k8s internal leader election [GH-568](https://github.com/hashicorp/vault-helm/pull/568) [GH-607](https://github.com/hashicorp/vault-helm/pull/607) + +## 0.15.0 (August 23rd, 2021) + +Improvements: +* Add imagePullSecrets on server test [GH-572](https://github.com/hashicorp/vault-helm/pull/572) +* Add injector.webhookAnnotations chart option [GH-584](https://github.com/hashicorp/vault-helm/pull/584) + +## 0.14.0 (July 28th, 2021) + +Features: +* Added templateConfig.exitOnRetryFailure chart option for the injector [GH-560](https://github.com/hashicorp/vault-helm/pull/560) + +Improvements: +* Support configuring pod tolerations, pod affinity, and node selectors as YAML [GH-565](https://github.com/hashicorp/vault-helm/pull/565) +* Set the default vault image to come from the hashicorp organization [GH-567](https://github.com/hashicorp/vault-helm/pull/567) +* Add support for running the acceptance tests against a local `kind` cluster [GH-567](https://github.com/hashicorp/vault-helm/pull/567) +* Add `server.ingress.activeService` to configure if the ingress should use the active service [GH-570](https://github.com/hashicorp/vault-helm/pull/570) +* Add `server.route.activeService` to configure if the route should use the active service [GH-570](https://github.com/hashicorp/vault-helm/pull/570) +* Support configuring `global.imagePullSecrets` from a string array [GH-576](https://github.com/hashicorp/vault-helm/pull/576) + + +## 0.13.0 (June 17th, 2021) + +Improvements: +* Added a helm test for vault server [GH-531](https://github.com/hashicorp/vault-helm/pull/531) +* Added server.enterpriseLicense option [GH-547](https://github.com/hashicorp/vault-helm/pull/547) +* Added OpenShift overrides [GH-549](https://github.com/hashicorp/vault-helm/pull/549) + +Bugs: +* Fix ui.serviceNodePort schema [GH-537](https://github.com/hashicorp/vault-helm/pull/537) +* Fix server.ha.disruptionBudget.maxUnavailable schema [GH-535](https://github.com/hashicorp/vault-helm/pull/535) +* Added webhook-certs volume mount to sidecar injector [GH-545](https://github.com/hashicorp/vault-helm/pull/545) + +## 0.12.0 (May 25th, 2021) + +Features: +* Pass additional arguments to `vault-csi-provider` using `csi.extraArgs` [GH-526](https://github.com/hashicorp/vault-helm/pull/526) + +Improvements: +* Set chart kubeVersion and added chart-verifier tests [GH-510](https://github.com/hashicorp/vault-helm/pull/510) +* Added values json schema [GH-513](https://github.com/hashicorp/vault-helm/pull/513) +* Ability to set tolerations for CSI daemonset pods [GH-521](https://github.com/hashicorp/vault-helm/pull/521) +* UI target port is now configurable [GH-437](https://github.com/hashicorp/vault-helm/pull/437) + +Bugs: +* CSI: `global.imagePullSecrets` are now also used for CSI daemonset [GH-519](https://github.com/hashicorp/vault-helm/pull/519) + +## 0.11.0 (April 14th, 2021) + +Features: +* Added `server.enabled` to explicitly skip installing a Vault server [GH-486](https://github.com/hashicorp/vault-helm/pull/486) +* Injector now supports enabling host network [GH-471](https://github.com/hashicorp/vault-helm/pull/471) +* Injector port is now configurable [GH-489](https://github.com/hashicorp/vault-helm/pull/489) +* Injector Vault Agent resource defaults are now configurable [GH-493](https://github.com/hashicorp/vault-helm/pull/493) +* Extra paths can now be added to the Vault ingress service [GH-460](https://github.com/hashicorp/vault-helm/pull/460) +* Log level and format can now be set directly using `server.logFormat` and `server.logLevel` [GH-488](https://github.com/hashicorp/vault-helm/pull/488) + +Improvements: +* Added `https` name to injector service port [GH-495](https://github.com/hashicorp/vault-helm/pull/495) + +Bugs: +* CSI: Fix ClusterRole name and DaemonSet's service account to properly match deployment name [GH-486](https://github.com/hashicorp/vault-helm/pull/486) + +## 0.10.0 (March 25th, 2021) + +Features: +* Add support for [Vault CSI provider](https://github.com/hashicorp/vault-csi-provider) [GH-461](https://github.com/hashicorp/vault-helm/pull/461) + +Improvements: +* `objectSelector` can now be set on the mutating admission webhook [GH-456](https://github.com/hashicorp/vault-helm/pull/456) + +## 0.9.1 (February 2nd, 2021) + +Bugs: +* Injector: fix labels for default anti-affinity rule [GH-441](https://github.com/hashicorp/vault-helm/pull/441), [GH-442](https://github.com/hashicorp/vault-helm/pull/442) +* Set VAULT_DEV_LISTEN_ADDRESS in dev mode [GH-446](https://github.com/hashicorp/vault-helm/pull/446) + +## 0.9.0 (January 5th, 2021) + +Features: +* Injector now supports configurable number of replicas [GH-436](https://github.com/hashicorp/vault-helm/pull/436) +* Injector now supports auto TLS for multiple replicas using leader elections [GH-436](https://github.com/hashicorp/vault-helm/pull/436) + +Improvements: +* Dev mode now supports `server.extraArgs` [GH-421](https://github.com/hashicorp/vault-helm/pull/421) +* Dev mode root token is now configurable with `server.dev.devRootToken` [GH-415](https://github.com/hashicorp/vault-helm/pull/415) +* ClusterRoleBinding updated to `v1` [GH-395](https://github.com/hashicorp/vault-helm/pull/395) +* MutatingWebhook updated to `v1` [GH-408](https://github.com/hashicorp/vault-helm/pull/408) +* Injector service now supports `injector.service.annotations` [425](https://github.com/hashicorp/vault-helm/pull/425) +* Injector now supports `injector.extraLabels` [428](https://github.com/hashicorp/vault-helm/pull/428) +* Added `allowPrivilegeEscalation: false` to Vault and Injector containers [429](https://github.com/hashicorp/vault-helm/pull/429) +* Network Policy now supports `server.networkPolicy.egress` [389](https://github.com/hashicorp/vault-helm/pull/389) + +## 0.8.0 (October 20th, 2020) + +Improvements: +* Make server NetworkPolicy independent of OpenShift [GH-381](https://github.com/hashicorp/vault-helm/pull/381) +* Added configurables for all probe values [GH-387](https://github.com/hashicorp/vault-helm/pull/387) +* MountPath for audit and data storage is now configurable [GH-393](https://github.com/hashicorp/vault-helm/pull/393) +* Annotations can now be added to the Injector pods [GH-394](https://github.com/hashicorp/vault-helm/pull/394) +* The injector can now be configured with a failurePolicy [GH-400](https://github.com/hashicorp/vault-helm/pull/400) +* Added additional environment variables for rendering within Vault config [GH-398](https://github.com/hashicorp/vault-helm/pull/398) +* Service account for Vault K8s auth is automatically created when `injector.externalVaultAddr` is set [GH-392](https://github.com/hashicorp/vault-helm/pull/392) + +Bugs: +* Fixed install output using Helm V2 command [GH-378](https://github.com/hashicorp/vault-helm/pull/378) + +## 0.7.0 (August 24th, 2020) + +Features: +* Added `volumes` and `volumeMounts` for mounting _any_ type of volume [GH-314](https://github.com/hashicorp/vault-helm/pull/314). +* Added configurable to enable prometheus telemetery exporter for Vault Agent Injector [GH-372](https://github.com/hashicorp/vault-helm/pull/372) + +Improvements: +* Added `defaultMode` configurable to `extraVolumes`[GH-321](https://github.com/hashicorp/vault-helm/pull/321) +* Option to install and use PodSecurityPolicy's for vault server and injector [GH-177](https://github.com/hashicorp/vault-helm/pull/177) +* `VAULT_API_ADDR` is now configurable [GH-290](https://github.com/hashicorp/vault-helm/pull/290) +* Removed deprecated tolerate unready endpoint annotations [GH-363](https://github.com/hashicorp/vault-helm/pull/363) +* Add an option to set annotations on the StatefulSet [GH-199](https://github.com/hashicorp/vault-helm/pull/199) +* Make the vault server serviceAccount name a configuration option [GH-367](https://github.com/hashicorp/vault-helm/pull/367) +* Removed annotation striction from `dev` mode [GH-371](https://github.com/hashicorp/vault-helm/pull/371) +* Add an option to set annotations on PVCs [GH-364](https://github.com/hashicorp/vault-helm/pull/364) +* Added service configurables for UI [GH-285](https://github.com/hashicorp/vault-helm/pull/285) + +Bugs: +* Fix python dependency in test image [GH-337](https://github.com/hashicorp/vault-helm/pull/337) +* Fix caBundle not being quoted causing validation issues with Helm 3 [GH-352](https://github.com/hashicorp/vault-helm/pull/352) +* Fix injector network policy being rendered when injector is not enabled [GH-358](https://github.com/hashicorp/vault-helm/pull/358) + +## 0.6.0 (June 3rd, 2020) + +Features: +* Added `extraInitContainers` to define init containers for the Vault cluster [GH-258](https://github.com/hashicorp/vault-helm/pull/258) +* Added `postStart` lifecycle hook allowing users to configure commands to run on the Vault pods after they're ready [GH-315](https://github.com/hashicorp/vault-helm/pull/315) +* Beta: Added OpenShift support [GH-319](https://github.com/hashicorp/vault-helm/pull/319) + +Improvements: +* Server configs can now be defined in YAML. Multi-line string configs are still compatible [GH-213](https://github.com/hashicorp/vault-helm/pull/213) +* Removed IPC_LOCK privileges since swap is disabled on containers [[GH-198](https://github.com/hashicorp/vault-helm/pull/198)] +* Use port names that map to vault.scheme [[GH-223](https://github.com/hashicorp/vault-helm/pull/223)] +* Allow both yaml and multi-line string annotations [[GH-272](https://github.com/hashicorp/vault-helm/pull/272)] +* Added configurable to set the Raft node name to hostname [[GH-269](https://github.com/hashicorp/vault-helm/pull/269)] +* Support setting priorityClassName on pods [[GH-282](https://github.com/hashicorp/vault-helm/pull/282)] +* Added support for ingress apiVersion `networking.k8s.io/v1beta1` [[GH-310](https://github.com/hashicorp/vault-helm/pull/310)] +* Added configurable to change service type for the HA active service [GH-317](https://github.com/hashicorp/vault-helm/pull/317) + +Bugs: +* Fixed default ingress path [[GH-224](https://github.com/hashicorp/vault-helm/pull/224)] +* Fixed annotations for HA standby/active services [[GH-268](https://github.com/hashicorp/vault-helm/pull/268)] +* Updated some value defaults to match their use in templates [[GH-309](https://github.com/hashicorp/vault-helm/pull/309)] +* Use active service on ingress when ha [[GH-270](https://github.com/hashicorp/vault-helm/pull/270)] +* Fixed bug where pull secrets weren't being used for injector image [GH-298](https://github.com/hashicorp/vault-helm/pull/298) + +## 0.5.0 (April 9th, 2020) + +Features: + +* Added Raft support for HA mode [[GH-228](https://github.com/hashicorp/vault-helm/pull/229)] +* Now supports Vault Enterprise [[GH-250](https://github.com/hashicorp/vault-helm/pull/250)] +* Added K8s Service Registration for HA modes [[GH-250](https://github.com/hashicorp/vault-helm/pull/250)] + +* Option to set `AGENT_INJECT_VAULT_AUTH_PATH` for the injector [[GH-185](https://github.com/hashicorp/vault-helm/pull/185)] +* Added environment variables for logging and revocation on Vault Agent Injector [[GH-219](https://github.com/hashicorp/vault-helm/pull/219)] +* Option to set environment variables for the injector deployment [[GH-232](https://github.com/hashicorp/vault-helm/pull/232)] +* Added affinity, tolerations, and nodeSelector options for the injector deployment [[GH-234](https://github.com/hashicorp/vault-helm/pull/234)] +* Made all annotations multi-line strings [[GH-227](https://github.com/hashicorp/vault-helm/pull/227)] + +## 0.4.0 (February 21st, 2020) + +Improvements: + +* Allow process namespace sharing between Vault and sidecar containers [[GH-174](https://github.com/hashicorp/vault-helm/pull/174)] +* Added configurable to change updateStrategy [[GH-172](https://github.com/hashicorp/vault-helm/pull/172)] +* Added sleep in the preStop lifecycle step [[GH-188](https://github.com/hashicorp/vault-helm/pull/188)] +* Updated chart and tests to Helm 3 [[GH-195](https://github.com/hashicorp/vault-helm/pull/195)] +* Adds Values.injector.externalVaultAddr to use the injector with an external vault [[GH-207](https://github.com/hashicorp/vault-helm/pull/207)] + +Bugs: + +* Fix bug where Vault lifecycle was appended after extra containers. [[GH-179](https://github.com/hashicorp/vault-helm/pull/179)] + +## 0.3.3 (January 14th, 2020) + +Security: + +* Added `server.extraArgs` to allow loading of additional Vault configurations containing sensitive settings [GH-175](https://github.com/hashicorp/vault-helm/issues/175) + +Bugs: + +* Fixed injection bug where wrong environment variables were being used for manually mounted TLS files + +## 0.3.2 (January 8th, 2020) + +Bugs: + +* Fixed injection bug where TLS Skip Verify was true by default [VK8S-35] + +## 0.3.1 (January 2nd, 2020) + +Bugs: + +* Fixed injection bug causing kube-system pods to be rejected [VK8S-14] + +## 0.3.0 (December 19th, 2019) + +Features: + +* Extra containers can now be added to the Vault pods +* Added configurability of pod probes +* Added Vault Agent Injector + +Improvements: + +* Moved `global.image` to `server.image` +* Changed UI service template to route pods that aren't ready via `publishNotReadyAddresses: true` +* Added better HTTP/HTTPS scheme support to http probes +* Added configurable node port for Vault service +* `server.authDelegator` is now enabled by default + +Bugs: + +* Fixed upgrade bug by removing chart label which contained the version +* Fixed typo on `serviceAccount` (was `serviceaccount`) +* Fixed readiness/liveliness HTTP probe default to accept standbys + +## 0.2.1 (November 12th, 2019) + +Bugs: + +* Removed `readOnlyRootFilesystem` causing issues when validating deployments + +## 0.2.0 (October 29th, 2019) + +Features: + +* Added load balancer support +* Added ingress support +* Added configurable for service types (ClusterIP, NodePort, LoadBalancer, etc) +* Removed root requirements, now runs as Vault user + +Improvements: + +* Added namespace value to all rendered objects +* Made ports configurable in services +* Added the ability to add custom annotations to services +* Added docker image for running bats test in CircleCI +* Removed restrictions around `dev` mode such as annotations +* `readOnlyRootFilesystem` is now configurable +* Image Pull Policy is now configurable + +Bugs: + +* Fixed selector bugs related to Helm label updates (services, affinities, and pod disruption) +* Fixed bug where audit storage was not being mounted in HA mode +* Fixed bug where Vault pod wasn't receiving SIGTERM signals + + +## 0.1.2 (August 22nd, 2019) + +Features: + +* Added `extraSecretEnvironmentVars` to allow users to mount secrets as + environment variables +* Added `tlsDisable` configurable to change HTTP protocols from HTTP/HTTPS + depending on the value +* Added `serviceNodePort` to configure a NodePort value when setting `serviceType` + to "NodePort" + +Improvements: + +* Changed UI port to 8200 for better HTTP protocol support +* Added `path` to `extraVolumes` to define where the volume should be + mounted. Defaults to `/vault/userconfig` +* Upgraded Vault to 1.2.2 + +Bugs: + +* Fixed bug where upgrade would fail because immutable labels were being + changed (Helm Version label) +* Fixed bug where UI service used wrong selector after updating helm labels +* Added `VAULT_API_ADDR` env to Vault pod to fixed bug where Vault thinks + Consul is the active node +* Removed `step-down` preStop since it requires authentication. Shutdown signal + sent by Kube acts similar to `step-down` + + +## 0.1.1 (August 7th, 2019) + +Features: + +* Added `authDelegator` Cluster Role Binding to Vault service account for + bootstrapping Kube auth method + +Improvements: + +* Added `server.service.clusterIP` to `values.yml` so users can toggle + the Vault service to headless by using the value `None`. +* Upgraded Vault to 1.2.1 + +## 0.1.0 (August 6th, 2019) + +Initial release diff --git a/charts/partners/hashicorp/vault/0.24.1/src/CONTRIBUTING.md b/charts/partners/hashicorp/vault/0.24.1/src/CONTRIBUTING.md new file mode 100644 index 00000000..ad31ac92 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/CONTRIBUTING.md @@ -0,0 +1,247 @@ +# Contributing to Vault Helm + +**Please note:** We take Vault's security and our users' trust very seriously. +If you believe you have found a security issue in Vault, please responsibly +disclose by contacting us at security@hashicorp.com. + +**First:** if you're unsure or afraid of _anything_, just ask or submit the +issue or pull request anyways. You won't be yelled at for giving it your best +effort. The worst that can happen is that you'll be politely asked to change +something. We appreciate any sort of contributions, and don't want a wall of +rules to get in the way of that. + +That said, if you want to ensure that a pull request is likely to be merged, +talk to us! You can find out our thoughts and ensure that your contribution +won't clash or be obviated by Vault's normal direction. A great way to do this +is via the [Vault Discussion Forum][1]. + +This document will cover what we're looking for in terms of reporting issues. +By addressing all the points we're looking for, it raises the chances we can +quickly merge or address your contributions. + +[1]: https://discuss.hashicorp.com/c/vault + +## Issues + +### Reporting an Issue + +* Make sure you test against the latest released version. It is possible + we already fixed the bug you're experiencing. Even better is if you can test + against `main`, as bugs are fixed regularly but new versions are only + released every few months. + +* Provide steps to reproduce the issue, and if possible include the expected + results as well as the actual results. Please provide text, not screen shots! + +* Respond as promptly as possible to any questions made by the Vault + team to your issue. Stale issues will be closed periodically. + +### Issue Lifecycle + +1. The issue is reported. + +2. The issue is verified and categorized by a Vault Helm collaborator. + Categorization is done via tags. For example, bugs are marked as "bugs". + +3. Unless it is critical, the issue may be left for a period of time (sometimes + many weeks), giving outside contributors -- maybe you!? -- a chance to + address the issue. + +4. The issue is addressed in a pull request or commit. The issue will be + referenced in the commit message so that the code that fixes it is clearly + linked. + +5. The issue is closed. Sometimes, valid issues will be closed to keep + the issue tracker clean. The issue is still indexed and available for + future viewers, or can be re-opened if necessary. + +## Testing + +The Helm chart ships with both unit and acceptance tests. + +The unit tests don't require any active Kubernetes cluster and complete +very quickly. These should be used for fast feedback during development. +The acceptance tests require a Kubernetes cluster with a configured `kubectl`. + +### Test Using Docker Container + +The following are the instructions for running bats tests using a Docker container. + +#### Prerequisites + +* Docker installed +* `vault-helm` checked out locally + +#### Test + +**Note:** the following commands should be run from the `vault-helm` directory. + +First, build the Docker image for running the tests: + +```shell +docker build -f ${PWD}/test/docker/Test.dockerfile ${PWD}/test/docker/ -t vault-helm-test +``` +Next, execute the tests with the following commands: +```shell +docker run -it --rm -v "${PWD}:/test" vault-helm-test bats /test/test/unit +``` +It's possible to only run specific bats tests using regular expressions. +For example, the following will run only tests with "injector" in the name: +```shell +docker run -it --rm -v "${PWD}:/test" vault-helm-test bats /test/test/unit -f "injector" +``` + +### Test Manually +The following are the instructions for running bats tests on your workstation. +#### Prerequisites +* [Bats](https://github.com/bats-core/bats-core) + ```bash + brew install bats-core + ``` +* [yq](https://pypi.org/project/yq/) + ```bash + brew install python-yq + ``` +* [helm](https://helm.sh) + ```bash + brew install kubernetes-helm + ``` + +#### Test + +To run the unit tests: + + bats ./test/unit + +To run the acceptance tests: + + bats ./test/acceptance + +If the acceptance tests fail, deployed resources in the Kubernetes cluster +may not be properly cleaned up. We recommend recycling the Kubernetes cluster to +start from a clean slate. + +**Note:** There is a Terraform configuration in the +[`test/terraform/`](https://github.com/hashicorp/vault-helm/tree/main/test/terraform) directory +that can be used to quickly bring up a GKE cluster and configure +`kubectl` and `helm` locally. This can be used to quickly spin up a test +cluster for acceptance tests. Unit tests _do not_ require a running Kubernetes +cluster. + +### Writing Unit Tests + +Changes to the Helm chart should be accompanied by appropriate unit tests. + +#### Formatting + +- Put tests in the test file in the same order as the variables appear in the `values.yaml`. +- Start tests for a chart value with a header that says what is being tested, like this: + ``` + #-------------------------------------------------------------------- + # annotations + ``` + +- Name the test based on what it's testing in the following format (this will be its first line): + ``` + @test "
: " { + ``` + + When adding tests to an existing file, the first section will be the same as the other tests in the file. + +#### Test Details + +[Bats](https://github.com/bats-core/bats-core) provides a way to run commands in a shell and inspect the output in an automated way. +In all of the tests in this repo, the base command being run is [helm template](https://docs.helm.sh/helm/#helm-template) which turns the templated files into straight yaml output. +In this way, we're able to test that the various conditionals in the templates render as we would expect. + +Each test defines the files that should be rendered using the `--show-only` flag, then it might adjust chart values by adding `--set` flags as well. +The output from this `helm template` command is then piped to [yq](https://pypi.org/project/yq/). +`yq` allows us to pull out just the information we're interested in, either by referencing its position in the yaml file directly or giving information about it (like its length). +The `-r` flag can be used with `yq` to return a raw string instead of a quoted one which is especially useful when looking for an exact match. + +The test passes or fails based on the conditional at the end that is in square brackets, which is a comparison of our expected value and the output of `helm template` piped to `yq`. + +The `| tee /dev/stderr ` pieces direct any terminal output of the `helm template` and `yq` commands to stderr so that it doesn't interfere with `bats`. + +#### Test Examples + +Here are some examples of common test patterns: + +- Check that a value is disabled by default + + ``` + @test "ui/Service: no type by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "null" ] + } + ``` + + In this example, nothing is changed from the default templates (no `--set` flags), then we use `yq` to retrieve the value we're checking, `.spec.type`. + This output is then compared against our expected value (`null` in this case) in the assertion `[ "${actual}" = "null" ]`. + + +- Check that a template value is rendered to a specific value + ``` + @test "ui/Service: specified type" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'ui.serviceType=LoadBalancer' \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "LoadBalancer" ] + } + ``` + + This is very similar to the last example, except we've changed a default value with the `--set` flag and correspondingly changed the expected value. + +- Check that a template value contains several values + ``` + @test "server/standalone-StatefulSet: custom resources" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.resources.requests.memory=256Mi' \ + --set 'server.resources.requests.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.requests.memory' | tee /dev/stderr) + [ "${actual}" = "256Mi" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.resources.limits.memory=256Mi' \ + --set 'server.resources.limits.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.limits.memory' | tee /dev/stderr) + [ "${actual}" = "256Mi" ] + ``` + + *Note:* If testing more than two conditions, it would be good to separate the `helm template` part of the command from the `yq` sections to reduce redundant work. + +- Check that an entire template file is not rendered + ``` + @test "syncCatalog/Deployment: disabled by default" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'global.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + } + ``` + Here we are check the length of the command output to see if the anything is rendered. + This style can easily be switched to check that a file is rendered instead. + +## Contributor License Agreement + +We require that all contributors sign our Contributor License Agreement ("CLA") +before we can accept the contribution. + +[Learn more about why HashiCorp requires a CLA and what the CLA includes](https://www.hashicorp.com/cla) diff --git a/charts/partners/hashicorp/vault/0.24.1/src/Chart.yaml b/charts/partners/hashicorp/vault/0.24.1/src/Chart.yaml new file mode 100644 index 00000000..8882245c --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/Chart.yaml @@ -0,0 +1,23 @@ +annotations: + charts.openshift.io/name: HashiCorp Vault +apiVersion: v2 +appVersion: 1.13.1 +description: Official HashiCorp Vault Chart +home: https://www.vaultproject.io +icon: https://github.com/hashicorp/vault/raw/f22d202cde2018f9455dec755118a9b84586e082/Vault_PrimaryLogo_Black.png +keywords: +- vault +- security +- encryption +- secrets +- management +- automation +- infrastructure +kubeVersion: '>= 1.22.0-0' +name: vault +sources: +- https://github.com/hashicorp/vault +- https://github.com/hashicorp/vault-helm +- https://github.com/hashicorp/vault-k8s +- https://github.com/hashicorp/vault-csi-provider +version: 0.24.1 diff --git a/charts/partners/hashicorp/vault/0.24.1/src/LICENSE b/charts/partners/hashicorp/vault/0.24.1/src/LICENSE new file mode 100644 index 00000000..74f38c01 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/LICENSE @@ -0,0 +1,355 @@ +Copyright (c) 2018 HashiCorp, Inc. + +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. “Contributor” + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. “Contributor Version” + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution” + + means Covered Software of a particular Contributor. + +1.4. “Covered Software” + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. “Incompatible With Secondary Licenses” + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of version + 1.1 or earlier of the License, but not also under the terms of a + Secondary License. + +1.6. “Executable Form” + + means any form of the work other than Source Code Form. + +1.7. “Larger Work” + + means a work that combines Covered Software with other material, in a separate + file or files, that is not Covered Software. + +1.8. “License” + + means this document. + +1.9. “Licensable” + + means having the right to grant, to the maximum extent possible, whether at the + time of the initial grant or subsequently, any and all of the rights conveyed by + this License. + +1.10. “Modifications” + + means any of the following: + + a. any file in Source Code Form that results from an addition to, deletion + from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. “Patent Claims” of a Contributor + + means any patent claim(s), including without limitation, method, process, + and apparatus claims, in any patent Licensable by such Contributor that + would be infringed, but for the grant of the License, by the making, + using, selling, offering for sale, having made, import, or transfer of + either its Contributions or its Contributor Version. + +1.12. “Secondary License” + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form” + + means the form of the work preferred for making modifications. + +1.14. “You” (or “Your”) + + means an individual or a legal entity exercising rights under this + License. For legal entities, “You” includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, “control” means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or as + part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its Contributions + or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution become + effective for each Contribution on the date the Contributor first distributes + such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under this + License. No additional rights or licenses will be implied from the distribution + or licensing of Covered Software under this License. Notwithstanding Section + 2.1(b) above, no patent license is granted by a Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of its + Contributions. + + This License does not grant any rights in the trademarks, service marks, or + logos of any Contributor (except as may be necessary to comply with the + notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this License + (see Section 10.2) or under the terms of a Secondary License (if permitted + under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its Contributions + are its original creation(s) or it has sufficient rights to grant the + rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under applicable + copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under the + terms of this License. You must inform recipients that the Source Code Form + of the Covered Software is governed by the terms of this License, and how + they can obtain a copy of this License. You may not attempt to alter or + restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this License, + or sublicense it under different terms, provided that the license for + the Executable Form does not attempt to limit or alter the recipients’ + rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for the + Covered Software. If the Larger Work is a combination of Covered Software + with a work governed by one or more Secondary Licenses, and the Covered + Software is not Incompatible With Secondary Licenses, this License permits + You to additionally distribute such Covered Software under the terms of + such Secondary License(s), so that the recipient of the Larger Work may, at + their option, further distribute the Covered Software under the terms of + either this License or such Secondary License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices (including + copyright notices, patent notices, disclaimers of warranty, or limitations + of liability) contained within the Source Code Form of the Covered + Software, except that You may alter any license notices to the extent + required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on behalf + of any Contributor. You must make it absolutely clear that any such + warranty, support, indemnity, or liability obligation is offered by You + alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, judicial + order, or regulation then You must: (a) comply with the terms of this License + to the maximum extent possible; and (b) describe the limitations and the code + they affect. Such description must be placed in a text file included with all + distributions of the Covered Software under this License. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing basis, + if such Contributor fails to notify You of the non-compliance by some + reasonable means prior to 60 days after You have come back into compliance. + Moreover, Your grants from a particular Contributor are reinstated on an + ongoing basis if such Contributor notifies You of the non-compliance by + some reasonable means, this is the first time You have received notice of + non-compliance with this License from such Contributor, and You become + compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, counter-claims, + and cross-claims) alleging that a Contributor Version directly or + indirectly infringes any patent, then the rights granted to You by any and + all Contributors for the Covered Software under Section 2.1 of this License + shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an “as is” basis, without + warranty of any kind, either expressed, implied, or statutory, including, + without limitation, warranties that the Covered Software is free of defects, + merchantable, fit for a particular purpose or non-infringing. The entire + risk as to the quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, You (not any + Contributor) assume the cost of any necessary servicing, repair, or + correction. This disclaimer of warranty constitutes an essential part of this + License. No use of any Covered Software is authorized under this License + except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from such + party’s negligence to the extent applicable law prohibits such limitation. + Some jurisdictions do not allow the exclusion or limitation of incidental or + consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts of + a jurisdiction where the defendant maintains its principal place of business + and such litigation shall be governed by laws of that jurisdiction, without + reference to its conflict-of-law provisions. Nothing in this Section shall + prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. Any law or regulation which provides that the language of a + contract shall be construed against the drafter shall not be used to construe + this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version of + the License under which You originally received the Covered Software, or + under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a relevant +directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is “Incompatible + With Secondary Licenses”, as defined by + the Mozilla Public License, v. 2.0. diff --git a/charts/partners/hashicorp/vault/0.24.1/src/Makefile b/charts/partners/hashicorp/vault/0.24.1/src/Makefile new file mode 100644 index 00000000..56002206 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/Makefile @@ -0,0 +1,101 @@ +TEST_IMAGE?=vault-helm-test +GOOGLE_CREDENTIALS?=vault-helm-test.json +CLOUDSDK_CORE_PROJECT?=vault-helm-dev-246514 +# set to run a single test - e.g acceptance/server-ha-enterprise-dr.bats +ACCEPTANCE_TESTS?=acceptance + +# filter bats unit tests to run. +UNIT_TESTS_FILTER?='.*' + +# set to 'true' to run acceptance tests locally in a kind cluster +LOCAL_ACCEPTANCE_TESTS?=false + +# kind cluster name +KIND_CLUSTER_NAME?=vault-helm + +# kind k8s version +KIND_K8S_VERSION?=v1.26.3 + +# Generate json schema for chart values. See test/README.md for more details. +values-schema: + helm schema-gen values.yaml > values.schema.json + +test-image: + @docker build --rm -t $(TEST_IMAGE) -f $(CURDIR)/test/docker/Test.dockerfile $(CURDIR) + +test-unit: + @docker run --rm -it -v ${PWD}:/helm-test $(TEST_IMAGE) bats -f $(UNIT_TESTS_FILTER) /helm-test/test/unit + +test-bats: test-unit test-acceptance + +test: test-image test-bats + +# run acceptance tests on GKE +# set google project/credential vars above +test-acceptance: +ifeq ($(LOCAL_ACCEPTANCE_TESTS),true) + make setup-kind acceptance +else + @docker run -it -v ${PWD}:/helm-test \ + -e GOOGLE_CREDENTIALS=${GOOGLE_CREDENTIALS} \ + -e CLOUDSDK_CORE_PROJECT=${CLOUDSDK_CORE_PROJECT} \ + -e KUBECONFIG=/helm-test/.kube/config \ + -e VAULT_LICENSE_CI=${VAULT_LICENSE_CI} \ + -w /helm-test \ + $(TEST_IMAGE) \ + make acceptance +endif + +# destroy GKE cluster using terraform +test-destroy: + @docker run -it -v ${PWD}:/helm-test \ + -e GOOGLE_CREDENTIALS=${GOOGLE_CREDENTIALS} \ + -e CLOUDSDK_CORE_PROJECT=${CLOUDSDK_CORE_PROJECT} \ + -w /helm-test \ + $(TEST_IMAGE) \ + make destroy-cluster + +# provision GKE cluster using terraform +test-provision: + @docker run -it -v ${PWD}:/helm-test \ + -e GOOGLE_CREDENTIALS=${GOOGLE_CREDENTIALS} \ + -e CLOUDSDK_CORE_PROJECT=${CLOUDSDK_CORE_PROJECT} \ + -e KUBECONFIG=/helm-test/.kube/config \ + -w /helm-test \ + $(TEST_IMAGE) \ + make provision-cluster + +# this target is for running the acceptance tests +# it is run in the docker container above when the test-acceptance target is invoked +acceptance: +ifneq ($(LOCAL_ACCEPTANCE_TESTS),true) + gcloud auth activate-service-account --key-file=${GOOGLE_CREDENTIALS} +endif + bats --tap --timing test/${ACCEPTANCE_TESTS} + +# this target is for provisioning the GKE cluster +# it is run in the docker container above when the test-provision target is invoked +provision-cluster: + gcloud auth activate-service-account --key-file=${GOOGLE_CREDENTIALS} + terraform init test/terraform + terraform apply -var project=${CLOUDSDK_CORE_PROJECT} -var init_cli=true -auto-approve test/terraform + +# this target is for removing the GKE cluster +# it is run in the docker container above when the test-destroy target is invoked +destroy-cluster: + terraform destroy -auto-approve + +# create a kind cluster for running the acceptance tests locally +setup-kind: + kind get clusters | grep -q "^${KIND_CLUSTER_NAME}$$" || \ + kind create cluster \ + --image kindest/node:${KIND_K8S_VERSION} \ + --name ${KIND_CLUSTER_NAME} \ + --config $(CURDIR)/test/kind/config.yaml + kubectl config use-context kind-${KIND_CLUSTER_NAME} + +# delete the kind cluster +delete-kind: + kind delete cluster --name ${KIND_CLUSTER_NAME} || : + +.PHONY: values-schema test-image test-unit test-bats test test-acceptance test-destroy test-provision acceptance provision-cluster destroy-cluster diff --git a/charts/partners/hashicorp/vault/0.24.1/src/README.md b/charts/partners/hashicorp/vault/0.24.1/src/README.md new file mode 100644 index 00000000..6e701436 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/README.md @@ -0,0 +1,43 @@ +# Vault Helm Chart + +> :warning: **Please note**: We take Vault's security and our users' trust very seriously. If +you believe you have found a security issue in Vault Helm, _please responsibly disclose_ +by contacting us at [security@hashicorp.com](mailto:security@hashicorp.com). + +This repository contains the official HashiCorp Helm chart for installing +and configuring Vault on Kubernetes. This chart supports multiple use +cases of Vault on Kubernetes depending on the values provided. + +For full documentation on this Helm chart along with all the ways you can +use Vault with Kubernetes, please see the +[Vault and Kubernetes documentation](https://www.vaultproject.io/docs/platform/k8s/). + +## Prerequisites + +To use the charts here, [Helm](https://helm.sh/) must be configured for your +Kubernetes cluster. Setting up Kubernetes and Helm is outside the scope of +this README. Please refer to the Kubernetes and Helm documentation. + +The versions required are: + + * **Helm 3.6+** + * **Kubernetes 1.22+** - This is the earliest version of Kubernetes tested. + It is possible that this chart works with earlier versions but it is + untested. + +## Usage + +To install the latest version of this chart, add the Hashicorp helm repository +and run `helm install`: + +```console +$ helm repo add hashicorp https://helm.releases.hashicorp.com +"hashicorp" has been added to your repositories + +$ helm install vault hashicorp/vault +``` + +Please see the many options supported in the `values.yaml` file. These are also +fully documented directly on the [Vault +website](https://www.vaultproject.io/docs/platform/k8s/helm) along with more +detailed installation instructions. diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/NOTES.txt b/charts/partners/hashicorp/vault/0.24.1/src/templates/NOTES.txt new file mode 100644 index 00000000..8e267121 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/NOTES.txt @@ -0,0 +1,14 @@ + +Thank you for installing HashiCorp Vault! + +Now that you have deployed Vault, you should look over the docs on using +Vault with Kubernetes available here: + +https://www.vaultproject.io/docs/ + + +Your release is named {{ .Release.Name }}. To learn more about the release, try: + + $ helm status {{ .Release.Name }} + $ helm get manifest {{ .Release.Name }} + diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/_helpers.tpl b/charts/partners/hashicorp/vault/0.24.1/src/templates/_helpers.tpl new file mode 100644 index 00000000..4b6baf10 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/_helpers.tpl @@ -0,0 +1,968 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to +this (by the DNS naming spec). If release name contains chart name it will +be used as a full name. +*/}} +{{- define "vault.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "vault.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Expand the name of the chart. +*/}} +{{- define "vault.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Compute if the csi driver is enabled. +*/}} +{{- define "vault.csiEnabled" -}} +{{- $_ := set . "csiEnabled" (or + (eq (.Values.csi.enabled | toString) "true") + (and (eq (.Values.csi.enabled | toString) "-") (eq (.Values.global.enabled | toString) "true"))) -}} +{{- end -}} + +{{/* +Compute if the injector is enabled. +*/}} +{{- define "vault.injectorEnabled" -}} +{{- $_ := set . "injectorEnabled" (or + (eq (.Values.injector.enabled | toString) "true") + (and (eq (.Values.injector.enabled | toString) "-") (eq (.Values.global.enabled | toString) "true"))) -}} +{{- end -}} + +{{/* +Compute if the server is enabled. +*/}} +{{- define "vault.serverEnabled" -}} +{{- $_ := set . "serverEnabled" (or + (eq (.Values.server.enabled | toString) "true") + (and (eq (.Values.server.enabled | toString) "-") (eq (.Values.global.enabled | toString) "true"))) -}} +{{- end -}} + +{{/* +Compute if the server serviceaccount is enabled. +*/}} +{{- define "vault.serverServiceAccountEnabled" -}} +{{- $_ := set . "serverServiceAccountEnabled" + (and + (eq (.Values.server.serviceAccount.create | toString) "true" ) + (or + (eq (.Values.server.enabled | toString) "true") + (eq (.Values.global.enabled | toString) "true"))) -}} +{{- end -}} + +{{/* +Compute if the server auth delegator serviceaccount is enabled. +*/}} +{{- define "vault.serverAuthDelegator" -}} +{{- $_ := set . "serverAuthDelegator" + (and + (eq (.Values.server.authDelegator.enabled | toString) "true" ) + (or (eq (.Values.server.serviceAccount.create | toString) "true") + (not (eq .Values.server.serviceAccount.name ""))) + (or + (eq (.Values.server.enabled | toString) "true") + (eq (.Values.global.enabled | toString) "true"))) -}} +{{- end -}} + +{{/* +Compute if the server service is enabled. +*/}} +{{- define "vault.serverServiceEnabled" -}} +{{- template "vault.serverEnabled" . -}} +{{- $_ := set . "serverServiceEnabled" (and .serverEnabled (eq (.Values.server.service.enabled | toString) "true")) -}} +{{- end -}} + +{{/* +Compute if the ui is enabled. +*/}} +{{- define "vault.uiEnabled" -}} +{{- $_ := set . "uiEnabled" (or + (eq (.Values.ui.enabled | toString) "true") + (and (eq (.Values.ui.enabled | toString) "-") (eq (.Values.global.enabled | toString) "true"))) -}} +{{- end -}} + +{{/* +Compute the maximum number of unavailable replicas for the PodDisruptionBudget. +This defaults to (n/2)-1 where n is the number of members of the server cluster. +Add a special case for replicas=1, where it should default to 0 as well. +*/}} +{{- define "vault.pdb.maxUnavailable" -}} +{{- if eq (int .Values.server.ha.replicas) 1 -}} +{{ 0 }} +{{- else if .Values.server.ha.disruptionBudget.maxUnavailable -}} +{{ .Values.server.ha.disruptionBudget.maxUnavailable -}} +{{- else -}} +{{- div (sub (div (mul (int .Values.server.ha.replicas) 10) 2) 1) 10 -}} +{{- end -}} +{{- end -}} + +{{/* +Set the variable 'mode' to the server mode requested by the user to simplify +template logic. +*/}} +{{- define "vault.mode" -}} + {{- template "vault.serverEnabled" . -}} + {{- if or (.Values.injector.externalVaultAddr) (.Values.global.externalVaultAddr) -}} + {{- $_ := set . "mode" "external" -}} + {{- else if not .serverEnabled -}} + {{- $_ := set . "mode" "external" -}} + {{- else if eq (.Values.server.dev.enabled | toString) "true" -}} + {{- $_ := set . "mode" "dev" -}} + {{- else if eq (.Values.server.ha.enabled | toString) "true" -}} + {{- $_ := set . "mode" "ha" -}} + {{- else if or (eq (.Values.server.standalone.enabled | toString) "true") (eq (.Values.server.standalone.enabled | toString) "-") -}} + {{- $_ := set . "mode" "standalone" -}} + {{- else -}} + {{- $_ := set . "mode" "" -}} + {{- end -}} +{{- end -}} + +{{/* +Set's the replica count based on the different modes configured by user +*/}} +{{- define "vault.replicas" -}} + {{ if eq .mode "standalone" }} + {{- default 1 -}} + {{ else if eq .mode "ha" }} + {{- .Values.server.ha.replicas | default 3 -}} + {{ else }} + {{- default 1 -}} + {{ end }} +{{- end -}} + +{{/* +Set's up configmap mounts if this isn't a dev deployment and the user +defined a custom configuration. Additionally iterates over any +extra volumes the user may have specified (such as a secret with TLS). +*/}} +{{- define "vault.volumes" -}} + {{- if and (ne .mode "dev") (or (.Values.server.standalone.config) (.Values.server.ha.config)) }} + - name: config + configMap: + name: {{ template "vault.fullname" . }}-config + {{ end }} + {{- range .Values.server.extraVolumes }} + - name: userconfig-{{ .name }} + {{ .type }}: + {{- if (eq .type "configMap") }} + name: {{ .name }} + {{- else if (eq .type "secret") }} + secretName: {{ .name }} + {{- end }} + defaultMode: {{ .defaultMode | default 420 }} + {{- end }} + {{- if .Values.server.volumes }} + {{- toYaml .Values.server.volumes | nindent 8}} + {{- end }} + {{- if (and .Values.server.enterpriseLicense.secretName .Values.server.enterpriseLicense.secretKey) }} + - name: vault-license + secret: + secretName: {{ .Values.server.enterpriseLicense.secretName }} + defaultMode: 0440 + {{- end }} +{{- end -}} + +{{/* +Set's the args for custom command to render the Vault configuration +file with IP addresses to make the out of box experience easier +for users looking to use this chart with Consul Helm. +*/}} +{{- define "vault.args" -}} + {{ if or (eq .mode "standalone") (eq .mode "ha") }} + - | + cp /vault/config/extraconfig-from-values.hcl /tmp/storageconfig.hcl; + [ -n "${HOST_IP}" ] && sed -Ei "s|HOST_IP|${HOST_IP?}|g" /tmp/storageconfig.hcl; + [ -n "${POD_IP}" ] && sed -Ei "s|POD_IP|${POD_IP?}|g" /tmp/storageconfig.hcl; + [ -n "${HOSTNAME}" ] && sed -Ei "s|HOSTNAME|${HOSTNAME?}|g" /tmp/storageconfig.hcl; + [ -n "${API_ADDR}" ] && sed -Ei "s|API_ADDR|${API_ADDR?}|g" /tmp/storageconfig.hcl; + [ -n "${TRANSIT_ADDR}" ] && sed -Ei "s|TRANSIT_ADDR|${TRANSIT_ADDR?}|g" /tmp/storageconfig.hcl; + [ -n "${RAFT_ADDR}" ] && sed -Ei "s|RAFT_ADDR|${RAFT_ADDR?}|g" /tmp/storageconfig.hcl; + /usr/local/bin/docker-entrypoint.sh vault server -config=/tmp/storageconfig.hcl {{ .Values.server.extraArgs }} + {{ else if eq .mode "dev" }} + - | + /usr/local/bin/docker-entrypoint.sh vault server -dev {{ .Values.server.extraArgs }} + {{ end }} +{{- end -}} + +{{/* +Set's additional environment variables based on the mode. +*/}} +{{- define "vault.envs" -}} + {{ if eq .mode "dev" }} + - name: VAULT_DEV_ROOT_TOKEN_ID + value: {{ .Values.server.dev.devRootToken }} + - name: VAULT_DEV_LISTEN_ADDRESS + value: "[::]:8200" + {{ end }} +{{- end -}} + +{{/* +Set's which additional volumes should be mounted to the container +based on the mode configured. +*/}} +{{- define "vault.mounts" -}} + {{ if eq (.Values.server.auditStorage.enabled | toString) "true" }} + - name: audit + mountPath: {{ .Values.server.auditStorage.mountPath }} + {{ end }} + {{ if or (eq .mode "standalone") (and (eq .mode "ha") (eq (.Values.server.ha.raft.enabled | toString) "true")) }} + {{ if eq (.Values.server.dataStorage.enabled | toString) "true" }} + - name: data + mountPath: {{ .Values.server.dataStorage.mountPath }} + {{ end }} + {{ end }} + {{ if and (ne .mode "dev") (or (.Values.server.standalone.config) (.Values.server.ha.config)) }} + - name: config + mountPath: /vault/config + {{ end }} + {{- range .Values.server.extraVolumes }} + - name: userconfig-{{ .name }} + readOnly: true + mountPath: {{ .path | default "/vault/userconfig" }}/{{ .name }} + {{- end }} + {{- if .Values.server.volumeMounts }} + {{- toYaml .Values.server.volumeMounts | nindent 12}} + {{- end }} + {{- if (and .Values.server.enterpriseLicense.secretName .Values.server.enterpriseLicense.secretKey) }} + - name: vault-license + mountPath: /vault/license + readOnly: true + {{- end }} +{{- end -}} + +{{/* +Set's up the volumeClaimTemplates when data or audit storage is required. HA +might not use data storage since Consul is likely it's backend, however, audit +storage might be desired by the user. +*/}} +{{- define "vault.volumeclaims" -}} + {{- if and (ne .mode "dev") (or .Values.server.dataStorage.enabled .Values.server.auditStorage.enabled) }} + volumeClaimTemplates: + {{- if and (eq (.Values.server.dataStorage.enabled | toString) "true") (or (eq .mode "standalone") (eq (.Values.server.ha.raft.enabled | toString ) "true" )) }} + - metadata: + name: data + {{- include "vault.dataVolumeClaim.annotations" . | nindent 6 }} + spec: + accessModes: + - {{ .Values.server.dataStorage.accessMode | default "ReadWriteOnce" }} + resources: + requests: + storage: {{ .Values.server.dataStorage.size }} + {{- if .Values.server.dataStorage.storageClass }} + storageClassName: {{ .Values.server.dataStorage.storageClass }} + {{- end }} + {{ end }} + {{- if eq (.Values.server.auditStorage.enabled | toString) "true" }} + - metadata: + name: audit + {{- include "vault.auditVolumeClaim.annotations" . | nindent 6 }} + spec: + accessModes: + - {{ .Values.server.auditStorage.accessMode | default "ReadWriteOnce" }} + resources: + requests: + storage: {{ .Values.server.auditStorage.size }} + {{- if .Values.server.auditStorage.storageClass }} + storageClassName: {{ .Values.server.auditStorage.storageClass }} + {{- end }} + {{ end }} + {{ end }} +{{- end -}} + +{{/* +Set's the affinity for pod placement when running in standalone and HA modes. +*/}} +{{- define "vault.affinity" -}} + {{- if and (ne .mode "dev") .Values.server.affinity }} + affinity: + {{ $tp := typeOf .Values.server.affinity }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.affinity . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.server.affinity | nindent 8 }} + {{- end }} + {{ end }} +{{- end -}} + +{{/* +Sets the injector affinity for pod placement +*/}} +{{- define "injector.affinity" -}} + {{- if .Values.injector.affinity }} + affinity: + {{ $tp := typeOf .Values.injector.affinity }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.affinity . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.injector.affinity | nindent 8 }} + {{- end }} + {{ end }} +{{- end -}} + +{{/* +Sets the topologySpreadConstraints when running in standalone and HA modes. +*/}} +{{- define "vault.topologySpreadConstraints" -}} + {{- if and (ne .mode "dev") .Values.server.topologySpreadConstraints }} + topologySpreadConstraints: + {{ $tp := typeOf .Values.server.topologySpreadConstraints }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.topologySpreadConstraints . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.server.topologySpreadConstraints | nindent 8 }} + {{- end }} + {{ end }} +{{- end -}} + + +{{/* +Sets the injector topologySpreadConstraints for pod placement +*/}} +{{- define "injector.topologySpreadConstraints" -}} + {{- if .Values.injector.topologySpreadConstraints }} + topologySpreadConstraints: + {{ $tp := typeOf .Values.injector.topologySpreadConstraints }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.topologySpreadConstraints . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.injector.topologySpreadConstraints | nindent 8 }} + {{- end }} + {{ end }} +{{- end -}} + +{{/* +Sets the toleration for pod placement when running in standalone and HA modes. +*/}} +{{- define "vault.tolerations" -}} + {{- if and (ne .mode "dev") .Values.server.tolerations }} + tolerations: + {{- $tp := typeOf .Values.server.tolerations }} + {{- if eq $tp "string" }} + {{ tpl .Values.server.tolerations . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.server.tolerations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets the injector toleration for pod placement +*/}} +{{- define "injector.tolerations" -}} + {{- if .Values.injector.tolerations }} + tolerations: + {{- $tp := typeOf .Values.injector.tolerations }} + {{- if eq $tp "string" }} + {{ tpl .Values.injector.tolerations . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.injector.tolerations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Set's the node selector for pod placement when running in standalone and HA modes. +*/}} +{{- define "vault.nodeselector" -}} + {{- if and (ne .mode "dev") .Values.server.nodeSelector }} + nodeSelector: + {{- $tp := typeOf .Values.server.nodeSelector }} + {{- if eq $tp "string" }} + {{ tpl .Values.server.nodeSelector . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.server.nodeSelector | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets the injector node selector for pod placement +*/}} +{{- define "injector.nodeselector" -}} + {{- if .Values.injector.nodeSelector }} + nodeSelector: + {{- $tp := typeOf .Values.injector.nodeSelector }} + {{- if eq $tp "string" }} + {{ tpl .Values.injector.nodeSelector . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.injector.nodeSelector | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets the injector deployment update strategy +*/}} +{{- define "injector.strategy" -}} + {{- if .Values.injector.strategy }} + strategy: + {{- $tp := typeOf .Values.injector.strategy }} + {{- if eq $tp "string" }} + {{ tpl .Values.injector.strategy . | nindent 4 | trim }} + {{- else }} + {{- toYaml .Values.injector.strategy | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra pod annotations +*/}} +{{- define "vault.annotations" -}} + {{- if .Values.server.annotations }} + annotations: + {{- $tp := typeOf .Values.server.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.annotations . | nindent 8 }} + {{- else }} + {{- toYaml .Values.server.annotations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra injector pod annotations +*/}} +{{- define "injector.annotations" -}} + {{- if .Values.injector.annotations }} + annotations: + {{- $tp := typeOf .Values.injector.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.annotations . | nindent 8 }} + {{- else }} + {{- toYaml .Values.injector.annotations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra injector service annotations +*/}} +{{- define "injector.service.annotations" -}} + {{- if .Values.injector.service.annotations }} + annotations: + {{- $tp := typeOf .Values.injector.service.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.service.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.injector.service.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +securityContext for the injector pod level. +*/}} +{{- define "injector.securityContext.pod" -}} + {{- if .Values.injector.securityContext.pod }} + securityContext: + {{- $tp := typeOf .Values.injector.securityContext.pod }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.securityContext.pod . | nindent 8 }} + {{- else }} + {{- toYaml .Values.injector.securityContext.pod | nindent 8 }} + {{- end }} + {{- else if not .Values.global.openshift }} + securityContext: + runAsNonRoot: true + runAsGroup: {{ .Values.injector.gid | default 1000 }} + runAsUser: {{ .Values.injector.uid | default 100 }} + fsGroup: {{ .Values.injector.gid | default 1000 }} + {{- end }} +{{- end -}} + +{{/* +securityContext for the injector container level. +*/}} +{{- define "injector.securityContext.container" -}} + {{- if .Values.injector.securityContext.container}} + securityContext: + {{- $tp := typeOf .Values.injector.securityContext.container }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.securityContext.container . | nindent 12 }} + {{- else }} + {{- toYaml .Values.injector.securityContext.container | nindent 12 }} + {{- end }} + {{- else if not .Values.global.openshift }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + {{- end }} +{{- end -}} + +{{/* +securityContext for the statefulset pod template. +*/}} +{{- define "server.statefulSet.securityContext.pod" -}} + {{- if .Values.server.statefulSet.securityContext.pod }} + securityContext: + {{- $tp := typeOf .Values.server.statefulSet.securityContext.pod }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.statefulSet.securityContext.pod . | nindent 8 }} + {{- else }} + {{- toYaml .Values.server.statefulSet.securityContext.pod | nindent 8 }} + {{- end }} + {{- else if not .Values.global.openshift }} + securityContext: + runAsNonRoot: true + runAsGroup: {{ .Values.server.gid | default 1000 }} + runAsUser: {{ .Values.server.uid | default 100 }} + fsGroup: {{ .Values.server.gid | default 1000 }} + {{- end }} +{{- end -}} + +{{/* +securityContext for the statefulset vault container +*/}} +{{- define "server.statefulSet.securityContext.container" -}} + {{- if .Values.server.statefulSet.securityContext.container }} + securityContext: + {{- $tp := typeOf .Values.server.statefulSet.securityContext.container }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.statefulSet.securityContext.container . | nindent 12 }} + {{- else }} + {{- toYaml .Values.server.statefulSet.securityContext.container | nindent 12 }} + {{- end }} + {{- else if not .Values.global.openshift }} + securityContext: + allowPrivilegeEscalation: false + {{- end }} +{{- end -}} + + +{{/* +Sets extra injector service account annotations +*/}} +{{- define "injector.serviceAccount.annotations" -}} + {{- if and (ne .mode "dev") .Values.injector.serviceAccount.annotations }} + annotations: + {{- $tp := typeOf .Values.injector.serviceAccount.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.serviceAccount.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.injector.serviceAccount.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra injector webhook annotations +*/}} +{{- define "injector.webhookAnnotations" -}} + {{- if or (((.Values.injector.webhook)).annotations) (.Values.injector.webhookAnnotations) }} + annotations: + {{- $tp := typeOf (or (((.Values.injector.webhook)).annotations) (.Values.injector.webhookAnnotations)) }} + {{- if eq $tp "string" }} + {{- tpl (((.Values.injector.webhook)).annotations | default .Values.injector.webhookAnnotations) . | nindent 4 }} + {{- else }} + {{- toYaml (((.Values.injector.webhook)).annotations | default .Values.injector.webhookAnnotations) | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Set's the injector webhook objectSelector +*/}} +{{- define "injector.objectSelector" -}} + {{- $v := or (((.Values.injector.webhook)).objectSelector) (.Values.injector.objectSelector) -}} + {{ if $v }} + objectSelector: + {{- $tp := typeOf $v -}} + {{ if eq $tp "string" }} + {{ tpl $v . | indent 6 | trim }} + {{ else }} + {{ toYaml $v | indent 6 | trim }} + {{ end }} + {{ end }} +{{ end }} + +{{/* +Sets extra ui service annotations +*/}} +{{- define "vault.ui.annotations" -}} + {{- if .Values.ui.annotations }} + annotations: + {{- $tp := typeOf .Values.ui.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.ui.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.ui.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "vault.serviceAccount.name" -}} +{{- if .Values.server.serviceAccount.create -}} + {{ default (include "vault.fullname" .) .Values.server.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.server.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Sets extra service account annotations +*/}} +{{- define "vault.serviceAccount.annotations" -}} + {{- if and (ne .mode "dev") .Values.server.serviceAccount.annotations }} + annotations: + {{- $tp := typeOf .Values.server.serviceAccount.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.serviceAccount.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.serviceAccount.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra ingress annotations +*/}} +{{- define "vault.ingress.annotations" -}} + {{- if .Values.server.ingress.annotations }} + annotations: + {{- $tp := typeOf .Values.server.ingress.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.ingress.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.ingress.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra route annotations +*/}} +{{- define "vault.route.annotations" -}} + {{- if .Values.server.route.annotations }} + annotations: + {{- $tp := typeOf .Values.server.route.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.route.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.route.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra vault server Service annotations +*/}} +{{- define "vault.service.annotations" -}} + {{- if .Values.server.service.annotations }} + {{- $tp := typeOf .Values.server.service.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.service.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.service.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets PodSecurityPolicy annotations +*/}} +{{- define "vault.psp.annotations" -}} + {{- if .Values.global.psp.annotations }} + annotations: + {{- $tp := typeOf .Values.global.psp.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.global.psp.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.global.psp.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra statefulset annotations +*/}} +{{- define "vault.statefulSet.annotations" -}} + {{- if .Values.server.statefulSet.annotations }} + annotations: + {{- $tp := typeOf .Values.server.statefulSet.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.statefulSet.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.statefulSet.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets VolumeClaim annotations for data volume +*/}} +{{- define "vault.dataVolumeClaim.annotations" -}} + {{- if and (ne .mode "dev") (.Values.server.dataStorage.enabled) (.Values.server.dataStorage.annotations) }} + annotations: + {{- $tp := typeOf .Values.server.dataStorage.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.dataStorage.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.dataStorage.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets VolumeClaim annotations for audit volume +*/}} +{{- define "vault.auditVolumeClaim.annotations" -}} + {{- if and (ne .mode "dev") (.Values.server.auditStorage.enabled) (.Values.server.auditStorage.annotations) }} + annotations: + {{- $tp := typeOf .Values.server.auditStorage.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.auditStorage.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.auditStorage.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Set's the container resources if the user has set any. +*/}} +{{- define "vault.resources" -}} + {{- if .Values.server.resources -}} + resources: +{{ toYaml .Values.server.resources | indent 12}} + {{ end }} +{{- end -}} + +{{/* +Sets the container resources if the user has set any. +*/}} +{{- define "injector.resources" -}} + {{- if .Values.injector.resources -}} + resources: +{{ toYaml .Values.injector.resources | indent 12}} + {{ end }} +{{- end -}} + +{{/* +Sets the container resources if the user has set any. +*/}} +{{- define "csi.resources" -}} + {{- if .Values.csi.resources -}} + resources: +{{ toYaml .Values.csi.resources | indent 12}} + {{ end }} +{{- end -}} + +{{/* +Sets the container resources for CSI's Agent sidecar if the user has set any. +*/}} +{{- define "csi.agent.resources" -}} + {{- if .Values.csi.agent.resources -}} + resources: +{{ toYaml .Values.csi.agent.resources | indent 12}} + {{ end }} +{{- end -}} + +{{/* +Sets extra CSI daemonset annotations +*/}} +{{- define "csi.daemonSet.annotations" -}} + {{- if .Values.csi.daemonSet.annotations }} + annotations: + {{- $tp := typeOf .Values.csi.daemonSet.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.csi.daemonSet.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.csi.daemonSet.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets CSI daemonset securityContext for pod template +*/}} +{{- define "csi.daemonSet.securityContext.pod" -}} + {{- if .Values.csi.daemonSet.securityContext.pod }} + securityContext: + {{- $tp := typeOf .Values.csi.daemonSet.securityContext.pod }} + {{- if eq $tp "string" }} + {{- tpl .Values.csi.daemonSet.securityContext.pod . | nindent 8 }} + {{- else }} + {{- toYaml .Values.csi.daemonSet.securityContext.pod | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets CSI daemonset securityContext for container +*/}} +{{- define "csi.daemonSet.securityContext.container" -}} + {{- if .Values.csi.daemonSet.securityContext.container }} + securityContext: + {{- $tp := typeOf .Values.csi.daemonSet.securityContext.container }} + {{- if eq $tp "string" }} + {{- tpl .Values.csi.daemonSet.securityContext.container . | nindent 12 }} + {{- else }} + {{- toYaml .Values.csi.daemonSet.securityContext.container | nindent 12 }} + {{- end }} + {{- end }} +{{- end -}} + + +{{/* +Sets the injector toleration for pod placement +*/}} +{{- define "csi.pod.tolerations" -}} + {{- if .Values.csi.pod.tolerations }} + tolerations: + {{- $tp := typeOf .Values.csi.pod.tolerations }} + {{- if eq $tp "string" }} + {{ tpl .Values.csi.pod.tolerations . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.csi.pod.tolerations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra CSI provider pod annotations +*/}} +{{- define "csi.pod.annotations" -}} + {{- if .Values.csi.pod.annotations }} + annotations: + {{- $tp := typeOf .Values.csi.pod.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.csi.pod.annotations . | nindent 8 }} + {{- else }} + {{- toYaml .Values.csi.pod.annotations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra CSI service account annotations +*/}} +{{- define "csi.serviceAccount.annotations" -}} + {{- if .Values.csi.serviceAccount.annotations }} + annotations: + {{- $tp := typeOf .Values.csi.serviceAccount.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.csi.serviceAccount.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.csi.serviceAccount.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Inject extra environment vars in the format key:value, if populated +*/}} +{{- define "vault.extraEnvironmentVars" -}} +{{- if .extraEnvironmentVars -}} +{{- range $key, $value := .extraEnvironmentVars }} +- name: {{ printf "%s" $key | replace "." "_" | upper | quote }} + value: {{ $value | quote }} +{{- end }} +{{- end -}} +{{- end -}} + +{{/* +Inject extra environment populated by secrets, if populated +*/}} +{{- define "vault.extraSecretEnvironmentVars" -}} +{{- if .extraSecretEnvironmentVars -}} +{{- range .extraSecretEnvironmentVars }} +- name: {{ .envName }} + valueFrom: + secretKeyRef: + name: {{ .secretName }} + key: {{ .secretKey }} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* Scheme for health check and local endpoint */}} +{{- define "vault.scheme" -}} +{{- if .Values.global.tlsDisable -}} +{{ "http" }} +{{- else -}} +{{ "https" }} +{{- end -}} +{{- end -}} + +{{/* +imagePullSecrets generates pull secrets from either string or map values. +A map value must be indexable by the key 'name'. +*/}} +{{- define "imagePullSecrets" -}} +{{- with .Values.global.imagePullSecrets -}} +imagePullSecrets: +{{- range . -}} +{{- if typeIs "string" . }} + - name: {{ . }} +{{- else if index . "name" }} + - name: {{ .name }} +{{- end }} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +externalTrafficPolicy sets a Service's externalTrafficPolicy if applicable. +Supported inputs are Values.server.service and Values.ui +*/}} +{{- define "service.externalTrafficPolicy" -}} +{{- $type := "" -}} +{{- if .serviceType -}} +{{- $type = .serviceType -}} +{{- else if .type -}} +{{- $type = .type -}} +{{- end -}} +{{- if and .externalTrafficPolicy (or (eq $type "LoadBalancer") (eq $type "NodePort")) }} + externalTrafficPolicy: {{ .externalTrafficPolicy }} +{{- else }} +{{- end }} +{{- end -}} + +{{/* +loadBalancer configuration for the the UI service. +Supported inputs are Values.ui +*/}} +{{- define "service.loadBalancer" -}} +{{- if eq (.serviceType | toString) "LoadBalancer" }} +{{- if .loadBalancerIP }} + loadBalancerIP: {{ .loadBalancerIP }} +{{- end }} +{{- with .loadBalancerSourceRanges }} + loadBalancerSourceRanges: +{{- range . }} + - {{ . }} +{{- end }} +{{- end -}} +{{- end }} +{{- end -}} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/csi-agent-configmap.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/csi-agent-configmap.yaml new file mode 100644 index 00000000..7af08e8f --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/csi-agent-configmap.yaml @@ -0,0 +1,34 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.csiEnabled" . -}} +{{- if and (.csiEnabled) (eq (.Values.csi.agent.enabled | toString) "true") -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "vault.fullname" . }}-csi-provider-agent-config + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +data: + config.hcl: | + vault { + {{- if .Values.global.externalVaultAddr }} + "address" = "{{ .Values.global.externalVaultAddr }}" + {{- else }} + "address" = "{{ include "vault.scheme" . }}://{{ template "vault.fullname" . }}.{{ .Release.Namespace }}.svc:{{ .Values.server.service.port }}" + {{- end }} + } + + cache {} + + listener "unix" { + address = "/var/run/vault/agent.sock" + tls_disable = true + } +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/csi-clusterrole.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/csi-clusterrole.yaml new file mode 100644 index 00000000..6d979ea4 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/csi-clusterrole.yaml @@ -0,0 +1,23 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.csiEnabled" . -}} +{{- if .csiEnabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "vault.fullname" . }}-csi-provider-clusterrole + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: + - "" + resources: + - serviceaccounts/token + verbs: + - create +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/csi-clusterrolebinding.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/csi-clusterrolebinding.yaml new file mode 100644 index 00000000..d5a93468 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/csi-clusterrolebinding.yaml @@ -0,0 +1,24 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.csiEnabled" . -}} +{{- if .csiEnabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "vault.fullname" . }}-csi-provider-clusterrolebinding + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "vault.fullname" . }}-csi-provider-clusterrole +subjects: +- kind: ServiceAccount + name: {{ template "vault.fullname" . }}-csi-provider + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/csi-daemonset.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/csi-daemonset.yaml new file mode 100644 index 00000000..a32ef7c7 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/csi-daemonset.yaml @@ -0,0 +1,155 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.csiEnabled" . -}} +{{- if .csiEnabled -}} +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: {{ template "vault.fullname" . }}-csi-provider + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- if .Values.csi.daemonSet.extraLabels -}} + {{- toYaml .Values.csi.daemonSet.extraLabels | nindent 4 -}} + {{- end -}} + {{ template "csi.daemonSet.annotations" . }} +spec: + updateStrategy: + type: {{ .Values.csi.daemonSet.updateStrategy.type }} + {{- if .Values.csi.daemonSet.updateStrategy.maxUnavailable }} + rollingUpdate: + maxUnavailable: {{ .Values.csi.daemonSet.updateStrategy.maxUnavailable }} + {{- end }} + selector: + matchLabels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + template: + metadata: + labels: + app.kubernetes.io/name: {{ template "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + {{- if .Values.csi.pod.extraLabels -}} + {{- toYaml .Values.csi.pod.extraLabels | nindent 8 -}} + {{- end -}} + {{ template "csi.pod.annotations" . }} + spec: + {{ template "csi.daemonSet.securityContext.pod" . }} + {{- if .Values.csi.priorityClassName }} + priorityClassName: {{ .Values.csi.priorityClassName }} + {{- end }} + serviceAccountName: {{ template "vault.fullname" . }}-csi-provider + {{- template "csi.pod.tolerations" . }} + containers: + - name: {{ include "vault.name" . }}-csi-provider + {{ template "csi.resources" . }} + {{ template "csi.daemonSet.securityContext.container" . }} + image: "{{ .Values.csi.image.repository }}:{{ .Values.csi.image.tag }}" + imagePullPolicy: {{ .Values.csi.image.pullPolicy }} + args: + - --endpoint=/provider/vault.sock + - --debug={{ .Values.csi.debug }} + {{- if .Values.csi.hmacSecretName }} + - --hmac-secret-name={{ .Values.csi.hmacSecretName }} + {{- else }} + - --hmac-secret-name={{- include "vault.name" . }}-csi-provider-hmac-key + {{- end }} + {{- if .Values.csi.extraArgs }} + {{- toYaml .Values.csi.extraArgs | nindent 12 }} + {{- end }} + env: + - name: VAULT_ADDR + {{- if eq (.Values.csi.agent.enabled | toString) "true" }} + value: "unix:///var/run/vault/agent.sock" + {{- else if .Values.global.externalVaultAddr }} + value: "{{ .Values.global.externalVaultAddr }}" + {{- else }} + value: {{ include "vault.scheme" . }}://{{ template "vault.fullname" . }}.{{ .Release.Namespace }}.svc:{{ .Values.server.service.port }} + {{- end }} + volumeMounts: + - name: providervol + mountPath: "/provider" + {{- if eq (.Values.csi.agent.enabled | toString) "true" }} + - name: agent-unix-socket + mountPath: /var/run/vault + {{- end }} + {{- if .Values.csi.volumeMounts }} + {{- toYaml .Values.csi.volumeMounts | nindent 12}} + {{- end }} + livenessProbe: + httpGet: + path: /health/ready + port: 8080 + failureThreshold: {{ .Values.csi.livenessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.csi.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.csi.livenessProbe.periodSeconds }} + successThreshold: {{ .Values.csi.livenessProbe.successThreshold }} + timeoutSeconds: {{ .Values.csi.livenessProbe.timeoutSeconds }} + readinessProbe: + httpGet: + path: /health/ready + port: 8080 + failureThreshold: {{ .Values.csi.readinessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.csi.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.csi.readinessProbe.periodSeconds }} + successThreshold: {{ .Values.csi.readinessProbe.successThreshold }} + timeoutSeconds: {{ .Values.csi.readinessProbe.timeoutSeconds }} + {{- if eq (.Values.csi.agent.enabled | toString) "true" }} + - name: {{ include "vault.name" . }}-agent + image: "{{ .Values.csi.agent.image.repository }}:{{ .Values.csi.agent.image.tag }}" + imagePullPolicy: {{ .Values.csi.agent.image.pullPolicy }} + {{ template "csi.agent.resources" . }} + command: + - vault + args: + - agent + - -config=/etc/vault/config.hcl + {{- if .Values.csi.agent.extraArgs }} + {{- toYaml .Values.csi.agent.extraArgs | nindent 12 }} + {{- end }} + ports: + - containerPort: 8200 + env: + - name: VAULT_LOG_LEVEL + value: "{{ .Values.csi.agent.logLevel }}" + - name: VAULT_LOG_FORMAT + value: "{{ .Values.csi.agent.logFormat }}" + securityContext: + runAsNonRoot: true + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsUser: 100 + runAsGroup: 1000 + volumeMounts: + - name: agent-config + mountPath: /etc/vault/config.hcl + subPath: config.hcl + readOnly: true + - name: agent-unix-socket + mountPath: /var/run/vault + {{- if .Values.csi.volumeMounts }} + {{- toYaml .Values.csi.volumeMounts | nindent 12 }} + {{- end }} + {{- end }} + volumes: + - name: providervol + hostPath: + path: {{ .Values.csi.daemonSet.providersDir }} + {{- if eq (.Values.csi.agent.enabled | toString) "true" }} + - name: agent-config + configMap: + name: {{ template "vault.fullname" . }}-csi-provider-agent-config + - name: agent-unix-socket + emptyDir: + medium: Memory + {{- end }} + {{- if .Values.csi.volumes }} + {{- toYaml .Values.csi.volumes | nindent 8}} + {{- end }} + {{- include "imagePullSecrets" . | nindent 6 }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/csi-role.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/csi-role.yaml new file mode 100644 index 00000000..dd23af65 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/csi-role.yaml @@ -0,0 +1,31 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.csiEnabled" . -}} +{{- if .csiEnabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "vault.fullname" . }}-csi-provider-role + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get"] + resourceNames: + {{- if .Values.csi.hmacSecretName }} + - {{ .Values.csi.hmacSecretName }} + {{- else }} + - {{ include "vault.name" . }}-csi-provider-hmac-key + {{- end }} +# 'create' permissions cannot be restricted by resource name: +# https://kubernetes.io/docs/reference/access-authn-authz/rbac/#referring-to-resources +- apiGroups: [""] + resources: ["secrets"] + verbs: ["create"] +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/csi-rolebinding.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/csi-rolebinding.yaml new file mode 100644 index 00000000..e61f2dc2 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/csi-rolebinding.yaml @@ -0,0 +1,24 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.csiEnabled" . -}} +{{- if .csiEnabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "vault.fullname" . }}-csi-provider-rolebinding + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "vault.fullname" . }}-csi-provider-role +subjects: +- kind: ServiceAccount + name: {{ template "vault.fullname" . }}-csi-provider + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/csi-serviceaccount.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/csi-serviceaccount.yaml new file mode 100644 index 00000000..25e123ee --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/csi-serviceaccount.yaml @@ -0,0 +1,21 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.csiEnabled" . -}} +{{- if .csiEnabled -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "vault.fullname" . }}-csi-provider + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- if .Values.csi.serviceAccount.extraLabels -}} + {{- toYaml .Values.csi.serviceAccount.extraLabels | nindent 4 -}} + {{- end -}} + {{ template "csi.serviceAccount.annotations" . }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-certs-secret.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-certs-secret.yaml new file mode 100644 index 00000000..3e5ddb7b --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-certs-secret.yaml @@ -0,0 +1,19 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if and (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} +apiVersion: v1 +kind: Secret +metadata: + name: vault-injector-certs + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-clusterrole.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-clusterrole.yaml new file mode 100644 index 00000000..d5682dd7 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-clusterrole.yaml @@ -0,0 +1,24 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-clusterrole + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: ["admissionregistration.k8s.io"] + resources: ["mutatingwebhookconfigurations"] + verbs: + - "get" + - "list" + - "watch" + - "patch" +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-clusterrolebinding.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-clusterrolebinding.yaml new file mode 100644 index 00000000..9253e4f0 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-clusterrolebinding.yaml @@ -0,0 +1,24 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-binding + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "vault.fullname" . }}-agent-injector-clusterrole +subjects: +- kind: ServiceAccount + name: {{ template "vault.fullname" . }}-agent-injector + namespace: {{ .Release.Namespace }} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-deployment.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-deployment.yaml new file mode 100644 index 00000000..7e0101a4 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-deployment.yaml @@ -0,0 +1,171 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +# Deployment for the injector +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "vault.fullname" . }}-agent-injector + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + component: webhook +spec: + replicas: {{ .Values.injector.replicas }} + selector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + component: webhook + {{ template "injector.strategy" . }} + template: + metadata: + labels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + component: webhook + {{- if .Values.injector.extraLabels -}} + {{- toYaml .Values.injector.extraLabels | nindent 8 -}} + {{- end -}} + {{ template "injector.annotations" . }} + spec: + {{ template "injector.affinity" . }} + {{ template "injector.topologySpreadConstraints" . }} + {{ template "injector.tolerations" . }} + {{ template "injector.nodeselector" . }} + {{- if .Values.injector.priorityClassName }} + priorityClassName: {{ .Values.injector.priorityClassName }} + {{- end }} + serviceAccountName: "{{ template "vault.fullname" . }}-agent-injector" + {{ template "injector.securityContext.pod" . -}} + {{- if not .Values.global.openshift }} + hostNetwork: {{ .Values.injector.hostNetwork }} + {{- end }} + containers: + - name: sidecar-injector + {{ template "injector.resources" . }} + image: "{{ .Values.injector.image.repository }}:{{ .Values.injector.image.tag }}" + imagePullPolicy: "{{ .Values.injector.image.pullPolicy }}" + {{- template "injector.securityContext.container" . }} + env: + - name: AGENT_INJECT_LISTEN + value: {{ printf ":%v" .Values.injector.port }} + - name: AGENT_INJECT_LOG_LEVEL + value: {{ .Values.injector.logLevel | default "info" }} + - name: AGENT_INJECT_VAULT_ADDR + {{- if .Values.global.externalVaultAddr }} + value: "{{ .Values.global.externalVaultAddr }}" + {{- else if .Values.injector.externalVaultAddr }} + value: "{{ .Values.injector.externalVaultAddr }}" + {{- else }} + value: {{ include "vault.scheme" . }}://{{ template "vault.fullname" . }}.{{ .Release.Namespace }}.svc:{{ .Values.server.service.port }} + {{- end }} + - name: AGENT_INJECT_VAULT_AUTH_PATH + value: {{ .Values.injector.authPath }} + - name: AGENT_INJECT_VAULT_IMAGE + value: "{{ .Values.injector.agentImage.repository }}:{{ .Values.injector.agentImage.tag }}" + {{- if .Values.injector.certs.secretName }} + - name: AGENT_INJECT_TLS_CERT_FILE + value: "/etc/webhook/certs/{{ .Values.injector.certs.certName }}" + - name: AGENT_INJECT_TLS_KEY_FILE + value: "/etc/webhook/certs/{{ .Values.injector.certs.keyName }}" + {{- else }} + - name: AGENT_INJECT_TLS_AUTO + value: {{ template "vault.fullname" . }}-agent-injector-cfg + - name: AGENT_INJECT_TLS_AUTO_HOSTS + value: {{ template "vault.fullname" . }}-agent-injector-svc,{{ template "vault.fullname" . }}-agent-injector-svc.{{ .Release.Namespace }},{{ template "vault.fullname" . }}-agent-injector-svc.{{ .Release.Namespace }}.svc + {{- end }} + - name: AGENT_INJECT_LOG_FORMAT + value: {{ .Values.injector.logFormat | default "standard" }} + - name: AGENT_INJECT_REVOKE_ON_SHUTDOWN + value: "{{ .Values.injector.revokeOnShutdown | default false }}" + {{- if .Values.global.openshift }} + - name: AGENT_INJECT_SET_SECURITY_CONTEXT + value: "false" + {{- end }} + {{- if .Values.injector.metrics.enabled }} + - name: AGENT_INJECT_TELEMETRY_PATH + value: "/metrics" + {{- end }} + {{- if and (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} + - name: AGENT_INJECT_USE_LEADER_ELECTOR + value: "true" + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- end }} + - name: AGENT_INJECT_CPU_REQUEST + value: "{{ .Values.injector.agentDefaults.cpuRequest }}" + - name: AGENT_INJECT_CPU_LIMIT + value: "{{ .Values.injector.agentDefaults.cpuLimit }}" + - name: AGENT_INJECT_MEM_REQUEST + value: "{{ .Values.injector.agentDefaults.memRequest }}" + - name: AGENT_INJECT_MEM_LIMIT + value: "{{ .Values.injector.agentDefaults.memLimit }}" + - name: AGENT_INJECT_DEFAULT_TEMPLATE + value: "{{ .Values.injector.agentDefaults.template }}" + - name: AGENT_INJECT_TEMPLATE_CONFIG_EXIT_ON_RETRY_FAILURE + value: "{{ .Values.injector.agentDefaults.templateConfig.exitOnRetryFailure }}" + {{- if .Values.injector.agentDefaults.templateConfig.staticSecretRenderInterval }} + - name: AGENT_INJECT_TEMPLATE_STATIC_SECRET_RENDER_INTERVAL + value: "{{ .Values.injector.agentDefaults.templateConfig.staticSecretRenderInterval }}" + {{- end }} + {{- include "vault.extraEnvironmentVars" .Values.injector | nindent 12 }} + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + args: + - agent-inject + - 2>&1 + livenessProbe: + httpGet: + path: /health/ready + port: {{ .Values.injector.port }} + scheme: HTTPS + failureThreshold: {{ .Values.injector.livenessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.injector.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.injector.livenessProbe.periodSeconds }} + successThreshold: {{ .Values.injector.livenessProbe.successThreshold }} + timeoutSeconds: {{ .Values.injector.livenessProbe.timeoutSeconds }} + readinessProbe: + httpGet: + path: /health/ready + port: {{ .Values.injector.port }} + scheme: HTTPS + failureThreshold: {{ .Values.injector.readinessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.injector.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.injector.readinessProbe.periodSeconds }} + successThreshold: {{ .Values.injector.readinessProbe.successThreshold }} + timeoutSeconds: {{ .Values.injector.readinessProbe.timeoutSeconds }} + startupProbe: + httpGet: + path: /health/ready + port: {{ .Values.injector.port }} + scheme: HTTPS + failureThreshold: {{ .Values.injector.startupProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.injector.startupProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.injector.startupProbe.periodSeconds }} + successThreshold: {{ .Values.injector.startupProbe.successThreshold }} + timeoutSeconds: {{ .Values.injector.startupProbe.timeoutSeconds }} +{{- if .Values.injector.certs.secretName }} + volumeMounts: + - name: webhook-certs + mountPath: /etc/webhook/certs + readOnly: true +{{- end }} +{{- if .Values.injector.certs.secretName }} + volumes: + - name: webhook-certs + secret: + secretName: "{{ .Values.injector.certs.secretName }}" +{{- end }} + {{- include "imagePullSecrets" . | nindent 6 }} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-disruptionbudget.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-disruptionbudget.yaml new file mode 100644 index 00000000..6ae714ba --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-disruptionbudget.yaml @@ -0,0 +1,25 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- if .Values.injector.podDisruptionBudget }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ template "vault.fullname" . }}-agent-injector + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + component: webhook +spec: + selector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + component: webhook + {{- toYaml .Values.injector.podDisruptionBudget | nindent 2 }} +{{- end -}} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-mutating-webhook.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-mutating-webhook.yaml new file mode 100644 index 00000000..d03cd136 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-mutating-webhook.yaml @@ -0,0 +1,44 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if .Capabilities.APIVersions.Has "admissionregistration.k8s.io/v1" }} +apiVersion: admissionregistration.k8s.io/v1 +{{- else }} +apiVersion: admissionregistration.k8s.io/v1beta1 +{{- end }} +kind: MutatingWebhookConfiguration +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-cfg + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- template "injector.webhookAnnotations" . }} +webhooks: + - name: vault.hashicorp.com + failurePolicy: {{ ((.Values.injector.webhook)).failurePolicy | default .Values.injector.failurePolicy }} + matchPolicy: {{ ((.Values.injector.webhook)).matchPolicy | default "Exact" }} + sideEffects: None + timeoutSeconds: {{ ((.Values.injector.webhook)).timeoutSeconds | default "30" }} + admissionReviewVersions: ["v1", "v1beta1"] + clientConfig: + service: + name: {{ template "vault.fullname" . }}-agent-injector-svc + namespace: {{ .Release.Namespace }} + path: "/mutate" + caBundle: {{ .Values.injector.certs.caBundle | quote }} + rules: + - operations: ["CREATE", "UPDATE"] + apiGroups: [""] + apiVersions: ["v1"] + resources: ["pods"] +{{- if or (.Values.injector.namespaceSelector) (((.Values.injector.webhook)).namespaceSelector) }} + namespaceSelector: +{{ toYaml (((.Values.injector.webhook)).namespaceSelector | default .Values.injector.namespaceSelector) | indent 6}} +{{ end }} +{{- template "injector.objectSelector" . -}} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-network-policy.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-network-policy.yaml new file mode 100644 index 00000000..4c3b0878 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-network-policy.yaml @@ -0,0 +1,29 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if eq (.Values.global.openshift | toString) "true" }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ template "vault.fullname" . }}-agent-injector + labels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + component: webhook + ingress: + - from: + - namespaceSelector: {} + ports: + - port: 8080 + protocol: TCP +{{ end }} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-psp-role.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-psp-role.yaml new file mode 100644 index 00000000..65d8e9ba --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-psp-role.yaml @@ -0,0 +1,25 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if eq (.Values.global.psp.enable | toString) "true" }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-psp + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: ['policy'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + - {{ template "vault.fullname" . }}-agent-injector +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-psp-rolebinding.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-psp-rolebinding.yaml new file mode 100644 index 00000000..48a3a26a --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-psp-rolebinding.yaml @@ -0,0 +1,26 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if eq (.Values.global.psp.enable | toString) "true" }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-psp + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + kind: Role + name: {{ template "vault.fullname" . }}-agent-injector-psp + apiGroup: rbac.authorization.k8s.io +subjects: + - kind: ServiceAccount + name: {{ template "vault.fullname" . }}-agent-injector +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-psp.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-psp.yaml new file mode 100644 index 00000000..0eca9a87 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-psp.yaml @@ -0,0 +1,51 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if eq (.Values.global.psp.enable | toString) "true" }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "vault.fullname" . }}-agent-injector + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- template "vault.psp.annotations" . }} +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + volumes: + - configMap + - emptyDir + - projected + - secret + - downwardAPI + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + # Require the container to run without root privileges. + rule: MustRunAsNonRoot + seLinux: + # This policy assumes the nodes are using AppArmor rather than SELinux. + rule: RunAsAny + supplementalGroups: + rule: MustRunAs + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + fsGroup: + rule: MustRunAs + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + readOnlyRootFilesystem: false +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-role.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-role.yaml new file mode 100644 index 00000000..df7b0ed7 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-role.yaml @@ -0,0 +1,34 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if and (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-leader-elector-role + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: + - apiGroups: [""] + resources: ["secrets", "configmaps"] + verbs: + - "create" + - "get" + - "watch" + - "list" + - "update" + - apiGroups: [""] + resources: ["pods"] + verbs: + - "get" + - "patch" + - "delete" +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-rolebinding.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-rolebinding.yaml new file mode 100644 index 00000000..0848e43d --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-rolebinding.yaml @@ -0,0 +1,27 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if and (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-leader-elector-binding + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "vault.fullname" . }}-agent-injector-leader-elector-role +subjects: + - kind: ServiceAccount + name: {{ template "vault.fullname" . }}-agent-injector + namespace: {{ .Release.Namespace }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-service.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-service.yaml new file mode 100644 index 00000000..5b206928 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-service.yaml @@ -0,0 +1,27 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-svc + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{ template "injector.service.annotations" . }} +spec: + ports: + - name: https + port: 443 + targetPort: {{ .Values.injector.port }} + selector: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + component: webhook +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-serviceaccount.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-serviceaccount.yaml new file mode 100644 index 00000000..9b5c2f6e --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/injector-serviceaccount.yaml @@ -0,0 +1,18 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "vault.fullname" . }}-agent-injector + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{ template "injector.serviceAccount.annotations" . }} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/prometheus-prometheusrules.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/prometheus-prometheusrules.yaml new file mode 100644 index 00000000..7e58a0e5 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/prometheus-prometheusrules.yaml @@ -0,0 +1,31 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ if and (.Values.serverTelemetry.prometheusRules.rules) + (or (.Values.global.serverTelemetry.prometheusOperator) (.Values.serverTelemetry.prometheusRules.enabled) ) +}} +--- +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ template "vault.fullname" . }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- /* update the selectors docs in values.yaml whenever the defaults below change. */ -}} + {{- $selectors := .Values.serverTelemetry.prometheusRules.selectors }} + {{- if $selectors }} + {{- toYaml $selectors | nindent 4 }} + {{- else }} + release: prometheus + {{- end }} +spec: + groups: + - name: {{ include "vault.fullname" . }} + rules: + {{- toYaml .Values.serverTelemetry.prometheusRules.rules | nindent 6 }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/prometheus-servicemonitor.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/prometheus-servicemonitor.yaml new file mode 100644 index 00000000..60f2729a --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/prometheus-servicemonitor.yaml @@ -0,0 +1,49 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{ if or (.Values.global.serverTelemetry.prometheusOperator) (.Values.serverTelemetry.serviceMonitor.enabled) }} +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ template "vault.fullname" . }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- /* update the selectors docs in values.yaml whenever the defaults below change. */ -}} + {{- $selectors := .Values.serverTelemetry.serviceMonitor.selectors }} + {{- if $selectors }} + {{- toYaml $selectors | nindent 4 }} + {{- else }} + release: prometheus + {{- end }} +spec: + selector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + {{- if eq .mode "ha" }} + vault-active: "true" + {{- else }} + vault-internal: "true" + {{- end }} + endpoints: + - port: {{ include "vault.scheme" . }} + interval: {{ .Values.serverTelemetry.serviceMonitor.interval }} + scrapeTimeout: {{ .Values.serverTelemetry.serviceMonitor.scrapeTimeout }} + scheme: {{ include "vault.scheme" . | lower }} + path: /v1/sys/metrics + params: + format: + - prometheus + tlsConfig: + insecureSkipVerify: true + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/server-clusterrolebinding.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-clusterrolebinding.yaml new file mode 100644 index 00000000..b694129b --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-clusterrolebinding.yaml @@ -0,0 +1,29 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.serverAuthDelegator" . }} +{{- if .serverAuthDelegator -}} +{{- if .Capabilities.APIVersions.Has "rbac.authorization.k8s.io/v1" -}} +apiVersion: rbac.authorization.k8s.io/v1 +{{- else }} +apiVersion: rbac.authorization.k8s.io/v1beta1 +{{- end }} +kind: ClusterRoleBinding +metadata: + name: {{ template "vault.fullname" . }}-server-binding + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: +- kind: ServiceAccount + name: {{ template "vault.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{ end }} \ No newline at end of file diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/server-config-configmap.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-config-configmap.yaml new file mode 100644 index 00000000..5d29e98d --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-config-configmap.yaml @@ -0,0 +1,45 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if .serverEnabled -}} +{{- if ne .mode "dev" -}} +{{ if or (.Values.server.standalone.config) (.Values.server.ha.config) -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "vault.fullname" . }}-config + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +data: + extraconfig-from-values.hcl: |- + {{- if or (eq .mode "ha") (eq .mode "standalone") }} + {{- $type := typeOf (index .Values.server .mode).config }} + {{- if eq $type "string" }} + disable_mlock = true + {{- if eq .mode "standalone" }} + {{ tpl .Values.server.standalone.config . | nindent 4 | trim }} + {{- else if and (eq .mode "ha") (eq (.Values.server.ha.raft.enabled | toString) "false") }} + {{ tpl .Values.server.ha.config . | nindent 4 | trim }} + {{- else if and (eq .mode "ha") (eq (.Values.server.ha.raft.enabled | toString) "true") }} + {{ tpl .Values.server.ha.raft.config . | nindent 4 | trim }} + {{ end }} + {{- else }} + {{- if and (eq .mode "ha") (eq (.Values.server.ha.raft.enabled | toString) "true") }} +{{ merge (dict "disable_mlock" true) (index .Values.server .mode).raft.config | toPrettyJson | indent 4 }} + {{- else }} +{{ merge (dict "disable_mlock" true) (index .Values.server .mode).config | toPrettyJson | indent 4 }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/server-discovery-role.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-discovery-role.yaml new file mode 100644 index 00000000..adae42a2 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-discovery-role.yaml @@ -0,0 +1,26 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if .serverEnabled -}} +{{- if eq .mode "ha" }} +{{- if eq (.Values.server.serviceAccount.serviceDiscovery.enabled | toString) "true" }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + namespace: {{ .Release.Namespace }} + name: {{ template "vault.fullname" . }}-discovery-role + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: [""] + resources: ["pods"] + verbs: ["get", "watch", "list", "update", "patch"] +{{ end }} +{{ end }} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/server-discovery-rolebinding.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-discovery-rolebinding.yaml new file mode 100644 index 00000000..853ee870 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-discovery-rolebinding.yaml @@ -0,0 +1,34 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if .serverEnabled -}} +{{- if eq .mode "ha" }} +{{- if eq (.Values.server.serviceAccount.serviceDiscovery.enabled | toString) "true" }} +{{- if .Capabilities.APIVersions.Has "rbac.authorization.k8s.io/v1" -}} +apiVersion: rbac.authorization.k8s.io/v1 +{{- else }} +apiVersion: rbac.authorization.k8s.io/v1beta1 +{{- end }} +kind: RoleBinding +metadata: + name: {{ template "vault.fullname" . }}-discovery-rolebinding + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "vault.fullname" . }}-discovery-role +subjects: +- kind: ServiceAccount + name: {{ template "vault.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{ end }} +{{ end }} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/server-disruptionbudget.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-disruptionbudget.yaml new file mode 100644 index 00000000..3ff11099 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-disruptionbudget.yaml @@ -0,0 +1,31 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if ne .mode "external" -}} +{{- if .serverEnabled -}} +{{- if and (eq .mode "ha") (eq (.Values.server.ha.disruptionBudget.enabled | toString) "true") -}} +# PodDisruptionBudget to prevent degrading the server cluster through +# voluntary cluster changes. +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ template "vault.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +spec: + maxUnavailable: {{ template "vault.pdb.maxUnavailable" . }} + selector: + matchLabels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/server-ha-active-service.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-ha-active-service.yaml new file mode 100644 index 00000000..58d540fd --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-ha-active-service.yaml @@ -0,0 +1,55 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- template "vault.serverServiceEnabled" . -}} +{{- if .serverServiceEnabled -}} +{{- if eq .mode "ha" }} +{{- if eq (.Values.server.service.active.enabled | toString) "true" }} +# Service for active Vault pod +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }}-active + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + vault-active: "true" + annotations: +{{ template "vault.service.annotations" .}} +spec: + {{- if .Values.server.service.type}} + type: {{ .Values.server.service.type }} + {{- end}} + {{- if .Values.server.service.clusterIP }} + clusterIP: {{ .Values.server.service.clusterIP }} + {{- end }} + {{- include "service.externalTrafficPolicy" .Values.server.service }} + publishNotReadyAddresses: {{ .Values.server.service.publishNotReadyAddresses }} + ports: + - name: {{ include "vault.scheme" . }} + port: {{ .Values.server.service.port }} + targetPort: {{ .Values.server.service.targetPort }} + {{- if and (.Values.server.service.activeNodePort) (eq (.Values.server.service.type | toString) "NodePort") }} + nodePort: {{ .Values.server.service.activeNodePort }} + {{- end }} + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: {{ include "vault.name" . }} + {{- if eq (.Values.server.service.instanceSelector.enabled | toString) "true" }} + app.kubernetes.io/instance: {{ .Release.Name }} + {{- end }} + component: server + vault-active: "true" +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/server-ha-standby-service.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-ha-standby-service.yaml new file mode 100644 index 00000000..b9f64358 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-ha-standby-service.yaml @@ -0,0 +1,54 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- template "vault.serverServiceEnabled" . -}} +{{- if .serverServiceEnabled -}} +{{- if eq .mode "ha" }} +{{- if eq (.Values.server.service.standby.enabled | toString) "true" }} +# Service for standby Vault pod +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }}-standby + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + annotations: +{{ template "vault.service.annotations" .}} +spec: + {{- if .Values.server.service.type}} + type: {{ .Values.server.service.type }} + {{- end}} + {{- if .Values.server.service.clusterIP }} + clusterIP: {{ .Values.server.service.clusterIP }} + {{- end }} + {{- include "service.externalTrafficPolicy" .Values.server.service }} + publishNotReadyAddresses: {{ .Values.server.service.publishNotReadyAddresses }} + ports: + - name: {{ include "vault.scheme" . }} + port: {{ .Values.server.service.port }} + targetPort: {{ .Values.server.service.targetPort }} + {{- if and (.Values.server.service.standbyNodePort) (eq (.Values.server.service.type | toString) "NodePort") }} + nodePort: {{ .Values.server.service.standbyNodePort }} + {{- end }} + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: {{ include "vault.name" . }} + {{- if eq (.Values.server.service.instanceSelector.enabled | toString) "true" }} + app.kubernetes.io/instance: {{ .Release.Name }} + {{- end }} + component: server + vault-active: "false" +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/server-headless-service.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-headless-service.yaml new file mode 100644 index 00000000..c4eca7af --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-headless-service.yaml @@ -0,0 +1,39 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- template "vault.serverServiceEnabled" . -}} +{{- if .serverServiceEnabled -}} +# Service for Vault cluster +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }}-internal + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + vault-internal: "true" + annotations: +{{ template "vault.service.annotations" .}} +spec: + clusterIP: None + publishNotReadyAddresses: {{ .Values.server.service.publishNotReadyAddresses }} + ports: + - name: "{{ include "vault.scheme" . }}" + port: {{ .Values.server.service.port }} + targetPort: {{ .Values.server.service.targetPort }} + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/server-ingress.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-ingress.yaml new file mode 100644 index 00000000..3aba6688 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-ingress.yaml @@ -0,0 +1,69 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- if not .Values.global.openshift }} +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if .Values.server.ingress.enabled -}} +{{- $extraPaths := .Values.server.ingress.extraPaths -}} +{{- $serviceName := include "vault.fullname" . -}} +{{- template "vault.serverServiceEnabled" . -}} +{{- if .serverServiceEnabled -}} +{{- if and (eq .mode "ha" ) (eq (.Values.server.ingress.activeService | toString) "true") }} +{{- $serviceName = printf "%s-%s" $serviceName "active" -}} +{{- end }} +{{- $servicePort := .Values.server.service.port -}} +{{- $pathType := .Values.server.ingress.pathType -}} +{{- $kubeVersion := .Capabilities.KubeVersion.Version }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ template "vault.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- with .Values.server.ingress.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- template "vault.ingress.annotations" . }} +spec: +{{- if .Values.server.ingress.tls }} + tls: + {{- range .Values.server.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} +{{- if .Values.server.ingress.ingressClassName }} + ingressClassName: {{ .Values.server.ingress.ingressClassName }} +{{- end }} + rules: + {{- range .Values.server.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: +{{ if $extraPaths }} +{{ toYaml $extraPaths | indent 10 }} +{{- end }} + {{- range (.paths | default (list "/")) }} + - path: {{ . }} + pathType: {{ $pathType }} + backend: + service: + name: {{ $serviceName }} + port: + number: {{ $servicePort }} + {{- end }} + {{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/server-network-policy.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-network-policy.yaml new file mode 100644 index 00000000..62d4ae1a --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-network-policy.yaml @@ -0,0 +1,31 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- if eq (.Values.server.networkPolicy.enabled | toString) "true" }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ template "vault.fullname" . }} + labels: + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + ingress: + - from: + - namespaceSelector: {} + ports: + - port: 8200 + protocol: TCP + - port: 8201 + protocol: TCP + {{- if .Values.server.networkPolicy.egress }} + egress: + {{- toYaml .Values.server.networkPolicy.egress | nindent 4 }} + {{ end }} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/server-psp-role.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-psp-role.yaml new file mode 100644 index 00000000..0c8c983e --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-psp-role.yaml @@ -0,0 +1,25 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if .serverEnabled -}} +{{- if and (ne .mode "") (eq (.Values.global.psp.enable | toString) "true") }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "vault.fullname" . }}-psp + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: ['policy'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + - {{ template "vault.fullname" . }} +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/server-psp-rolebinding.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-psp-rolebinding.yaml new file mode 100644 index 00000000..9b975d55 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-psp-rolebinding.yaml @@ -0,0 +1,26 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if .serverEnabled -}} +{{- if and (ne .mode "") (eq (.Values.global.psp.enable | toString) "true") }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "vault.fullname" . }}-psp + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + kind: Role + name: {{ template "vault.fullname" . }}-psp + apiGroup: rbac.authorization.k8s.io +subjects: + - kind: ServiceAccount + name: {{ template "vault.fullname" . }} +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/server-psp.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-psp.yaml new file mode 100644 index 00000000..567e6624 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-psp.yaml @@ -0,0 +1,54 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if .serverEnabled -}} +{{- if and (ne .mode "") (eq (.Values.global.psp.enable | toString) "true") }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "vault.fullname" . }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- template "vault.psp.annotations" . }} +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + volumes: + - configMap + - emptyDir + - projected + - secret + - downwardAPI + {{- if eq (.Values.server.dataStorage.enabled | toString) "true" }} + - persistentVolumeClaim + {{- end }} + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + # Require the container to run without root privileges. + rule: MustRunAsNonRoot + seLinux: + # This policy assumes the nodes are using AppArmor rather than SELinux. + rule: RunAsAny + supplementalGroups: + rule: MustRunAs + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + fsGroup: + rule: MustRunAs + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + readOnlyRootFilesystem: false +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/server-route.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-route.yaml new file mode 100644 index 00000000..3f35aefe --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-route.yaml @@ -0,0 +1,39 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- if .Values.global.openshift }} +{{- if ne .mode "external" }} +{{- if .Values.server.route.enabled -}} +{{- $serviceName := include "vault.fullname" . -}} +{{- if and (eq .mode "ha" ) (eq (.Values.server.route.activeService | toString) "true") }} +{{- $serviceName = printf "%s-%s" $serviceName "active" -}} +{{- end }} +kind: Route +apiVersion: route.openshift.io/v1 +metadata: + name: {{ template "vault.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- with .Values.server.route.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- template "vault.route.annotations" . }} +spec: + host: {{ .Values.server.route.host }} + to: + kind: Service + name: {{ $serviceName }} + weight: 100 + port: + targetPort: 8200 + tls: + {{- toYaml .Values.server.route.tls | nindent 4 }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/server-service.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-service.yaml new file mode 100644 index 00000000..8e34c88c --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-service.yaml @@ -0,0 +1,51 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- template "vault.serverServiceEnabled" . -}} +{{- if .serverServiceEnabled -}} +# Service for Vault cluster +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + annotations: +{{ template "vault.service.annotations" .}} +spec: + {{- if .Values.server.service.type}} + type: {{ .Values.server.service.type }} + {{- end}} + {{- if .Values.server.service.clusterIP }} + clusterIP: {{ .Values.server.service.clusterIP }} + {{- end }} + {{- include "service.externalTrafficPolicy" .Values.server.service }} + # We want the servers to become available even if they're not ready + # since this DNS is also used for join operations. + publishNotReadyAddresses: {{ .Values.server.service.publishNotReadyAddresses }} + ports: + - name: {{ include "vault.scheme" . }} + port: {{ .Values.server.service.port }} + targetPort: {{ .Values.server.service.targetPort }} + {{- if and (.Values.server.service.nodePort) (eq (.Values.server.service.type | toString) "NodePort") }} + nodePort: {{ .Values.server.service.nodePort }} + {{- end }} + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: {{ include "vault.name" . }} + {{- if eq (.Values.server.service.instanceSelector.enabled | toString) "true" }} + app.kubernetes.io/instance: {{ .Release.Name }} + {{- end }} + component: server +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/server-serviceaccount.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-serviceaccount.yaml new file mode 100644 index 00000000..e154f8dc --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-serviceaccount.yaml @@ -0,0 +1,22 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.serverServiceAccountEnabled" . }} +{{- if .serverServiceAccountEnabled -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "vault.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- if .Values.server.serviceAccount.extraLabels -}} + {{- toYaml .Values.server.serviceAccount.extraLabels | nindent 4 -}} + {{- end -}} + {{ template "vault.serviceAccount.annotations" . }} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/server-statefulset.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-statefulset.yaml new file mode 100644 index 00000000..7ab7de8e --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/server-statefulset.yaml @@ -0,0 +1,217 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if ne .mode "" }} +{{- if .serverEnabled -}} +# StatefulSet to run the actual vault server cluster. +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ template "vault.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- template "vault.statefulSet.annotations" . }} +spec: + serviceName: {{ template "vault.fullname" . }}-internal + podManagementPolicy: Parallel + replicas: {{ template "vault.replicas" . }} + updateStrategy: + type: {{ .Values.server.updateStrategyType }} + selector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server + template: + metadata: + labels: + helm.sh/chart: {{ template "vault.chart" . }} + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server + {{- if .Values.server.extraLabels -}} + {{- toYaml .Values.server.extraLabels | nindent 8 -}} + {{- end -}} + {{ template "vault.annotations" . }} + spec: + {{ template "vault.affinity" . }} + {{ template "vault.topologySpreadConstraints" . }} + {{ template "vault.tolerations" . }} + {{ template "vault.nodeselector" . }} + {{- if .Values.server.priorityClassName }} + priorityClassName: {{ .Values.server.priorityClassName }} + {{- end }} + terminationGracePeriodSeconds: {{ .Values.server.terminationGracePeriodSeconds }} + serviceAccountName: {{ template "vault.serviceAccount.name" . }} + {{ if .Values.server.shareProcessNamespace }} + shareProcessNamespace: true + {{ end }} + {{- template "server.statefulSet.securityContext.pod" . }} + {{- if not .Values.global.openshift }} + hostNetwork: {{ .Values.server.hostNetwork }} + {{- end }} + volumes: + {{ template "vault.volumes" . }} + - name: home + emptyDir: {} + {{- if .Values.server.extraInitContainers }} + initContainers: + {{ toYaml .Values.server.extraInitContainers | nindent 8}} + {{- end }} + containers: + - name: vault + {{ template "vault.resources" . }} + image: {{ .Values.server.image.repository }}:{{ .Values.server.image.tag | default "latest" }} + imagePullPolicy: {{ .Values.server.image.pullPolicy }} + command: + - "/bin/sh" + - "-ec" + args: {{ template "vault.args" . }} + {{- template "server.statefulSet.securityContext.container" . }} + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: VAULT_K8S_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: VAULT_K8S_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: VAULT_ADDR + value: "{{ include "vault.scheme" . }}://127.0.0.1:8200" + - name: VAULT_API_ADDR + {{- if .Values.server.ha.apiAddr }} + value: {{ .Values.server.ha.apiAddr }} + {{- else }} + value: "{{ include "vault.scheme" . }}://$(POD_IP):8200" + {{- end }} + - name: SKIP_CHOWN + value: "true" + - name: SKIP_SETCAP + value: "true" + - name: HOSTNAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: VAULT_CLUSTER_ADDR + {{- if .Values.server.ha.clusterAddr }} + value: {{ .Values.server.ha.clusterAddr | quote }} + {{- else }} + value: "https://$(HOSTNAME).{{ template "vault.fullname" . }}-internal:8201" + {{- end }} + {{- if and (eq (.Values.server.ha.raft.enabled | toString) "true") (eq (.Values.server.ha.raft.setNodeId | toString) "true") }} + - name: VAULT_RAFT_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- end }} + - name: HOME + value: "/home/vault" + {{- if .Values.server.logLevel }} + - name: VAULT_LOG_LEVEL + value: "{{ .Values.server.logLevel }}" + {{- end }} + {{- if .Values.server.logFormat }} + - name: VAULT_LOG_FORMAT + value: "{{ .Values.server.logFormat }}" + {{- end }} + {{- if (and .Values.server.enterpriseLicense.secretName .Values.server.enterpriseLicense.secretKey) }} + - name: VAULT_LICENSE_PATH + value: /vault/license/{{ .Values.server.enterpriseLicense.secretKey }} + {{- end }} + {{ template "vault.envs" . }} + {{- include "vault.extraEnvironmentVars" .Values.server | nindent 12 }} + {{- include "vault.extraSecretEnvironmentVars" .Values.server | nindent 12 }} + volumeMounts: + {{ template "vault.mounts" . }} + - name: home + mountPath: /home/vault + ports: + - containerPort: 8200 + name: {{ include "vault.scheme" . }} + - containerPort: 8201 + name: https-internal + - containerPort: 8202 + name: {{ include "vault.scheme" . }}-rep + {{- if .Values.server.extraPorts -}} + {{ toYaml .Values.server.extraPorts | nindent 12}} + {{- end }} + {{- if .Values.server.readinessProbe.enabled }} + readinessProbe: + {{- if .Values.server.readinessProbe.path }} + httpGet: + path: {{ .Values.server.readinessProbe.path | quote }} + port: {{ .Values.server.readinessProbe.port }} + scheme: {{ include "vault.scheme" . | upper }} + {{- else }} + # Check status; unsealed vault servers return 0 + # The exit code reflects the seal status: + # 0 - unsealed + # 1 - error + # 2 - sealed + exec: + command: ["/bin/sh", "-ec", "vault status -tls-skip-verify"] + {{- end }} + failureThreshold: {{ .Values.server.readinessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.server.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.server.readinessProbe.periodSeconds }} + successThreshold: {{ .Values.server.readinessProbe.successThreshold }} + timeoutSeconds: {{ .Values.server.readinessProbe.timeoutSeconds }} + {{- end }} + {{- if .Values.server.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: {{ .Values.server.livenessProbe.path | quote }} + port: {{ .Values.server.livenessProbe.port }} + scheme: {{ include "vault.scheme" . | upper }} + failureThreshold: {{ .Values.server.livenessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.server.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.server.livenessProbe.periodSeconds }} + successThreshold: {{ .Values.server.livenessProbe.successThreshold }} + timeoutSeconds: {{ .Values.server.livenessProbe.timeoutSeconds }} + {{- end }} + lifecycle: + # Vault container doesn't receive SIGTERM from Kubernetes + # and after the grace period ends, Kube sends SIGKILL. This + # causes issues with graceful shutdowns such as deregistering itself + # from Consul (zombie services). + preStop: + exec: + command: [ + "/bin/sh", "-c", + # Adding a sleep here to give the pod eviction a + # chance to propagate, so requests will not be made + # to this pod while it's terminating + "sleep {{ .Values.server.preStopSleepSeconds }} && kill -SIGTERM $(pidof vault)", + ] + {{- if .Values.server.postStart }} + postStart: + exec: + command: + {{- range (.Values.server.postStart) }} + - {{ . | quote }} + {{- end }} + {{- end }} + {{- if .Values.server.extraContainers }} + {{ toYaml .Values.server.extraContainers | nindent 8}} + {{- end }} + {{- include "imagePullSecrets" . | nindent 6 }} + {{ template "vault.volumeclaims" . }} +{{ end }} +{{ end }} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/tests/server-test.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/tests/server-test.yaml new file mode 100644 index 00000000..59b15010 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/tests/server-test.yaml @@ -0,0 +1,56 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if .serverEnabled -}} +apiVersion: v1 +kind: Pod +metadata: + name: "{{ .Release.Name }}-server-test" + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": test +spec: + {{- include "imagePullSecrets" . | nindent 2 }} + containers: + - name: {{ .Release.Name }}-server-test + image: {{ .Values.server.image.repository }}:{{ .Values.server.image.tag | default "latest" }} + imagePullPolicy: {{ .Values.server.image.pullPolicy }} + env: + - name: VAULT_ADDR + value: {{ include "vault.scheme" . }}://{{ template "vault.fullname" . }}.{{ .Release.Namespace }}.svc:{{ .Values.server.service.port }} + {{- include "vault.extraEnvironmentVars" .Values.server | nindent 8 }} + command: + - /bin/sh + - -c + - | + echo "Checking for sealed info in 'vault status' output" + ATTEMPTS=10 + n=0 + until [ "$n" -ge $ATTEMPTS ] + do + echo "Attempt" $n... + vault status -format yaml | grep -E '^sealed: (true|false)' && break + n=$((n+1)) + sleep 5 + done + if [ $n -ge $ATTEMPTS ]; then + echo "timed out looking for sealed info in 'vault status' output" + exit 1 + fi + + exit 0 + volumeMounts: + {{- if .Values.server.volumeMounts }} + {{- toYaml .Values.server.volumeMounts | nindent 8}} + {{- end }} + volumes: + {{- if .Values.server.volumes }} + {{- toYaml .Values.server.volumes | nindent 4}} + {{- end }} + restartPolicy: Never +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/templates/ui-service.yaml b/charts/partners/hashicorp/vault/0.24.1/src/templates/ui-service.yaml new file mode 100644 index 00000000..4b2e8f7e --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/templates/ui-service.yaml @@ -0,0 +1,42 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- template "vault.uiEnabled" . -}} +{{- if .uiEnabled -}} + +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }}-ui + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }}-ui + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- template "vault.ui.annotations" . }} +spec: + selector: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server + {{- if and (.Values.ui.activeVaultPodOnly) (eq .mode "ha") }} + vault-active: "true" + {{- end }} + publishNotReadyAddresses: {{ .Values.ui.publishNotReadyAddresses }} + ports: + - name: {{ include "vault.scheme" . }} + port: {{ .Values.ui.externalPort }} + targetPort: {{ .Values.ui.targetPort }} + {{- if .Values.ui.serviceNodePort }} + nodePort: {{ .Values.ui.serviceNodePort }} + {{- end }} + type: {{ .Values.ui.serviceType }} + {{- include "service.externalTrafficPolicy" .Values.ui }} + {{- include "service.loadBalancer" .Values.ui }} +{{- end -}} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/values.openshift.yaml b/charts/partners/hashicorp/vault/0.24.1/src/values.openshift.yaml new file mode 100644 index 00000000..da71dcfb --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/values.openshift.yaml @@ -0,0 +1,21 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +# These overrides are appropriate defaults for deploying this chart on OpenShift + +global: + openshift: true + +injector: + image: + repository: "registry.connect.redhat.com/hashicorp/vault-k8s" + tag: "1.2.1-ubi" + + agentImage: + repository: "registry.connect.redhat.com/hashicorp/vault" + tag: "1.13.1-ubi" + +server: + image: + repository: "registry.connect.redhat.com/hashicorp/vault" + tag: "1.13.1-ubi" diff --git a/charts/partners/hashicorp/vault/0.24.1/src/values.schema.json b/charts/partners/hashicorp/vault/0.24.1/src/values.schema.json new file mode 100644 index 00000000..44980e16 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/values.schema.json @@ -0,0 +1,1105 @@ +{ + "$schema": "http://json-schema.org/schema#", + "type": "object", + "properties": { + "csi": { + "type": "object", + "properties": { + "agent": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "extraArgs": { + "type": "array" + }, + "image": { + "type": "object", + "properties": { + "pullPolicy": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "logFormat": { + "type": "string" + }, + "logLevel": { + "type": "string" + }, + "resources": { + "type": "object" + } + } + }, + "daemonSet": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "extraLabels": { + "type": "object" + }, + "kubeletRootDir": { + "type": "string" + }, + "providersDir": { + "type": "string" + }, + "securityContext": { + "type": "object", + "properties": { + "container": { + "type": [ + "object", + "string" + ] + }, + "pod": { + "type": [ + "object", + "string" + ] + } + } + }, + "updateStrategy": { + "type": "object", + "properties": { + "maxUnavailable": { + "type": "string" + }, + "type": { + "type": "string" + } + } + } + } + }, + "debug": { + "type": "boolean" + }, + "enabled": { + "type": [ + "boolean", + "string" + ] + }, + "extraArgs": { + "type": "array" + }, + "image": { + "type": "object", + "properties": { + "pullPolicy": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "livenessProbe": { + "type": "object", + "properties": { + "failureThreshold": { + "type": "integer" + }, + "initialDelaySeconds": { + "type": "integer" + }, + "periodSeconds": { + "type": "integer" + }, + "successThreshold": { + "type": "integer" + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "pod": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "extraLabels": { + "type": "object" + }, + "tolerations": { + "type": [ + "null", + "array", + "string" + ] + } + } + }, + "priorityClassName": { + "type": "string" + }, + "readinessProbe": { + "type": "object", + "properties": { + "failureThreshold": { + "type": "integer" + }, + "initialDelaySeconds": { + "type": "integer" + }, + "periodSeconds": { + "type": "integer" + }, + "successThreshold": { + "type": "integer" + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "resources": { + "type": "object" + }, + "serviceAccount": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "extraLabels": { + "type": "object" + } + } + }, + "volumeMounts": { + "type": [ + "null", + "array" + ] + }, + "volumes": { + "type": [ + "null", + "array" + ] + } + } + }, + "global": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "externalVaultAddr": { + "type": "string" + }, + "imagePullSecrets": { + "type": "array" + }, + "openshift": { + "type": "boolean" + }, + "psp": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enable": { + "type": "boolean" + } + } + }, + "tlsDisable": { + "type": "boolean" + } + } + }, + "injector": { + "type": "object", + "properties": { + "affinity": { + "type": [ + "object", + "string" + ] + }, + "agentDefaults": { + "type": "object", + "properties": { + "cpuLimit": { + "type": "string" + }, + "cpuRequest": { + "type": "string" + }, + "memLimit": { + "type": "string" + }, + "memRequest": { + "type": "string" + }, + "template": { + "type": "string" + }, + "templateConfig": { + "type": "object", + "properties": { + "exitOnRetryFailure": { + "type": "boolean" + }, + "staticSecretRenderInterval": { + "type": "string" + } + } + } + } + }, + "agentImage": { + "type": "object", + "properties": { + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "authPath": { + "type": "string" + }, + "certs": { + "type": "object", + "properties": { + "caBundle": { + "type": "string" + }, + "certName": { + "type": "string" + }, + "keyName": { + "type": "string" + }, + "secretName": { + "type": [ + "null", + "string" + ] + } + } + }, + "enabled": { + "type": [ + "boolean", + "string" + ] + }, + "externalVaultAddr": { + "type": "string" + }, + "extraEnvironmentVars": { + "type": "object" + }, + "extraLabels": { + "type": "object" + }, + "failurePolicy": { + "type": "string" + }, + "hostNetwork": { + "type": "boolean" + }, + "image": { + "type": "object", + "properties": { + "pullPolicy": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "leaderElector": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "logFormat": { + "type": "string" + }, + "logLevel": { + "type": "string" + }, + "metrics": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "namespaceSelector": { + "type": "object" + }, + "nodeSelector": { + "type": [ + "null", + "object", + "string" + ] + }, + "objectSelector": { + "type": [ + "object", + "string" + ] + }, + "podDisruptionBudget": { + "type": "object" + }, + "port": { + "type": "integer" + }, + "priorityClassName": { + "type": "string" + }, + "replicas": { + "type": "integer" + }, + "resources": { + "type": "object" + }, + "revokeOnShutdown": { + "type": "boolean" + }, + "securityContext": { + "type": "object", + "properties": { + "container": { + "type": [ + "object", + "string" + ] + }, + "pod": { + "type": [ + "object", + "string" + ] + } + } + }, + "service": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + } + } + }, + "serviceAccount": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + } + } + }, + "strategy": { + "type": [ + "object", + "string" + ] + }, + "tolerations": { + "type": [ + "null", + "array", + "string" + ] + }, + "topologySpreadConstraints": { + "type": [ + "null", + "array", + "string" + ] + }, + "webhook": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "failurePolicy": { + "type": "string" + }, + "matchPolicy": { + "type": "string" + }, + "namespaceSelector": { + "type": "object" + }, + "objectSelector": { + "type": [ + "object", + "string" + ] + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "webhookAnnotations": { + "type": [ + "object", + "string" + ] + } + } + }, + "server": { + "type": "object", + "properties": { + "affinity": { + "type": [ + "object", + "string" + ] + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "auditStorage": { + "type": "object", + "properties": { + "accessMode": { + "type": "string" + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": [ + "boolean", + "string" + ] + }, + "mountPath": { + "type": "string" + }, + "size": { + "type": "string" + }, + "storageClass": { + "type": [ + "null", + "string" + ] + } + } + }, + "authDelegator": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "dataStorage": { + "type": "object", + "properties": { + "accessMode": { + "type": "string" + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": [ + "boolean", + "string" + ] + }, + "mountPath": { + "type": "string" + }, + "size": { + "type": "string" + }, + "storageClass": { + "type": [ + "null", + "string" + ] + } + } + }, + "dev": { + "type": "object", + "properties": { + "devRootToken": { + "type": "string" + }, + "enabled": { + "type": "boolean" + } + } + }, + "enabled": { + "type": [ + "boolean", + "string" + ] + }, + "enterpriseLicense": { + "type": "object", + "properties": { + "secretKey": { + "type": "string" + }, + "secretName": { + "type": "string" + } + } + }, + "extraArgs": { + "type": "string" + }, + "extraPorts": { + "type": [ + "null", + "array" + ] + }, + "extraContainers": { + "type": [ + "null", + "array" + ] + }, + "extraEnvironmentVars": { + "type": "object" + }, + "extraInitContainers": { + "type": [ + "null", + "array" + ] + }, + "extraLabels": { + "type": "object" + }, + "extraSecretEnvironmentVars": { + "type": "array" + }, + "extraVolumes": { + "type": "array" + }, + "ha": { + "type": "object", + "properties": { + "apiAddr": { + "type": [ + "null", + "string" + ] + }, + "clusterAddr": { + "type": [ + "null", + "string" + ] + }, + "config": { + "type": [ + "string", + "object" + ] + }, + "disruptionBudget": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "maxUnavailable": { + "type": [ + "null", + "integer" + ] + } + } + }, + "enabled": { + "type": "boolean" + }, + "raft": { + "type": "object", + "properties": { + "config": { + "type": [ + "string", + "object" + ] + }, + "enabled": { + "type": "boolean" + }, + "setNodeId": { + "type": "boolean" + } + } + }, + "replicas": { + "type": "integer" + } + } + }, + "image": { + "type": "object", + "properties": { + "pullPolicy": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "ingress": { + "type": "object", + "properties": { + "activeService": { + "type": "boolean" + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": "boolean" + }, + "extraPaths": { + "type": "array" + }, + "hosts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "host": { + "type": "string" + }, + "paths": { + "type": "array" + } + } + } + }, + "ingressClassName": { + "type": "string" + }, + "labels": { + "type": "object" + }, + "pathType": { + "type": "string" + }, + "tls": { + "type": "array" + } + } + }, + "livenessProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "failureThreshold": { + "type": "integer" + }, + "initialDelaySeconds": { + "type": "integer" + }, + "path": { + "type": "string" + }, + "periodSeconds": { + "type": "integer" + }, + "successThreshold": { + "type": "integer" + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "logFormat": { + "type": "string" + }, + "logLevel": { + "type": "string" + }, + "networkPolicy": { + "type": "object", + "properties": { + "egress": { + "type": "array" + }, + "enabled": { + "type": "boolean" + } + } + }, + "nodeSelector": { + "type": [ + "null", + "object", + "string" + ] + }, + "postStart": { + "type": "array" + }, + "preStopSleepSeconds": { + "type": "integer" + }, + "priorityClassName": { + "type": "string" + }, + "readinessProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "failureThreshold": { + "type": "integer" + }, + "initialDelaySeconds": { + "type": "integer" + }, + "periodSeconds": { + "type": "integer" + }, + "successThreshold": { + "type": "integer" + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "resources": { + "type": "object" + }, + "route": { + "type": "object", + "properties": { + "activeService": { + "type": "boolean" + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": "boolean" + }, + "host": { + "type": "string" + }, + "labels": { + "type": "object" + }, + "tls": { + "type": "object" + } + } + }, + "service": { + "type": "object", + "properties": { + "active": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": "boolean" + }, + "externalTrafficPolicy": { + "type": "string" + }, + "instanceSelector": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "port": { + "type": "integer" + }, + "publishNotReadyAddresses": { + "type": "boolean" + }, + "standby": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "targetPort": { + "type": "integer" + }, + "nodePort": { + "type": "integer" + }, + "activeNodePort": { + "type": "integer" + }, + "standbyNodePort": { + "type": "integer" + } + } + }, + "serviceAccount": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "create": { + "type": "boolean" + }, + "extraLabels": { + "type": "object" + }, + "name": { + "type": "string" + }, + "serviceDiscovery": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + } + } + }, + "shareProcessNamespace": { + "type": "boolean" + }, + "standalone": { + "type": "object", + "properties": { + "config": { + "type": [ + "string", + "object" + ] + }, + "enabled": { + "type": [ + "string", + "boolean" + ] + } + } + }, + "statefulSet": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "securityContext": { + "type": "object", + "properties": { + "container": { + "type": [ + "object", + "string" + ] + }, + "pod": { + "type": [ + "object", + "string" + ] + } + } + } + } + }, + "terminationGracePeriodSeconds": { + "type": "integer" + }, + "tolerations": { + "type": [ + "null", + "array", + "string" + ] + }, + "topologySpreadConstraints": { + "type": [ + "null", + "array", + "string" + ] + }, + "updateStrategyType": { + "type": "string" + }, + "volumeMounts": { + "type": [ + "null", + "array" + ] + }, + "volumes": { + "type": [ + "null", + "array" + ] + }, + "hostNetwork": { + "type": "boolean" + } + } + }, + "ui": { + "type": "object", + "properties": { + "activeVaultPodOnly": { + "type": "boolean" + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": [ + "boolean", + "string" + ] + }, + "externalPort": { + "type": "integer" + }, + "externalTrafficPolicy": { + "type": "string" + }, + "publishNotReadyAddresses": { + "type": "boolean" + }, + "serviceNodePort": { + "type": [ + "null", + "integer" + ] + }, + "serviceType": { + "type": "string" + }, + "targetPort": { + "type": "integer" + } + } + } + } +} diff --git a/charts/partners/hashicorp/vault/0.24.1/src/values.yaml b/charts/partners/hashicorp/vault/0.24.1/src/values.yaml new file mode 100644 index 00000000..815e5d64 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.24.1/src/values.yaml @@ -0,0 +1,1092 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +# Available parameters and their default values for the Vault chart. + +global: + # enabled is the master enabled switch. Setting this to true or false + # will enable or disable all the components within this chart by default. + enabled: true + # Image pull secret to use for registry authentication. + # Alternatively, the value may be specified as an array of strings. + imagePullSecrets: [] + # imagePullSecrets: + # - name: image-pull-secret + + # TLS for end-to-end encrypted transport + tlsDisable: true + # External vault server address for the injector and CSI provider to use. + # Setting this will disable deployment of a vault server. + externalVaultAddr: "" + # If deploying to OpenShift + openshift: true + # Create PodSecurityPolicy for pods + psp: + enable: false + # Annotation for PodSecurityPolicy. + # This is a multi-line templated string map, and can also be set as YAML. + annotations: | + seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default,runtime/default + apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default + seccomp.security.alpha.kubernetes.io/defaultProfileName: runtime/default + apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default + serverTelemetry: + # Enable integration with the Prometheus Operator + # See the top level serverTelemetry section below before enabling this feature. + prometheusOperator: false +injector: + # True if you want to enable vault agent injection. + # @default: global.enabled + enabled: "-" + replicas: 1 + # Configures the port the injector should listen on + port: 8080 + # If multiple replicas are specified, by default a leader will be determined + # so that only one injector attempts to create TLS certificates. + leaderElector: + enabled: true + # If true, will enable a node exporter metrics endpoint at /metrics. + metrics: + enabled: false + # Deprecated: Please use global.externalVaultAddr instead. + externalVaultAddr: "" + # image sets the repo and tag of the vault-k8s image to use for the injector. + image: + repository: "registry.connect.redhat.com/hashicorp/vault-k8s" + tag: "1.2.1-ubi" + pullPolicy: IfNotPresent + # agentImage sets the repo and tag of the Vault image to use for the Vault Agent + # containers. This should be set to the official Vault image. Vault 1.3.1+ is + # required. + agentImage: + repository: "registry.connect.redhat.com/hashicorp/vault" + tag: "1.13.1-ubi" + # The default values for the injected Vault Agent containers. + agentDefaults: + # For more information on configuring resources, see the K8s documentation: + # https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + cpuLimit: "500m" + cpuRequest: "250m" + memLimit: "128Mi" + memRequest: "64Mi" + # Default template type for secrets when no custom template is specified. + # Possible values include: "json" and "map". + template: "map" + # Default values within Agent's template_config stanza. + templateConfig: + exitOnRetryFailure: true + staticSecretRenderInterval: "" + # Used to define custom livenessProbe settings + livenessProbe: + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 2 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 5 + # How often (in seconds) to perform the probe + periodSeconds: 2 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 5 + # Used to define custom readinessProbe settings + readinessProbe: + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 2 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 5 + # How often (in seconds) to perform the probe + periodSeconds: 2 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 5 + # Used to define custom startupProbe settings + startupProbe: + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 12 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 5 + # How often (in seconds) to perform the probe + periodSeconds: 5 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 5 + # Mount Path of the Vault Kubernetes Auth Method. + authPath: "auth/kubernetes" + # Configures the log verbosity of the injector. + # Supported log levels include: trace, debug, info, warn, error + logLevel: "info" + # Configures the log format of the injector. Supported log formats: "standard", "json". + logFormat: "standard" + # Configures all Vault Agent sidecars to revoke their token when shutting down + revokeOnShutdown: false + webhook: + # Configures failurePolicy of the webhook. The "unspecified" default behaviour depends on the + # API Version of the WebHook. + # To block pod creation while the webhook is unavailable, set the policy to `Fail` below. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy + # + failurePolicy: Ignore + # matchPolicy specifies the approach to accepting changes based on the rules of + # the MutatingWebhookConfiguration. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy + # for more details. + # + matchPolicy: Exact + # timeoutSeconds is the amount of seconds before the webhook request will be ignored + # or fails. + # If it is ignored or fails depends on the failurePolicy + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#timeouts + # for more details. + # + timeoutSeconds: 30 + # namespaceSelector is the selector for restricting the webhook to only + # specific namespaces. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector + # for more details. + # Example: + # namespaceSelector: + # matchLabels: + # sidecar-injector: enabled + namespaceSelector: {} + # objectSelector is the selector for restricting the webhook to only + # specific labels. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector + # for more details. + # Example: + # objectSelector: + # matchLabels: + # vault-sidecar-injector: enabled + objectSelector: | + matchExpressions: + - key: app.kubernetes.io/name + operator: NotIn + values: + - {{ template "vault.name" . }}-agent-injector + # Extra annotations to attach to the webhook + annotations: {} + # Deprecated: please use 'webhook.failurePolicy' instead + # Configures failurePolicy of the webhook. The "unspecified" default behaviour depends on the + # API Version of the WebHook. + # To block pod creation while webhook is unavailable, set the policy to `Fail` below. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy + # + failurePolicy: Ignore + # Deprecated: please use 'webhook.namespaceSelector' instead + # namespaceSelector is the selector for restricting the webhook to only + # specific namespaces. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector + # for more details. + # Example: + # namespaceSelector: + # matchLabels: + # sidecar-injector: enabled + namespaceSelector: {} + # Deprecated: please use 'webhook.objectSelector' instead + # objectSelector is the selector for restricting the webhook to only + # specific labels. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector + # for more details. + # Example: + # objectSelector: + # matchLabels: + # vault-sidecar-injector: enabled + objectSelector: {} + # Deprecated: please use 'webhook.annotations' instead + # Extra annotations to attach to the webhook + webhookAnnotations: {} + certs: + # secretName is the name of the secret that has the TLS certificate and + # private key to serve the injector webhook. If this is null, then the + # injector will default to its automatic management mode that will assign + # a service account to the injector to generate its own certificates. + secretName: null + # caBundle is a base64-encoded PEM-encoded certificate bundle for the CA + # that signed the TLS certificate that the webhook serves. This must be set + # if secretName is non-null unless an external service like cert-manager is + # keeping the caBundle updated. + caBundle: "" + # certName and keyName are the names of the files within the secret for + # the TLS cert and private key, respectively. These have reasonable + # defaults but can be customized if necessary. + certName: tls.crt + keyName: tls.key + # Security context for the pod template and the injector container + # The default pod securityContext is: + # runAsNonRoot: true + # runAsGroup: {{ .Values.injector.gid | default 1000 }} + # runAsUser: {{ .Values.injector.uid | default 100 }} + # fsGroup: {{ .Values.injector.gid | default 1000 }} + # and for container is + # allowPrivilegeEscalation: false + # capabilities: + # drop: + # - ALL + securityContext: + pod: {} + container: {} + resources: {} + # resources: + # requests: + # memory: 256Mi + # cpu: 250m + # limits: + # memory: 256Mi + # cpu: 250m + + # extraEnvironmentVars is a list of extra environment variables to set in the + # injector deployment. + extraEnvironmentVars: {} + # KUBERNETES_SERVICE_HOST: kubernetes.default.svc + + # Affinity Settings for injector pods + # This can either be a multi-line string or YAML matching the PodSpec's affinity field. + # Commenting out or setting as empty the affinity variable, will allow + # deployment of multiple replicas to single node services such as Minikube. + affinity: | + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: "{{ .Release.Name }}" + component: webhook + topologyKey: kubernetes.io/hostname + # Topology settings for injector pods + # ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ + # This should be either a multi-line string or YAML matching the topologySpreadConstraints array + # in a PodSpec. + topologySpreadConstraints: [] + # Toleration Settings for injector pods + # This should be either a multi-line string or YAML matching the Toleration array + # in a PodSpec. + tolerations: [] + # nodeSelector labels for server pod assignment, formatted as a multi-line string or YAML map. + # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + # Example: + # nodeSelector: + # beta.kubernetes.io/arch: amd64 + nodeSelector: {} + # Priority class for injector pods + priorityClassName: "" + # Extra annotations to attach to the injector pods + # This can either be YAML or a YAML-formatted multi-line templated string map + # of the annotations to apply to the injector pods + annotations: {} + # Extra labels to attach to the agent-injector + # This should be a YAML map of the labels to apply to the injector + extraLabels: {} + # Should the injector pods run on the host network (useful when using + # an alternate CNI in EKS) + hostNetwork: false + # Injector service specific config + service: + # Extra annotations to attach to the injector service + annotations: {} + # Injector serviceAccount specific config + serviceAccount: + # Extra annotations to attach to the injector serviceAccount + annotations: {} + # A disruption budget limits the number of pods of a replicated application + # that are down simultaneously from voluntary disruptions + podDisruptionBudget: {} + # podDisruptionBudget: + # maxUnavailable: 1 + + # strategy for updating the deployment. This can be a multi-line string or a + # YAML map. + strategy: {} + # strategy: | + # rollingUpdate: + # maxSurge: 25% + # maxUnavailable: 25% + # type: RollingUpdate +server: + # If true, or "-" with global.enabled true, Vault server will be installed. + # See vault.mode in _helpers.tpl for implementation details. + enabled: "-" + # [Enterprise Only] This value refers to a Kubernetes secret that you have + # created that contains your enterprise license. If you are not using an + # enterprise image or if you plan to introduce the license key via another + # route, then leave secretName blank ("") or set it to null. + # Requires Vault Enterprise 1.8 or later. + enterpriseLicense: + # The name of the Kubernetes secret that holds the enterprise license. The + # secret must be in the same namespace that Vault is installed into. + secretName: "" + # The key within the Kubernetes secret that holds the enterprise license. + secretKey: "license" + # Resource requests, limits, etc. for the server cluster placement. This + # should map directly to the value of the resources field for a PodSpec. + # By default no direct resource request is made. + image: + repository: "registry.connect.redhat.com/hashicorp/vault" + tag: "1.13.1-ubi" + # Overrides the default Image Pull Policy + pullPolicy: IfNotPresent + # Configure the Update Strategy Type for the StatefulSet + # See https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies + updateStrategyType: "OnDelete" + # Configure the logging verbosity for the Vault server. + # Supported log levels include: trace, debug, info, warn, error + logLevel: "" + # Configure the logging format for the Vault server. + # Supported log formats include: standard, json + logFormat: "" + resources: {} + # resources: + # requests: + # memory: 256Mi + # cpu: 250m + # limits: + # memory: 256Mi + # cpu: 250m + + # Ingress allows ingress services to be created to allow external access + # from Kubernetes to access Vault pods. + # If deployment is on OpenShift, the following block is ignored. + # In order to expose the service, use the route section below + ingress: + enabled: false + labels: {} + # traffic: external + annotations: {} + # | + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + # or + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + + # Optionally use ingressClassName instead of deprecated annotation. + # See: https://kubernetes.io/docs/concepts/services-networking/ingress/#deprecated-annotation + ingressClassName: "" + # As of Kubernetes 1.19, all Ingress Paths must have a pathType configured. The default value below should be sufficient in most cases. + # See: https://kubernetes.io/docs/concepts/services-networking/ingress/#path-types for other possible values. + pathType: Prefix + # When HA mode is enabled and K8s service registration is being used, + # configure the ingress to point to the Vault active service. + activeService: true + hosts: + - host: chart-example.local + paths: [] + ## Extra paths to prepend to the host configuration. This is useful when working with annotation based services. + extraPaths: [] + # - path: /* + # backend: + # service: + # name: ssl-redirect + # port: + # number: use-annotation + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + # OpenShift only - create a route to expose the service + # By default the created route will be of type passthrough + route: + enabled: false + # When HA mode is enabled and K8s service registration is being used, + # configure the route to point to the Vault active service. + activeService: true + labels: {} + annotations: {} + host: chart-example.local + # tls will be passed directly to the route's TLS config, which + # can be used to configure other termination methods that terminate + # TLS at the router + tls: + termination: passthrough + # authDelegator enables a cluster role binding to be attached to the service + # account. This cluster role binding can be used to setup Kubernetes auth + # method. https://www.vaultproject.io/docs/auth/kubernetes.html + authDelegator: + enabled: true + # extraInitContainers is a list of init containers. Specified as a YAML list. + # This is useful if you need to run a script to provision TLS certificates or + # write out configuration files in a dynamic way. + extraInitContainers: null + # # This example installs a plugin pulled from github into the /usr/local/libexec/vault/oauthapp folder, + # # which is defined in the volumes value. + # - name: oauthapp + # image: "alpine" + # command: [sh, -c] + # args: + # - cd /tmp && + # wget https://github.com/puppetlabs/vault-plugin-secrets-oauthapp/releases/download/v1.2.0/vault-plugin-secrets-oauthapp-v1.2.0-linux-amd64.tar.xz -O oauthapp.xz && + # tar -xf oauthapp.xz && + # mv vault-plugin-secrets-oauthapp-v1.2.0-linux-amd64 /usr/local/libexec/vault/oauthapp && + # chmod +x /usr/local/libexec/vault/oauthapp + # volumeMounts: + # - name: plugins + # mountPath: /usr/local/libexec/vault + + # extraContainers is a list of sidecar containers. Specified as a YAML list. + extraContainers: null + # shareProcessNamespace enables process namespace sharing between Vault and the extraContainers + # This is useful if Vault must be signaled, e.g. to send a SIGHUP for a log rotation + shareProcessNamespace: false + # extraArgs is a string containing additional Vault server arguments. + extraArgs: "" + # extraPorts is a list of extra ports. Specified as a YAML list. + # This is useful if you need to add additional ports to the statefulset in dynamic way. + extraPorts: null + # - containerPort: 8300 + # name: http-monitoring + + # Used to define custom readinessProbe settings + readinessProbe: + enabled: true + # If you need to use a http path instead of the default exec + # path: /v1/sys/health?standbyok=true + + # Port number on which readinessProbe will be checked. + port: 8200 + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 2 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 5 + # How often (in seconds) to perform the probe + periodSeconds: 5 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 3 + # Used to enable a livenessProbe for the pods + livenessProbe: + enabled: false + path: "/v1/sys/health?standbyok=true" + # Port nuumber on which livenessProbe will be checked. + port: 8200 + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 2 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 60 + # How often (in seconds) to perform the probe + periodSeconds: 5 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 3 + # Optional duration in seconds the pod needs to terminate gracefully. + # See: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/ + terminationGracePeriodSeconds: 10 + # Used to set the sleep time during the preStop step + preStopSleepSeconds: 5 + # Used to define commands to run after the pod is ready. + # This can be used to automate processes such as initialization + # or boostrapping auth methods. + postStart: [] + # - /bin/sh + # - -c + # - /vault/userconfig/myscript/run.sh + + # extraEnvironmentVars is a list of extra environment variables to set with the stateful set. These could be + # used to include variables required for auto-unseal. + extraEnvironmentVars: {} + # GOOGLE_REGION: global + # GOOGLE_PROJECT: myproject + # GOOGLE_APPLICATION_CREDENTIALS: /vault/userconfig/myproject/myproject-creds.json + + # extraSecretEnvironmentVars is a list of extra environment variables to set with the stateful set. + # These variables take value from existing Secret objects. + extraSecretEnvironmentVars: [] + # - envName: AWS_SECRET_ACCESS_KEY + # secretName: vault + # secretKey: AWS_SECRET_ACCESS_KEY + + # Deprecated: please use 'volumes' instead. + # extraVolumes is a list of extra volumes to mount. These will be exposed + # to Vault in the path `/vault/userconfig//`. The value below is + # an array of objects, examples are shown below. + extraVolumes: [] + # - type: secret (or "configMap") + # name: my-secret + # path: null # default is `/vault/userconfig` + + # volumes is a list of volumes made available to all containers. These are rendered + # via toYaml rather than pre-processed like the extraVolumes value. + # The purpose is to make it easy to share volumes between containers. + volumes: null + # - name: plugins + # emptyDir: {} + + # volumeMounts is a list of volumeMounts for the main server container. These are rendered + # via toYaml rather than pre-processed like the extraVolumes value. + # The purpose is to make it easy to share volumes between containers. + volumeMounts: null + # - mountPath: /usr/local/libexec/vault + # name: plugins + # readOnly: true + + # Affinity Settings + # Commenting out or setting as empty the affinity variable, will allow + # deployment to single node services such as Minikube + # This should be either a multi-line string or YAML matching the PodSpec's affinity field. + affinity: | + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: "{{ .Release.Name }}" + component: server + topologyKey: kubernetes.io/hostname + # Topology settings for server pods + # ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ + # This should be either a multi-line string or YAML matching the topologySpreadConstraints array + # in a PodSpec. + topologySpreadConstraints: [] + # Toleration Settings for server pods + # This should be either a multi-line string or YAML matching the Toleration array + # in a PodSpec. + tolerations: [] + # nodeSelector labels for server pod assignment, formatted as a multi-line string or YAML map. + # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + # Example: + # nodeSelector: + # beta.kubernetes.io/arch: amd64 + nodeSelector: {} + # Enables network policy for server pods + networkPolicy: + enabled: false + egress: [] + # egress: + # - to: + # - ipBlock: + # cidr: 10.0.0.0/24 + # ports: + # - protocol: TCP + # port: 443 + # Priority class for server pods + priorityClassName: "" + # Extra labels to attach to the server pods + # This should be a YAML map of the labels to apply to the server pods + extraLabels: {} + # Extra annotations to attach to the server pods + # This can either be YAML or a YAML-formatted multi-line templated string map + # of the annotations to apply to the server pods + annotations: {} + # Enables a headless service to be used by the Vault Statefulset + service: + enabled: true + # Enable or disable the vault-active service, which selects Vault pods that + # have labelled themselves as the cluster leader with `vault-active: "true"` + active: + enabled: true + # Enable or disable the vault-standby service, which selects Vault pods that + # have labelled themselves as a cluster follower with `vault-active: "false"` + standby: + enabled: true + # If enabled, the service selectors will include `app.kubernetes.io/instance: {{ .Release.Name }}` + # When disabled, services may select Vault pods not deployed from the chart. + # Does not affect the headless vault-internal service with `ClusterIP: None` + instanceSelector: + enabled: true + # clusterIP controls whether a Cluster IP address is attached to the + # Vault service within Kubernetes. By default, the Vault service will + # be given a Cluster IP address, set to None to disable. When disabled + # Kubernetes will create a "headless" service. Headless services can be + # used to communicate with pods directly through DNS instead of a round-robin + # load balancer. + # clusterIP: None + + # Configures the service type for the main Vault service. Can be ClusterIP + # or NodePort. + #type: ClusterIP + + # Do not wait for pods to be ready + publishNotReadyAddresses: true + # The externalTrafficPolicy can be set to either Cluster or Local + # and is only valid for LoadBalancer and NodePort service types. + # The default value is Cluster. + # ref: https://kubernetes.io/docs/concepts/services-networking/service/#external-traffic-policy + externalTrafficPolicy: Cluster + # If type is set to "NodePort", a specific nodePort value can be configured, + # will be random if left blank. + #nodePort: 30000 + + # When HA mode is enabled + # If type is set to "NodePort", a specific nodePort value can be configured, + # will be random if left blank. + #activeNodePort: 30001 + + # When HA mode is enabled + # If type is set to "NodePort", a specific nodePort value can be configured, + # will be random if left blank. + #standbyNodePort: 30002 + + # Port on which Vault server is listening + port: 8200 + # Target port to which the service should be mapped to + targetPort: 8200 + # Extra annotations for the service definition. This can either be YAML or a + # YAML-formatted multi-line templated string map of the annotations to apply + # to the service. + annotations: {} + # This configures the Vault Statefulset to create a PVC for data + # storage when using the file or raft backend storage engines. + # See https://www.vaultproject.io/docs/configuration/storage/index.html to know more + dataStorage: + enabled: true + # Size of the PVC created + size: 10Gi + # Location where the PVC will be mounted. + mountPath: "/vault/data" + # Name of the storage class to use. If null it will use the + # configured default Storage Class. + storageClass: null + # Access Mode of the storage device being used for the PVC + accessMode: ReadWriteOnce + # Annotations to apply to the PVC + annotations: {} + # This configures the Vault Statefulset to create a PVC for audit + # logs. Once Vault is deployed, initialized, and unsealed, Vault must + # be configured to use this for audit logs. This will be mounted to + # /vault/audit + # See https://www.vaultproject.io/docs/audit/index.html to know more + auditStorage: + enabled: false + # Size of the PVC created + size: 10Gi + # Location where the PVC will be mounted. + mountPath: "/vault/audit" + # Name of the storage class to use. If null it will use the + # configured default Storage Class. + storageClass: null + # Access Mode of the storage device being used for the PVC + accessMode: ReadWriteOnce + # Annotations to apply to the PVC + annotations: {} + # Run Vault in "dev" mode. This requires no further setup, no state management, + # and no initialization. This is useful for experimenting with Vault without + # needing to unseal, store keys, et. al. All data is lost on restart - do not + # use dev mode for anything other than experimenting. + # See https://www.vaultproject.io/docs/concepts/dev-server.html to know more + dev: + enabled: false + # Set VAULT_DEV_ROOT_TOKEN_ID value + devRootToken: "root" + # Run Vault in "standalone" mode. This is the default mode that will deploy if + # no arguments are given to helm. This requires a PVC for data storage to use + # the "file" backend. This mode is not highly available and should not be scaled + # past a single replica. + standalone: + enabled: "-" + # config is a raw string of default configuration when using a Stateful + # deployment. Default is to use a PersistentVolumeClaim mounted at /vault/data + # and store data there. This is only used when using a Replica count of 1, and + # using a stateful set. This should be HCL. + + # Note: Configuration files are stored in ConfigMaps so sensitive data + # such as passwords should be either mounted through extraSecretEnvironmentVars + # or through a Kube secret. For more information see: + # https://www.vaultproject.io/docs/platform/k8s/helm/run#protecting-sensitive-vault-configurations + config: | + ui = true + + listener "tcp" { + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + # Enable unauthenticated metrics access (necessary for Prometheus Operator) + #telemetry { + # unauthenticated_metrics_access = "true" + #} + } + storage "file" { + path = "/vault/data" + } + + # Example configuration for using auto-unseal, using Google Cloud KMS. The + # GKMS keys must already exist, and the cluster must have a service account + # that is authorized to access GCP KMS. + #seal "gcpckms" { + # project = "vault-helm-dev" + # region = "global" + # key_ring = "vault-helm-unseal-kr" + # crypto_key = "vault-helm-unseal-key" + #} + + # Example configuration for enabling Prometheus metrics in your config. + #telemetry { + # prometheus_retention_time = "30s" + # disable_hostname = true + #} + # Run Vault in "HA" mode. There are no storage requirements unless the audit log + # persistence is required. In HA mode Vault will configure itself to use Consul + # for its storage backend. The default configuration provided will work the Consul + # Helm project by default. It is possible to manually configure Vault to use a + # different HA backend. + ha: + enabled: false + replicas: 3 + # Set the api_addr configuration for Vault HA + # See https://www.vaultproject.io/docs/configuration#api_addr + # If set to null, this will be set to the Pod IP Address + apiAddr: null + # Set the cluster_addr confuguration for Vault HA + # See https://www.vaultproject.io/docs/configuration#cluster_addr + # If set to null, this will be set to https://$(HOSTNAME).{{ template "vault.fullname" . }}-internal:8201 + clusterAddr: null + # Enables Vault's integrated Raft storage. Unlike the typical HA modes where + # Vault's persistence is external (such as Consul), enabling Raft mode will create + # persistent volumes for Vault to store data according to the configuration under server.dataStorage. + # The Vault cluster will coordinate leader elections and failovers internally. + raft: + # Enables Raft integrated storage + enabled: false + # Set the Node Raft ID to the name of the pod + setNodeId: false + # Note: Configuration files are stored in ConfigMaps so sensitive data + # such as passwords should be either mounted through extraSecretEnvironmentVars + # or through a Kube secret. For more information see: + # https://www.vaultproject.io/docs/platform/k8s/helm/run#protecting-sensitive-vault-configurations + config: | + ui = true + + listener "tcp" { + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + # Enable unauthenticated metrics access (necessary for Prometheus Operator) + #telemetry { + # unauthenticated_metrics_access = "true" + #} + } + + storage "raft" { + path = "/vault/data" + } + + service_registration "kubernetes" {} + # config is a raw string of default configuration when using a Stateful + # deployment. Default is to use a Consul for its HA storage backend. + # This should be HCL. + + # Note: Configuration files are stored in ConfigMaps so sensitive data + # such as passwords should be either mounted through extraSecretEnvironmentVars + # or through a Kube secret. For more information see: + # https://www.vaultproject.io/docs/platform/k8s/helm/run#protecting-sensitive-vault-configurations + config: | + ui = true + + listener "tcp" { + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + } + storage "consul" { + path = "vault" + address = "HOST_IP:8500" + } + + service_registration "kubernetes" {} + + # Example configuration for using auto-unseal, using Google Cloud KMS. The + # GKMS keys must already exist, and the cluster must have a service account + # that is authorized to access GCP KMS. + #seal "gcpckms" { + # project = "vault-helm-dev-246514" + # region = "global" + # key_ring = "vault-helm-unseal-kr" + # crypto_key = "vault-helm-unseal-key" + #} + + # Example configuration for enabling Prometheus metrics. + # If you are using Prometheus Operator you can enable a ServiceMonitor resource below. + # You may wish to enable unauthenticated metrics in the listener block above. + #telemetry { + # prometheus_retention_time = "30s" + # disable_hostname = true + #} + # A disruption budget limits the number of pods of a replicated application + # that are down simultaneously from voluntary disruptions + disruptionBudget: + enabled: true + # maxUnavailable will default to (n/2)-1 where n is the number of + # replicas. If you'd like a custom value, you can specify an override here. + maxUnavailable: null + # Definition of the serviceAccount used to run Vault. + # These options are also used when using an external Vault server to validate + # Kubernetes tokens. + serviceAccount: + # Specifies whether a service account should be created + create: true + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + # Extra annotations for the serviceAccount definition. This can either be + # YAML or a YAML-formatted multi-line templated string map of the + # annotations to apply to the serviceAccount. + annotations: {} + # Extra labels to attach to the serviceAccount + # This should be a YAML map of the labels to apply to the serviceAccount + extraLabels: {} + # Enable or disable a service account role binding with the permissions required for + # Vault's Kubernetes service_registration config option. + # See https://developer.hashicorp.com/vault/docs/configuration/service-registration/kubernetes + serviceDiscovery: + enabled: true + # Settings for the statefulSet used to run Vault. + statefulSet: + # Extra annotations for the statefulSet. This can either be YAML or a + # YAML-formatted multi-line templated string map of the annotations to apply + # to the statefulSet. + annotations: {} + # Set the pod and container security contexts. + # If not set, these will default to, and for *not* OpenShift: + # pod: + # runAsNonRoot: true + # runAsGroup: {{ .Values.server.gid | default 1000 }} + # runAsUser: {{ .Values.server.uid | default 100 }} + # fsGroup: {{ .Values.server.gid | default 1000 }} + # container: + # allowPrivilegeEscalation: false + # + # If not set, these will default to, and for OpenShift: + # pod: {} + # container: {} + securityContext: + pod: {} + container: {} + # Should the server pods run on the host network + hostNetwork: false +# Vault UI +ui: + # True if you want to create a Service entry for the Vault UI. + # + # serviceType can be used to control the type of service created. For + # example, setting this to "LoadBalancer" will create an external load + # balancer (for supported K8S installations) to access the UI. + enabled: false + publishNotReadyAddresses: true + # The service should only contain selectors for active Vault pod + activeVaultPodOnly: false + serviceType: "ClusterIP" + serviceNodePort: null + externalPort: 8200 + targetPort: 8200 + # The externalTrafficPolicy can be set to either Cluster or Local + # and is only valid for LoadBalancer and NodePort service types. + # The default value is Cluster. + # ref: https://kubernetes.io/docs/concepts/services-networking/service/#external-traffic-policy + externalTrafficPolicy: Cluster + #loadBalancerSourceRanges: + # - 10.0.0.0/16 + # - 1.78.23.3/32 + + # loadBalancerIP: + + # Extra annotations to attach to the ui service + # This can either be YAML or a YAML-formatted multi-line templated string map + # of the annotations to apply to the ui service + annotations: {} +# secrets-store-csi-driver-provider-vault +csi: + # True if you want to install a secrets-store-csi-driver-provider-vault daemonset. + # + # Requires installing the secrets-store-csi-driver separately, see: + # https://github.com/kubernetes-sigs/secrets-store-csi-driver#install-the-secrets-store-csi-driver + # + # With the driver and provider installed, you can mount Vault secrets into volumes + # similar to the Vault Agent injector, and you can also sync those secrets into + # Kubernetes secrets. + enabled: false + image: + repository: "hashicorp/vault-csi-provider" + tag: "1.3.0" + pullPolicy: IfNotPresent + # volumes is a list of volumes made available to all containers. These are rendered + # via toYaml rather than pre-processed like the extraVolumes value. + # The purpose is to make it easy to share volumes between containers. + volumes: null + # - name: tls + # secret: + # secretName: vault-tls + + # volumeMounts is a list of volumeMounts for the main server container. These are rendered + # via toYaml rather than pre-processed like the extraVolumes value. + # The purpose is to make it easy to share volumes between containers. + volumeMounts: null + # - name: tls + # mountPath: "/vault/tls" + # readOnly: true + + resources: {} + # resources: + # requests: + # cpu: 50m + # memory: 128Mi + # limits: + # cpu: 50m + # memory: 128Mi + + # Override the default secret name for the CSI Provider's HMAC key used for + # generating secret versions. + hmacSecretName: "" + # Settings for the daemonSet used to run the provider. + daemonSet: + updateStrategy: + type: RollingUpdate + maxUnavailable: "" + # Extra annotations for the daemonSet. This can either be YAML or a + # YAML-formatted multi-line templated string map of the annotations to apply + # to the daemonSet. + annotations: {} + # Provider host path (must match the CSI provider's path) + providersDir: "/etc/kubernetes/secrets-store-csi-providers" + # Kubelet host path + kubeletRootDir: "/var/lib/kubelet" + # Extra labels to attach to the vault-csi-provider daemonSet + # This should be a YAML map of the labels to apply to the csi provider daemonSet + extraLabels: {} + # security context for the pod template and container in the csi provider daemonSet + securityContext: + pod: {} + container: {} + pod: + # Extra annotations for the provider pods. This can either be YAML or a + # YAML-formatted multi-line templated string map of the annotations to apply + # to the pod. + annotations: {} + # Toleration Settings for provider pods + # This should be either a multi-line string or YAML matching the Toleration array + # in a PodSpec. + tolerations: [] + # Extra labels to attach to the vault-csi-provider pod + # This should be a YAML map of the labels to apply to the csi provider pod + extraLabels: {} + agent: + enabled: true + extraArgs: [] + image: + repository: "hashicorp/vault" + tag: "1.13.1" + pullPolicy: IfNotPresent + logFormat: standard + logLevel: info + resources: {} + # resources: + # requests: + # memory: 256Mi + # cpu: 250m + # limits: + # memory: 256Mi + # cpu: 250m + # Priority class for csi pods + priorityClassName: "" + serviceAccount: + # Extra annotations for the serviceAccount definition. This can either be + # YAML or a YAML-formatted multi-line templated string map of the + # annotations to apply to the serviceAccount. + annotations: {} + # Extra labels to attach to the vault-csi-provider serviceAccount + # This should be a YAML map of the labels to apply to the csi provider serviceAccount + extraLabels: {} + # Used to configure readinessProbe for the pods. + readinessProbe: + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 2 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 5 + # How often (in seconds) to perform the probe + periodSeconds: 5 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 3 + # Used to configure livenessProbe for the pods. + livenessProbe: + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 2 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 5 + # How often (in seconds) to perform the probe + periodSeconds: 5 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 3 + # Enables debug logging. + debug: false + # Pass arbitrary additional arguments to vault-csi-provider. + # See https://www.vaultproject.io/docs/platform/k8s/csi/configurations#command-line-arguments + # for the available command line flags. + extraArgs: [] +# Vault is able to collect and publish various runtime metrics. +# Enabling this feature requires setting adding `telemetry{}` stanza to +# the Vault configuration. There are a few examples included in the `config` sections above. +# +# For more information see: +# https://www.vaultproject.io/docs/configuration/telemetry +# https://www.vaultproject.io/docs/internals/telemetry +serverTelemetry: + # Enable support for the Prometheus Operator. Currently, this chart does not support + # authenticating to Vault's metrics endpoint, so the following `telemetry{}` must be included + # in the `listener "tcp"{}` stanza + # telemetry { + # unauthenticated_metrics_access = "true" + # } + # + # See the `standalone.config` for a more complete example of this. + # + # In addition, a top level `telemetry{}` stanza must also be included in the Vault configuration: + # + # example: + # telemetry { + # prometheus_retention_time = "30s" + # disable_hostname = true + # } + # + # Configuration for monitoring the Vault server. + serviceMonitor: + # The Prometheus operator *must* be installed before enabling this feature, + # if not the chart will fail to install due to missing CustomResourceDefinitions + # provided by the operator. + # + # Instructions on how to install the Helm chart can be found here: + # https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack + # More information can be found here: + # https://github.com/prometheus-operator/prometheus-operator + # https://github.com/prometheus-operator/kube-prometheus + + # Enable deployment of the Vault Server ServiceMonitor CustomResource. + enabled: false + # Selector labels to add to the ServiceMonitor. + # When empty, defaults to: + # release: prometheus + selectors: {} + # Interval at which Prometheus scrapes metrics + interval: 30s + # Timeout for Prometheus scrapes + scrapeTimeout: 10s + prometheusRules: + # The Prometheus operator *must* be installed before enabling this feature, + # if not the chart will fail to install due to missing CustomResourceDefinitions + # provided by the operator. + + # Deploy the PrometheusRule custom resource for AlertManager based alerts. + # Requires that AlertManager is properly deployed. + enabled: false + # Selector labels to add to the PrometheusRules. + # When empty, defaults to: + # release: prometheus + selectors: {} + # Some example rules. + rules: {} + # - alert: vault-HighResponseTime + # annotations: + # message: The response time of Vault is over 500ms on average over the last 5 minutes. + # expr: vault_core_handle_request{quantile="0.5", namespace="mynamespace"} > 500 + # for: 5m + # labels: + # severity: warning + # - alert: vault-HighResponseTime + # annotations: + # message: The response time of Vault is over 1s on average over the last 5 minutes. + # expr: vault_core_handle_request{quantile="0.5", namespace="mynamespace"} > 1000 + # for: 5m + # labels: + # severity: critical diff --git a/charts/partners/hashicorp/vault/0.25.0/src/.helmignore b/charts/partners/hashicorp/vault/0.25.0/src/.helmignore new file mode 100644 index 00000000..4007e243 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/.helmignore @@ -0,0 +1,28 @@ +# 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 +.terraform/ +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj + +# CI and test +.circleci/ +.github/ +.gitlab-ci.yml +test/ diff --git a/charts/partners/hashicorp/vault/0.25.0/src/CHANGELOG.md b/charts/partners/hashicorp/vault/0.25.0/src/CHANGELOG.md new file mode 100644 index 00000000..f3c466f2 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/CHANGELOG.md @@ -0,0 +1,484 @@ +## Unreleased + +## 0.25.0 (June 26, 2023) + +Changes: +* Latest Kubernetes version tested is now 1.27 +* server: Headless service ignores `server.service.publishNotReadyAddresses` setting and always sets it as `true` [GH-902](https://github.com/hashicorp/vault-helm/pull/902) +* `vault` updated to 1.14.0 [GH-916](https://github.com/hashicorp/vault-helm/pull/916) +* `vault-csi-provider` updated to 1.4.0 [GH-916](https://github.com/hashicorp/vault-helm/pull/916) + +Improvements: +* CSI: Make `nodeSelector` and `affinity` configurable for CSI daemonset's pods [GH-862](https://github.com/hashicorp/vault-helm/pull/862) +* injector: Add `ephemeralLimit` and `ephemeralRequest` as options for configuring Agent's ephemeral storage resources [GH-798](https://github.com/hashicorp/vault-helm/pull/798) +* Minimum kubernetes version for chart reverted to 1.20.0 to allow installation on clusters older than the oldest tested version [GH-916](https://github.com/hashicorp/vault-helm/pull/916) + +Bugs: +* server: Set the default for `prometheusRules.rules` to an empty list [GH-886](https://github.com/hashicorp/vault-helm/pull/886) + +## 0.24.1 (April 17, 2023) + +Bugs: +* csi: Add RBAC required by v1.3.0 to create secret for HMAC key used to generate secret versions [GH-872](https://github.com/hashicorp/vault-helm/pull/872) + +## 0.24.0 (April 6, 2023) + +Changes: +* Earliest Kubernetes version tested is now 1.22 +* `vault` updated to 1.13.1 [GH-863](https://github.com/hashicorp/vault-helm/pull/863) +* `vault-k8s` updated to 1.2.1 [GH-868](https://github.com/hashicorp/vault-helm/pull/868) +* `vault-csi-provider` updated to 1.3.0 [GH-749](https://github.com/hashicorp/vault-helm/pull/749) + +Features: +* server: New `extraPorts` option for adding ports to the Vault server statefulset [GH-841](https://github.com/hashicorp/vault-helm/pull/841) +* server: Add configurable Port Number in readinessProbe and livenessProbe for the server-statefulset [GH-831](https://github.com/hashicorp/vault-helm/pull/831) +* injector: Make livenessProbe and readinessProbe configurable and add configurable startupProbe [GH-852](https://github.com/hashicorp/vault-helm/pull/852) +* csi: Add an Agent sidecar to Vault CSI Provider pods to provide lease caching and renewals [GH-749](https://github.com/hashicorp/vault-helm/pull/749) + +## 0.23.0 (November 28th, 2022) + +Changes: +* `vault` updated to 1.12.1 [GH-814](https://github.com/hashicorp/vault-helm/pull/814) +* `vault-k8s` updated to 1.1.0 [GH-814](https://github.com/hashicorp/vault-helm/pull/814) +* `vault-csi-provider` updated to 1.2.1 [GH-814](https://github.com/hashicorp/vault-helm/pull/814) + +Features: +* server: Add `extraLabels` for Vault server serviceAccount [GH-806](https://github.com/hashicorp/vault-helm/pull/806) +* server: Add `server.service.active.enabled` and `server.service.standby.enabled` options to selectively disable additional services [GH-811](https://github.com/hashicorp/vault-helm/pull/811) +* server: Add `server.serviceAccount.serviceDiscovery.enabled` option to selectively disable a Vault service discovery role and role binding [GH-811](https://github.com/hashicorp/vault-helm/pull/811) +* server: Add `server.service.instanceSelector.enabled` option to allow selecting pods outside the helm chart deployment [GH-813](https://github.com/hashicorp/vault-helm/pull/813) + +Bugs: +* server: Quote `.server.ha.clusterAddr` value [GH-810](https://github.com/hashicorp/vault-helm/pull/810) + +## 0.22.1 (October 26th, 2022) + +Changes: +* `vault` updated to 1.12.0 [GH-803](https://github.com/hashicorp/vault-helm/pull/803) +* `vault-k8s` updated to 1.0.1 [GH-803](https://github.com/hashicorp/vault-helm/pull/803) + +## 0.22.0 (September 8th, 2022) + +Features: +* Add PrometheusOperator support for collecting Vault server metrics. [GH-772](https://github.com/hashicorp/vault-helm/pull/772) + +Changes: +* `vault-k8s` to 1.0.0 [GH-784](https://github.com/hashicorp/vault-helm/pull/784) +* Test against Kubernetes 1.25 [GH-784](https://github.com/hashicorp/vault-helm/pull/784) +* `vault` updated to 1.11.3 [GH-785](https://github.com/hashicorp/vault-helm/pull/785) + +## 0.21.0 (August 10th, 2022) + +CHANGES: +* `vault-k8s` updated to 0.17.0. [GH-771](https://github.com/hashicorp/vault-helm/pull/771) +* `vault-csi-provider` updated to 1.2.0 [GH-771](https://github.com/hashicorp/vault-helm/pull/771) +* `vault` updated to 1.11.2 [GH-771](https://github.com/hashicorp/vault-helm/pull/771) +* Start testing against Kubernetes 1.24. [GH-744](https://github.com/hashicorp/vault-helm/pull/744) +* Deprecated `injector.externalVaultAddr`. Added `global.externalVaultAddr`, which applies to both the Injector and the CSI Provider. [GH-745](https://github.com/hashicorp/vault-helm/pull/745) +* CSI Provider pods now set the `VAULT_ADDR` environment variable to either the internal Vault service or the configured external address. [GH-745](https://github.com/hashicorp/vault-helm/pull/745) + +Features: +* server: Add `server.statefulSet.securityContext` to override pod and container `securityContext`. [GH-767](https://github.com/hashicorp/vault-helm/pull/767) +* csi: Add `csi.daemonSet.securityContext` to override pod and container `securityContext`. [GH-767](https://github.com/hashicorp/vault-helm/pull/767) +* injector: Add `injector.securityContext` to override pod and container `securityContext`. [GH-750](https://github.com/hashicorp/vault-helm/pull/750) and [GH-767](https://github.com/hashicorp/vault-helm/pull/767) +* Add `server.service.activeNodePort` and `server.service.standbyNodePort` to specify the `nodePort` for active and standby services. [GH-610](https://github.com/hashicorp/vault-helm/pull/610) +* Support for setting annotations on the injector's serviceAccount [GH-753](https://github.com/hashicorp/vault-helm/pull/753) + +## 0.20.1 (May 25th, 2022) +CHANGES: +* `vault-k8s` updated to 0.16.1 [GH-739](https://github.com/hashicorp/vault-helm/pull/739) + +Improvements: +* Mutating webhook will no longer target the agent injector pod [GH-736](https://github.com/hashicorp/vault-helm/pull/736) + +Bugs: +* `vault` service account is now created even if the server is set to disabled, as per before 0.20.0 [GH-737](https://github.com/hashicorp/vault-helm/pull/737) + +## 0.20.0 (May 16th, 2022) + +CHANGES: +* `global.enabled` now works as documented, that is, setting `global.enabled` to false will disable everything, with individual components able to be turned on individually [GH-703](https://github.com/hashicorp/vault-helm/pull/703) +* Default value of `-` used for injector and server to indicate that they follow `global.enabled`. [GH-703](https://github.com/hashicorp/vault-helm/pull/703) +* Vault default image to 1.10.3 +* CSI provider default image to 1.1.0 +* Vault K8s default image to 0.16.0 +* Earliest Kubernetes version tested is now 1.16 +* Helm 3.6+ now required + +Features: +* Support topologySpreadConstraints in server and injector. [GH-652](https://github.com/hashicorp/vault-helm/pull/652) + +Improvements: +* CSI: Set `extraLabels` for daemonset, pods, and service account [GH-690](https://github.com/hashicorp/vault-helm/pull/690) +* Add namespace to injector-leader-elector role, rolebinding and secret [GH-683](https://github.com/hashicorp/vault-helm/pull/683) +* Support policy/v1 PodDisruptionBudget in Kubernetes 1.21+ for server and injector [GH-710](https://github.com/hashicorp/vault-helm/pull/710) +* Make the Cluster Address (CLUSTER_ADDR) configurable [GH-629](https://github.com/hashicorp/vault-helm/pull/709) +* server: Make `publishNotReadyAddresses` configurable for services [GH-694](https://github.com/hashicorp/vault-helm/pull/694) +* server: Allow config to be defined as a YAML object in the values file [GH-684](https://github.com/hashicorp/vault-helm/pull/684) +* Maintain default MutatingWebhookConfiguration values from `v1beta1` [GH-692](https://github.com/hashicorp/vault-helm/pull/692) + +## 0.19.0 (January 20th, 2022) + +CHANGES: +* Vault image default 1.9.2 +* Vault K8s image default 0.14.2 + +Features: +* Added configurable podDisruptionBudget for injector [GH-653](https://github.com/hashicorp/vault-helm/pull/653) +* Make terminationGracePeriodSeconds configurable for server [GH-659](https://github.com/hashicorp/vault-helm/pull/659) +* Added configurable update strategy for injector [GH-661](https://github.com/hashicorp/vault-helm/pull/661) +* csi: ability to set priorityClassName for CSI daemonset pods [GH-670](https://github.com/hashicorp/vault-helm/pull/670) + +Improvements: +* Set the namespace on the OpenShift Route [GH-679](https://github.com/hashicorp/vault-helm/pull/679) +* Add volumes and env vars to helm hook test pod [GH-673](https://github.com/hashicorp/vault-helm/pull/673) +* Make TLS configurable for OpenShift routes [GH-686](https://github.com/hashicorp/vault-helm/pull/686) + +## 0.18.0 (November 17th, 2021) + +CHANGES: +* Removed support for deploying a leader-elector container with the [vault-k8s injector](https://github.com/hashicorp/vault-k8s) injector since vault-k8s now uses an internal mechanism to determine leadership [GH-649](https://github.com/hashicorp/vault-helm/pull/649) +* Vault image default 1.9.0 +* Vault K8s image default 0.14.1 + +Improvements: +* Added templateConfig.staticSecretRenderInterval chart option for the injector [GH-621](https://github.com/hashicorp/vault-helm/pull/621) + +## 0.17.1 (October 25th, 2021) + +Improvements: + * Add option for Ingress PathType [GH-634](https://github.com/hashicorp/vault-helm/pull/634) + +## 0.17.0 (October 21st, 2021) + +KNOWN ISSUES: +* The chart will fail to deploy on Kubernetes 1.19+ with `server.ingress.enabled=true` because no `pathType` is set + +CHANGES: +* Vault image default 1.8.4 +* Vault K8s image default 0.14.0 + +Improvements: +* Support Ingress stable networking API [GH-590](https://github.com/hashicorp/vault-helm/pull/590) +* Support setting the `externalTrafficPolicy` for `LoadBalancer` and `NodePort` service types [GH-626](https://github.com/hashicorp/vault-helm/pull/626) +* Support setting ingressClassName on server Ingress [GH-630](https://github.com/hashicorp/vault-helm/pull/630) + +Bugs: +* Ensure `kubeletRootDir` volume path and mounts are the same when `csi.daemonSet.kubeletRootDir` is overridden [GH-628](https://github.com/hashicorp/vault-helm/pull/628) + +## 0.16.1 (September 29th, 2021) + +CHANGES: +* Vault image default 1.8.3 +* Vault K8s image default 0.13.1 + +## 0.16.0 (September 16th, 2021) + +CHANGES: +* Support for deploying a leader-elector container with the [vault-k8s injector](https://github.com/hashicorp/vault-k8s) injector will be removed in version 0.18.0 of this chart since vault-k8s now uses an internal mechanism to determine leadership. To enable the deployment of the leader-elector container for use with vault-k8s 0.12.0 and earlier, set `useContainer=true`. + +Improvements: + * Make CSI provider `hostPaths` configurable via `csi.daemonSet.providersDir` and `csi.daemonSet.kubeletRootDir` [GH-603](https://github.com/hashicorp/vault-helm/pull/603) + * Support vault-k8s internal leader election [GH-568](https://github.com/hashicorp/vault-helm/pull/568) [GH-607](https://github.com/hashicorp/vault-helm/pull/607) + +## 0.15.0 (August 23rd, 2021) + +Improvements: +* Add imagePullSecrets on server test [GH-572](https://github.com/hashicorp/vault-helm/pull/572) +* Add injector.webhookAnnotations chart option [GH-584](https://github.com/hashicorp/vault-helm/pull/584) + +## 0.14.0 (July 28th, 2021) + +Features: +* Added templateConfig.exitOnRetryFailure chart option for the injector [GH-560](https://github.com/hashicorp/vault-helm/pull/560) + +Improvements: +* Support configuring pod tolerations, pod affinity, and node selectors as YAML [GH-565](https://github.com/hashicorp/vault-helm/pull/565) +* Set the default vault image to come from the hashicorp organization [GH-567](https://github.com/hashicorp/vault-helm/pull/567) +* Add support for running the acceptance tests against a local `kind` cluster [GH-567](https://github.com/hashicorp/vault-helm/pull/567) +* Add `server.ingress.activeService` to configure if the ingress should use the active service [GH-570](https://github.com/hashicorp/vault-helm/pull/570) +* Add `server.route.activeService` to configure if the route should use the active service [GH-570](https://github.com/hashicorp/vault-helm/pull/570) +* Support configuring `global.imagePullSecrets` from a string array [GH-576](https://github.com/hashicorp/vault-helm/pull/576) + + +## 0.13.0 (June 17th, 2021) + +Improvements: +* Added a helm test for vault server [GH-531](https://github.com/hashicorp/vault-helm/pull/531) +* Added server.enterpriseLicense option [GH-547](https://github.com/hashicorp/vault-helm/pull/547) +* Added OpenShift overrides [GH-549](https://github.com/hashicorp/vault-helm/pull/549) + +Bugs: +* Fix ui.serviceNodePort schema [GH-537](https://github.com/hashicorp/vault-helm/pull/537) +* Fix server.ha.disruptionBudget.maxUnavailable schema [GH-535](https://github.com/hashicorp/vault-helm/pull/535) +* Added webhook-certs volume mount to sidecar injector [GH-545](https://github.com/hashicorp/vault-helm/pull/545) + +## 0.12.0 (May 25th, 2021) + +Features: +* Pass additional arguments to `vault-csi-provider` using `csi.extraArgs` [GH-526](https://github.com/hashicorp/vault-helm/pull/526) + +Improvements: +* Set chart kubeVersion and added chart-verifier tests [GH-510](https://github.com/hashicorp/vault-helm/pull/510) +* Added values json schema [GH-513](https://github.com/hashicorp/vault-helm/pull/513) +* Ability to set tolerations for CSI daemonset pods [GH-521](https://github.com/hashicorp/vault-helm/pull/521) +* UI target port is now configurable [GH-437](https://github.com/hashicorp/vault-helm/pull/437) + +Bugs: +* CSI: `global.imagePullSecrets` are now also used for CSI daemonset [GH-519](https://github.com/hashicorp/vault-helm/pull/519) + +## 0.11.0 (April 14th, 2021) + +Features: +* Added `server.enabled` to explicitly skip installing a Vault server [GH-486](https://github.com/hashicorp/vault-helm/pull/486) +* Injector now supports enabling host network [GH-471](https://github.com/hashicorp/vault-helm/pull/471) +* Injector port is now configurable [GH-489](https://github.com/hashicorp/vault-helm/pull/489) +* Injector Vault Agent resource defaults are now configurable [GH-493](https://github.com/hashicorp/vault-helm/pull/493) +* Extra paths can now be added to the Vault ingress service [GH-460](https://github.com/hashicorp/vault-helm/pull/460) +* Log level and format can now be set directly using `server.logFormat` and `server.logLevel` [GH-488](https://github.com/hashicorp/vault-helm/pull/488) + +Improvements: +* Added `https` name to injector service port [GH-495](https://github.com/hashicorp/vault-helm/pull/495) + +Bugs: +* CSI: Fix ClusterRole name and DaemonSet's service account to properly match deployment name [GH-486](https://github.com/hashicorp/vault-helm/pull/486) + +## 0.10.0 (March 25th, 2021) + +Features: +* Add support for [Vault CSI provider](https://github.com/hashicorp/vault-csi-provider) [GH-461](https://github.com/hashicorp/vault-helm/pull/461) + +Improvements: +* `objectSelector` can now be set on the mutating admission webhook [GH-456](https://github.com/hashicorp/vault-helm/pull/456) + +## 0.9.1 (February 2nd, 2021) + +Bugs: +* Injector: fix labels for default anti-affinity rule [GH-441](https://github.com/hashicorp/vault-helm/pull/441), [GH-442](https://github.com/hashicorp/vault-helm/pull/442) +* Set VAULT_DEV_LISTEN_ADDRESS in dev mode [GH-446](https://github.com/hashicorp/vault-helm/pull/446) + +## 0.9.0 (January 5th, 2021) + +Features: +* Injector now supports configurable number of replicas [GH-436](https://github.com/hashicorp/vault-helm/pull/436) +* Injector now supports auto TLS for multiple replicas using leader elections [GH-436](https://github.com/hashicorp/vault-helm/pull/436) + +Improvements: +* Dev mode now supports `server.extraArgs` [GH-421](https://github.com/hashicorp/vault-helm/pull/421) +* Dev mode root token is now configurable with `server.dev.devRootToken` [GH-415](https://github.com/hashicorp/vault-helm/pull/415) +* ClusterRoleBinding updated to `v1` [GH-395](https://github.com/hashicorp/vault-helm/pull/395) +* MutatingWebhook updated to `v1` [GH-408](https://github.com/hashicorp/vault-helm/pull/408) +* Injector service now supports `injector.service.annotations` [425](https://github.com/hashicorp/vault-helm/pull/425) +* Injector now supports `injector.extraLabels` [428](https://github.com/hashicorp/vault-helm/pull/428) +* Added `allowPrivilegeEscalation: false` to Vault and Injector containers [429](https://github.com/hashicorp/vault-helm/pull/429) +* Network Policy now supports `server.networkPolicy.egress` [389](https://github.com/hashicorp/vault-helm/pull/389) + +## 0.8.0 (October 20th, 2020) + +Improvements: +* Make server NetworkPolicy independent of OpenShift [GH-381](https://github.com/hashicorp/vault-helm/pull/381) +* Added configurables for all probe values [GH-387](https://github.com/hashicorp/vault-helm/pull/387) +* MountPath for audit and data storage is now configurable [GH-393](https://github.com/hashicorp/vault-helm/pull/393) +* Annotations can now be added to the Injector pods [GH-394](https://github.com/hashicorp/vault-helm/pull/394) +* The injector can now be configured with a failurePolicy [GH-400](https://github.com/hashicorp/vault-helm/pull/400) +* Added additional environment variables for rendering within Vault config [GH-398](https://github.com/hashicorp/vault-helm/pull/398) +* Service account for Vault K8s auth is automatically created when `injector.externalVaultAddr` is set [GH-392](https://github.com/hashicorp/vault-helm/pull/392) + +Bugs: +* Fixed install output using Helm V2 command [GH-378](https://github.com/hashicorp/vault-helm/pull/378) + +## 0.7.0 (August 24th, 2020) + +Features: +* Added `volumes` and `volumeMounts` for mounting _any_ type of volume [GH-314](https://github.com/hashicorp/vault-helm/pull/314). +* Added configurable to enable prometheus telemetery exporter for Vault Agent Injector [GH-372](https://github.com/hashicorp/vault-helm/pull/372) + +Improvements: +* Added `defaultMode` configurable to `extraVolumes`[GH-321](https://github.com/hashicorp/vault-helm/pull/321) +* Option to install and use PodSecurityPolicy's for vault server and injector [GH-177](https://github.com/hashicorp/vault-helm/pull/177) +* `VAULT_API_ADDR` is now configurable [GH-290](https://github.com/hashicorp/vault-helm/pull/290) +* Removed deprecated tolerate unready endpoint annotations [GH-363](https://github.com/hashicorp/vault-helm/pull/363) +* Add an option to set annotations on the StatefulSet [GH-199](https://github.com/hashicorp/vault-helm/pull/199) +* Make the vault server serviceAccount name a configuration option [GH-367](https://github.com/hashicorp/vault-helm/pull/367) +* Removed annotation striction from `dev` mode [GH-371](https://github.com/hashicorp/vault-helm/pull/371) +* Add an option to set annotations on PVCs [GH-364](https://github.com/hashicorp/vault-helm/pull/364) +* Added service configurables for UI [GH-285](https://github.com/hashicorp/vault-helm/pull/285) + +Bugs: +* Fix python dependency in test image [GH-337](https://github.com/hashicorp/vault-helm/pull/337) +* Fix caBundle not being quoted causing validation issues with Helm 3 [GH-352](https://github.com/hashicorp/vault-helm/pull/352) +* Fix injector network policy being rendered when injector is not enabled [GH-358](https://github.com/hashicorp/vault-helm/pull/358) + +## 0.6.0 (June 3rd, 2020) + +Features: +* Added `extraInitContainers` to define init containers for the Vault cluster [GH-258](https://github.com/hashicorp/vault-helm/pull/258) +* Added `postStart` lifecycle hook allowing users to configure commands to run on the Vault pods after they're ready [GH-315](https://github.com/hashicorp/vault-helm/pull/315) +* Beta: Added OpenShift support [GH-319](https://github.com/hashicorp/vault-helm/pull/319) + +Improvements: +* Server configs can now be defined in YAML. Multi-line string configs are still compatible [GH-213](https://github.com/hashicorp/vault-helm/pull/213) +* Removed IPC_LOCK privileges since swap is disabled on containers [[GH-198](https://github.com/hashicorp/vault-helm/pull/198)] +* Use port names that map to vault.scheme [[GH-223](https://github.com/hashicorp/vault-helm/pull/223)] +* Allow both yaml and multi-line string annotations [[GH-272](https://github.com/hashicorp/vault-helm/pull/272)] +* Added configurable to set the Raft node name to hostname [[GH-269](https://github.com/hashicorp/vault-helm/pull/269)] +* Support setting priorityClassName on pods [[GH-282](https://github.com/hashicorp/vault-helm/pull/282)] +* Added support for ingress apiVersion `networking.k8s.io/v1beta1` [[GH-310](https://github.com/hashicorp/vault-helm/pull/310)] +* Added configurable to change service type for the HA active service [GH-317](https://github.com/hashicorp/vault-helm/pull/317) + +Bugs: +* Fixed default ingress path [[GH-224](https://github.com/hashicorp/vault-helm/pull/224)] +* Fixed annotations for HA standby/active services [[GH-268](https://github.com/hashicorp/vault-helm/pull/268)] +* Updated some value defaults to match their use in templates [[GH-309](https://github.com/hashicorp/vault-helm/pull/309)] +* Use active service on ingress when ha [[GH-270](https://github.com/hashicorp/vault-helm/pull/270)] +* Fixed bug where pull secrets weren't being used for injector image [GH-298](https://github.com/hashicorp/vault-helm/pull/298) + +## 0.5.0 (April 9th, 2020) + +Features: + +* Added Raft support for HA mode [[GH-228](https://github.com/hashicorp/vault-helm/pull/229)] +* Now supports Vault Enterprise [[GH-250](https://github.com/hashicorp/vault-helm/pull/250)] +* Added K8s Service Registration for HA modes [[GH-250](https://github.com/hashicorp/vault-helm/pull/250)] + +* Option to set `AGENT_INJECT_VAULT_AUTH_PATH` for the injector [[GH-185](https://github.com/hashicorp/vault-helm/pull/185)] +* Added environment variables for logging and revocation on Vault Agent Injector [[GH-219](https://github.com/hashicorp/vault-helm/pull/219)] +* Option to set environment variables for the injector deployment [[GH-232](https://github.com/hashicorp/vault-helm/pull/232)] +* Added affinity, tolerations, and nodeSelector options for the injector deployment [[GH-234](https://github.com/hashicorp/vault-helm/pull/234)] +* Made all annotations multi-line strings [[GH-227](https://github.com/hashicorp/vault-helm/pull/227)] + +## 0.4.0 (February 21st, 2020) + +Improvements: + +* Allow process namespace sharing between Vault and sidecar containers [[GH-174](https://github.com/hashicorp/vault-helm/pull/174)] +* Added configurable to change updateStrategy [[GH-172](https://github.com/hashicorp/vault-helm/pull/172)] +* Added sleep in the preStop lifecycle step [[GH-188](https://github.com/hashicorp/vault-helm/pull/188)] +* Updated chart and tests to Helm 3 [[GH-195](https://github.com/hashicorp/vault-helm/pull/195)] +* Adds Values.injector.externalVaultAddr to use the injector with an external vault [[GH-207](https://github.com/hashicorp/vault-helm/pull/207)] + +Bugs: + +* Fix bug where Vault lifecycle was appended after extra containers. [[GH-179](https://github.com/hashicorp/vault-helm/pull/179)] + +## 0.3.3 (January 14th, 2020) + +Security: + +* Added `server.extraArgs` to allow loading of additional Vault configurations containing sensitive settings [GH-175](https://github.com/hashicorp/vault-helm/issues/175) + +Bugs: + +* Fixed injection bug where wrong environment variables were being used for manually mounted TLS files + +## 0.3.2 (January 8th, 2020) + +Bugs: + +* Fixed injection bug where TLS Skip Verify was true by default [VK8S-35] + +## 0.3.1 (January 2nd, 2020) + +Bugs: + +* Fixed injection bug causing kube-system pods to be rejected [VK8S-14] + +## 0.3.0 (December 19th, 2019) + +Features: + +* Extra containers can now be added to the Vault pods +* Added configurability of pod probes +* Added Vault Agent Injector + +Improvements: + +* Moved `global.image` to `server.image` +* Changed UI service template to route pods that aren't ready via `publishNotReadyAddresses: true` +* Added better HTTP/HTTPS scheme support to http probes +* Added configurable node port for Vault service +* `server.authDelegator` is now enabled by default + +Bugs: + +* Fixed upgrade bug by removing chart label which contained the version +* Fixed typo on `serviceAccount` (was `serviceaccount`) +* Fixed readiness/liveliness HTTP probe default to accept standbys + +## 0.2.1 (November 12th, 2019) + +Bugs: + +* Removed `readOnlyRootFilesystem` causing issues when validating deployments + +## 0.2.0 (October 29th, 2019) + +Features: + +* Added load balancer support +* Added ingress support +* Added configurable for service types (ClusterIP, NodePort, LoadBalancer, etc) +* Removed root requirements, now runs as Vault user + +Improvements: + +* Added namespace value to all rendered objects +* Made ports configurable in services +* Added the ability to add custom annotations to services +* Added docker image for running bats test in CircleCI +* Removed restrictions around `dev` mode such as annotations +* `readOnlyRootFilesystem` is now configurable +* Image Pull Policy is now configurable + +Bugs: + +* Fixed selector bugs related to Helm label updates (services, affinities, and pod disruption) +* Fixed bug where audit storage was not being mounted in HA mode +* Fixed bug where Vault pod wasn't receiving SIGTERM signals + + +## 0.1.2 (August 22nd, 2019) + +Features: + +* Added `extraSecretEnvironmentVars` to allow users to mount secrets as + environment variables +* Added `tlsDisable` configurable to change HTTP protocols from HTTP/HTTPS + depending on the value +* Added `serviceNodePort` to configure a NodePort value when setting `serviceType` + to "NodePort" + +Improvements: + +* Changed UI port to 8200 for better HTTP protocol support +* Added `path` to `extraVolumes` to define where the volume should be + mounted. Defaults to `/vault/userconfig` +* Upgraded Vault to 1.2.2 + +Bugs: + +* Fixed bug where upgrade would fail because immutable labels were being + changed (Helm Version label) +* Fixed bug where UI service used wrong selector after updating helm labels +* Added `VAULT_API_ADDR` env to Vault pod to fixed bug where Vault thinks + Consul is the active node +* Removed `step-down` preStop since it requires authentication. Shutdown signal + sent by Kube acts similar to `step-down` + + +## 0.1.1 (August 7th, 2019) + +Features: + +* Added `authDelegator` Cluster Role Binding to Vault service account for + bootstrapping Kube auth method + +Improvements: + +* Added `server.service.clusterIP` to `values.yml` so users can toggle + the Vault service to headless by using the value `None`. +* Upgraded Vault to 1.2.1 + +## 0.1.0 (August 6th, 2019) + +Initial release diff --git a/charts/partners/hashicorp/vault/0.25.0/src/CODEOWNERS b/charts/partners/hashicorp/vault/0.25.0/src/CODEOWNERS new file mode 100644 index 00000000..af6a3500 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/CODEOWNERS @@ -0,0 +1 @@ +* @hashicorp/vault-ecosystem-foundations diff --git a/charts/partners/hashicorp/vault/0.25.0/src/CONTRIBUTING.md b/charts/partners/hashicorp/vault/0.25.0/src/CONTRIBUTING.md new file mode 100644 index 00000000..ad31ac92 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/CONTRIBUTING.md @@ -0,0 +1,247 @@ +# Contributing to Vault Helm + +**Please note:** We take Vault's security and our users' trust very seriously. +If you believe you have found a security issue in Vault, please responsibly +disclose by contacting us at security@hashicorp.com. + +**First:** if you're unsure or afraid of _anything_, just ask or submit the +issue or pull request anyways. You won't be yelled at for giving it your best +effort. The worst that can happen is that you'll be politely asked to change +something. We appreciate any sort of contributions, and don't want a wall of +rules to get in the way of that. + +That said, if you want to ensure that a pull request is likely to be merged, +talk to us! You can find out our thoughts and ensure that your contribution +won't clash or be obviated by Vault's normal direction. A great way to do this +is via the [Vault Discussion Forum][1]. + +This document will cover what we're looking for in terms of reporting issues. +By addressing all the points we're looking for, it raises the chances we can +quickly merge or address your contributions. + +[1]: https://discuss.hashicorp.com/c/vault + +## Issues + +### Reporting an Issue + +* Make sure you test against the latest released version. It is possible + we already fixed the bug you're experiencing. Even better is if you can test + against `main`, as bugs are fixed regularly but new versions are only + released every few months. + +* Provide steps to reproduce the issue, and if possible include the expected + results as well as the actual results. Please provide text, not screen shots! + +* Respond as promptly as possible to any questions made by the Vault + team to your issue. Stale issues will be closed periodically. + +### Issue Lifecycle + +1. The issue is reported. + +2. The issue is verified and categorized by a Vault Helm collaborator. + Categorization is done via tags. For example, bugs are marked as "bugs". + +3. Unless it is critical, the issue may be left for a period of time (sometimes + many weeks), giving outside contributors -- maybe you!? -- a chance to + address the issue. + +4. The issue is addressed in a pull request or commit. The issue will be + referenced in the commit message so that the code that fixes it is clearly + linked. + +5. The issue is closed. Sometimes, valid issues will be closed to keep + the issue tracker clean. The issue is still indexed and available for + future viewers, or can be re-opened if necessary. + +## Testing + +The Helm chart ships with both unit and acceptance tests. + +The unit tests don't require any active Kubernetes cluster and complete +very quickly. These should be used for fast feedback during development. +The acceptance tests require a Kubernetes cluster with a configured `kubectl`. + +### Test Using Docker Container + +The following are the instructions for running bats tests using a Docker container. + +#### Prerequisites + +* Docker installed +* `vault-helm` checked out locally + +#### Test + +**Note:** the following commands should be run from the `vault-helm` directory. + +First, build the Docker image for running the tests: + +```shell +docker build -f ${PWD}/test/docker/Test.dockerfile ${PWD}/test/docker/ -t vault-helm-test +``` +Next, execute the tests with the following commands: +```shell +docker run -it --rm -v "${PWD}:/test" vault-helm-test bats /test/test/unit +``` +It's possible to only run specific bats tests using regular expressions. +For example, the following will run only tests with "injector" in the name: +```shell +docker run -it --rm -v "${PWD}:/test" vault-helm-test bats /test/test/unit -f "injector" +``` + +### Test Manually +The following are the instructions for running bats tests on your workstation. +#### Prerequisites +* [Bats](https://github.com/bats-core/bats-core) + ```bash + brew install bats-core + ``` +* [yq](https://pypi.org/project/yq/) + ```bash + brew install python-yq + ``` +* [helm](https://helm.sh) + ```bash + brew install kubernetes-helm + ``` + +#### Test + +To run the unit tests: + + bats ./test/unit + +To run the acceptance tests: + + bats ./test/acceptance + +If the acceptance tests fail, deployed resources in the Kubernetes cluster +may not be properly cleaned up. We recommend recycling the Kubernetes cluster to +start from a clean slate. + +**Note:** There is a Terraform configuration in the +[`test/terraform/`](https://github.com/hashicorp/vault-helm/tree/main/test/terraform) directory +that can be used to quickly bring up a GKE cluster and configure +`kubectl` and `helm` locally. This can be used to quickly spin up a test +cluster for acceptance tests. Unit tests _do not_ require a running Kubernetes +cluster. + +### Writing Unit Tests + +Changes to the Helm chart should be accompanied by appropriate unit tests. + +#### Formatting + +- Put tests in the test file in the same order as the variables appear in the `values.yaml`. +- Start tests for a chart value with a header that says what is being tested, like this: + ``` + #-------------------------------------------------------------------- + # annotations + ``` + +- Name the test based on what it's testing in the following format (this will be its first line): + ``` + @test "
: " { + ``` + + When adding tests to an existing file, the first section will be the same as the other tests in the file. + +#### Test Details + +[Bats](https://github.com/bats-core/bats-core) provides a way to run commands in a shell and inspect the output in an automated way. +In all of the tests in this repo, the base command being run is [helm template](https://docs.helm.sh/helm/#helm-template) which turns the templated files into straight yaml output. +In this way, we're able to test that the various conditionals in the templates render as we would expect. + +Each test defines the files that should be rendered using the `--show-only` flag, then it might adjust chart values by adding `--set` flags as well. +The output from this `helm template` command is then piped to [yq](https://pypi.org/project/yq/). +`yq` allows us to pull out just the information we're interested in, either by referencing its position in the yaml file directly or giving information about it (like its length). +The `-r` flag can be used with `yq` to return a raw string instead of a quoted one which is especially useful when looking for an exact match. + +The test passes or fails based on the conditional at the end that is in square brackets, which is a comparison of our expected value and the output of `helm template` piped to `yq`. + +The `| tee /dev/stderr ` pieces direct any terminal output of the `helm template` and `yq` commands to stderr so that it doesn't interfere with `bats`. + +#### Test Examples + +Here are some examples of common test patterns: + +- Check that a value is disabled by default + + ``` + @test "ui/Service: no type by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "null" ] + } + ``` + + In this example, nothing is changed from the default templates (no `--set` flags), then we use `yq` to retrieve the value we're checking, `.spec.type`. + This output is then compared against our expected value (`null` in this case) in the assertion `[ "${actual}" = "null" ]`. + + +- Check that a template value is rendered to a specific value + ``` + @test "ui/Service: specified type" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'ui.serviceType=LoadBalancer' \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "LoadBalancer" ] + } + ``` + + This is very similar to the last example, except we've changed a default value with the `--set` flag and correspondingly changed the expected value. + +- Check that a template value contains several values + ``` + @test "server/standalone-StatefulSet: custom resources" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.resources.requests.memory=256Mi' \ + --set 'server.resources.requests.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.requests.memory' | tee /dev/stderr) + [ "${actual}" = "256Mi" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.resources.limits.memory=256Mi' \ + --set 'server.resources.limits.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.limits.memory' | tee /dev/stderr) + [ "${actual}" = "256Mi" ] + ``` + + *Note:* If testing more than two conditions, it would be good to separate the `helm template` part of the command from the `yq` sections to reduce redundant work. + +- Check that an entire template file is not rendered + ``` + @test "syncCatalog/Deployment: disabled by default" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'global.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + } + ``` + Here we are check the length of the command output to see if the anything is rendered. + This style can easily be switched to check that a file is rendered instead. + +## Contributor License Agreement + +We require that all contributors sign our Contributor License Agreement ("CLA") +before we can accept the contribution. + +[Learn more about why HashiCorp requires a CLA and what the CLA includes](https://www.hashicorp.com/cla) diff --git a/charts/partners/hashicorp/vault/0.25.0/src/Chart.yaml b/charts/partners/hashicorp/vault/0.25.0/src/Chart.yaml new file mode 100644 index 00000000..1211b356 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/Chart.yaml @@ -0,0 +1,23 @@ +annotations: + charts.openshift.io/name: HashiCorp Vault +apiVersion: v2 +appVersion: 1.14.0 +description: Official HashiCorp Vault Chart +home: https://www.vaultproject.io +icon: https://github.com/hashicorp/vault/raw/f22d202cde2018f9455dec755118a9b84586e082/Vault_PrimaryLogo_Black.png +keywords: +- vault +- security +- encryption +- secrets +- management +- automation +- infrastructure +kubeVersion: '>= 1.20.0-0' +name: vault +sources: +- https://github.com/hashicorp/vault +- https://github.com/hashicorp/vault-helm +- https://github.com/hashicorp/vault-k8s +- https://github.com/hashicorp/vault-csi-provider +version: 0.25.0 diff --git a/charts/partners/hashicorp/vault/0.25.0/src/LICENSE b/charts/partners/hashicorp/vault/0.25.0/src/LICENSE new file mode 100644 index 00000000..74f38c01 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/LICENSE @@ -0,0 +1,355 @@ +Copyright (c) 2018 HashiCorp, Inc. + +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. “Contributor” + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. “Contributor Version” + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution” + + means Covered Software of a particular Contributor. + +1.4. “Covered Software” + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. “Incompatible With Secondary Licenses” + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of version + 1.1 or earlier of the License, but not also under the terms of a + Secondary License. + +1.6. “Executable Form” + + means any form of the work other than Source Code Form. + +1.7. “Larger Work” + + means a work that combines Covered Software with other material, in a separate + file or files, that is not Covered Software. + +1.8. “License” + + means this document. + +1.9. “Licensable” + + means having the right to grant, to the maximum extent possible, whether at the + time of the initial grant or subsequently, any and all of the rights conveyed by + this License. + +1.10. “Modifications” + + means any of the following: + + a. any file in Source Code Form that results from an addition to, deletion + from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. “Patent Claims” of a Contributor + + means any patent claim(s), including without limitation, method, process, + and apparatus claims, in any patent Licensable by such Contributor that + would be infringed, but for the grant of the License, by the making, + using, selling, offering for sale, having made, import, or transfer of + either its Contributions or its Contributor Version. + +1.12. “Secondary License” + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form” + + means the form of the work preferred for making modifications. + +1.14. “You” (or “Your”) + + means an individual or a legal entity exercising rights under this + License. For legal entities, “You” includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, “control” means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or as + part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its Contributions + or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution become + effective for each Contribution on the date the Contributor first distributes + such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under this + License. No additional rights or licenses will be implied from the distribution + or licensing of Covered Software under this License. Notwithstanding Section + 2.1(b) above, no patent license is granted by a Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of its + Contributions. + + This License does not grant any rights in the trademarks, service marks, or + logos of any Contributor (except as may be necessary to comply with the + notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this License + (see Section 10.2) or under the terms of a Secondary License (if permitted + under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its Contributions + are its original creation(s) or it has sufficient rights to grant the + rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under applicable + copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under the + terms of this License. You must inform recipients that the Source Code Form + of the Covered Software is governed by the terms of this License, and how + they can obtain a copy of this License. You may not attempt to alter or + restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this License, + or sublicense it under different terms, provided that the license for + the Executable Form does not attempt to limit or alter the recipients’ + rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for the + Covered Software. If the Larger Work is a combination of Covered Software + with a work governed by one or more Secondary Licenses, and the Covered + Software is not Incompatible With Secondary Licenses, this License permits + You to additionally distribute such Covered Software under the terms of + such Secondary License(s), so that the recipient of the Larger Work may, at + their option, further distribute the Covered Software under the terms of + either this License or such Secondary License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices (including + copyright notices, patent notices, disclaimers of warranty, or limitations + of liability) contained within the Source Code Form of the Covered + Software, except that You may alter any license notices to the extent + required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on behalf + of any Contributor. You must make it absolutely clear that any such + warranty, support, indemnity, or liability obligation is offered by You + alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, judicial + order, or regulation then You must: (a) comply with the terms of this License + to the maximum extent possible; and (b) describe the limitations and the code + they affect. Such description must be placed in a text file included with all + distributions of the Covered Software under this License. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing basis, + if such Contributor fails to notify You of the non-compliance by some + reasonable means prior to 60 days after You have come back into compliance. + Moreover, Your grants from a particular Contributor are reinstated on an + ongoing basis if such Contributor notifies You of the non-compliance by + some reasonable means, this is the first time You have received notice of + non-compliance with this License from such Contributor, and You become + compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, counter-claims, + and cross-claims) alleging that a Contributor Version directly or + indirectly infringes any patent, then the rights granted to You by any and + all Contributors for the Covered Software under Section 2.1 of this License + shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an “as is” basis, without + warranty of any kind, either expressed, implied, or statutory, including, + without limitation, warranties that the Covered Software is free of defects, + merchantable, fit for a particular purpose or non-infringing. The entire + risk as to the quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, You (not any + Contributor) assume the cost of any necessary servicing, repair, or + correction. This disclaimer of warranty constitutes an essential part of this + License. No use of any Covered Software is authorized under this License + except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from such + party’s negligence to the extent applicable law prohibits such limitation. + Some jurisdictions do not allow the exclusion or limitation of incidental or + consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts of + a jurisdiction where the defendant maintains its principal place of business + and such litigation shall be governed by laws of that jurisdiction, without + reference to its conflict-of-law provisions. Nothing in this Section shall + prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. Any law or regulation which provides that the language of a + contract shall be construed against the drafter shall not be used to construe + this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version of + the License under which You originally received the Covered Software, or + under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a relevant +directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is “Incompatible + With Secondary Licenses”, as defined by + the Mozilla Public License, v. 2.0. diff --git a/charts/partners/hashicorp/vault/0.25.0/src/Makefile b/charts/partners/hashicorp/vault/0.25.0/src/Makefile new file mode 100644 index 00000000..56002206 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/Makefile @@ -0,0 +1,101 @@ +TEST_IMAGE?=vault-helm-test +GOOGLE_CREDENTIALS?=vault-helm-test.json +CLOUDSDK_CORE_PROJECT?=vault-helm-dev-246514 +# set to run a single test - e.g acceptance/server-ha-enterprise-dr.bats +ACCEPTANCE_TESTS?=acceptance + +# filter bats unit tests to run. +UNIT_TESTS_FILTER?='.*' + +# set to 'true' to run acceptance tests locally in a kind cluster +LOCAL_ACCEPTANCE_TESTS?=false + +# kind cluster name +KIND_CLUSTER_NAME?=vault-helm + +# kind k8s version +KIND_K8S_VERSION?=v1.26.3 + +# Generate json schema for chart values. See test/README.md for more details. +values-schema: + helm schema-gen values.yaml > values.schema.json + +test-image: + @docker build --rm -t $(TEST_IMAGE) -f $(CURDIR)/test/docker/Test.dockerfile $(CURDIR) + +test-unit: + @docker run --rm -it -v ${PWD}:/helm-test $(TEST_IMAGE) bats -f $(UNIT_TESTS_FILTER) /helm-test/test/unit + +test-bats: test-unit test-acceptance + +test: test-image test-bats + +# run acceptance tests on GKE +# set google project/credential vars above +test-acceptance: +ifeq ($(LOCAL_ACCEPTANCE_TESTS),true) + make setup-kind acceptance +else + @docker run -it -v ${PWD}:/helm-test \ + -e GOOGLE_CREDENTIALS=${GOOGLE_CREDENTIALS} \ + -e CLOUDSDK_CORE_PROJECT=${CLOUDSDK_CORE_PROJECT} \ + -e KUBECONFIG=/helm-test/.kube/config \ + -e VAULT_LICENSE_CI=${VAULT_LICENSE_CI} \ + -w /helm-test \ + $(TEST_IMAGE) \ + make acceptance +endif + +# destroy GKE cluster using terraform +test-destroy: + @docker run -it -v ${PWD}:/helm-test \ + -e GOOGLE_CREDENTIALS=${GOOGLE_CREDENTIALS} \ + -e CLOUDSDK_CORE_PROJECT=${CLOUDSDK_CORE_PROJECT} \ + -w /helm-test \ + $(TEST_IMAGE) \ + make destroy-cluster + +# provision GKE cluster using terraform +test-provision: + @docker run -it -v ${PWD}:/helm-test \ + -e GOOGLE_CREDENTIALS=${GOOGLE_CREDENTIALS} \ + -e CLOUDSDK_CORE_PROJECT=${CLOUDSDK_CORE_PROJECT} \ + -e KUBECONFIG=/helm-test/.kube/config \ + -w /helm-test \ + $(TEST_IMAGE) \ + make provision-cluster + +# this target is for running the acceptance tests +# it is run in the docker container above when the test-acceptance target is invoked +acceptance: +ifneq ($(LOCAL_ACCEPTANCE_TESTS),true) + gcloud auth activate-service-account --key-file=${GOOGLE_CREDENTIALS} +endif + bats --tap --timing test/${ACCEPTANCE_TESTS} + +# this target is for provisioning the GKE cluster +# it is run in the docker container above when the test-provision target is invoked +provision-cluster: + gcloud auth activate-service-account --key-file=${GOOGLE_CREDENTIALS} + terraform init test/terraform + terraform apply -var project=${CLOUDSDK_CORE_PROJECT} -var init_cli=true -auto-approve test/terraform + +# this target is for removing the GKE cluster +# it is run in the docker container above when the test-destroy target is invoked +destroy-cluster: + terraform destroy -auto-approve + +# create a kind cluster for running the acceptance tests locally +setup-kind: + kind get clusters | grep -q "^${KIND_CLUSTER_NAME}$$" || \ + kind create cluster \ + --image kindest/node:${KIND_K8S_VERSION} \ + --name ${KIND_CLUSTER_NAME} \ + --config $(CURDIR)/test/kind/config.yaml + kubectl config use-context kind-${KIND_CLUSTER_NAME} + +# delete the kind cluster +delete-kind: + kind delete cluster --name ${KIND_CLUSTER_NAME} || : + +.PHONY: values-schema test-image test-unit test-bats test test-acceptance test-destroy test-provision acceptance provision-cluster destroy-cluster diff --git a/charts/partners/hashicorp/vault/0.25.0/src/README.md b/charts/partners/hashicorp/vault/0.25.0/src/README.md new file mode 100644 index 00000000..6e701436 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/README.md @@ -0,0 +1,43 @@ +# Vault Helm Chart + +> :warning: **Please note**: We take Vault's security and our users' trust very seriously. If +you believe you have found a security issue in Vault Helm, _please responsibly disclose_ +by contacting us at [security@hashicorp.com](mailto:security@hashicorp.com). + +This repository contains the official HashiCorp Helm chart for installing +and configuring Vault on Kubernetes. This chart supports multiple use +cases of Vault on Kubernetes depending on the values provided. + +For full documentation on this Helm chart along with all the ways you can +use Vault with Kubernetes, please see the +[Vault and Kubernetes documentation](https://www.vaultproject.io/docs/platform/k8s/). + +## Prerequisites + +To use the charts here, [Helm](https://helm.sh/) must be configured for your +Kubernetes cluster. Setting up Kubernetes and Helm is outside the scope of +this README. Please refer to the Kubernetes and Helm documentation. + +The versions required are: + + * **Helm 3.6+** + * **Kubernetes 1.22+** - This is the earliest version of Kubernetes tested. + It is possible that this chart works with earlier versions but it is + untested. + +## Usage + +To install the latest version of this chart, add the Hashicorp helm repository +and run `helm install`: + +```console +$ helm repo add hashicorp https://helm.releases.hashicorp.com +"hashicorp" has been added to your repositories + +$ helm install vault hashicorp/vault +``` + +Please see the many options supported in the `values.yaml` file. These are also +fully documented directly on the [Vault +website](https://www.vaultproject.io/docs/platform/k8s/helm) along with more +detailed installation instructions. diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/NOTES.txt b/charts/partners/hashicorp/vault/0.25.0/src/templates/NOTES.txt new file mode 100644 index 00000000..8e267121 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/NOTES.txt @@ -0,0 +1,14 @@ + +Thank you for installing HashiCorp Vault! + +Now that you have deployed Vault, you should look over the docs on using +Vault with Kubernetes available here: + +https://www.vaultproject.io/docs/ + + +Your release is named {{ .Release.Name }}. To learn more about the release, try: + + $ helm status {{ .Release.Name }} + $ helm get manifest {{ .Release.Name }} + diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/_helpers.tpl b/charts/partners/hashicorp/vault/0.25.0/src/templates/_helpers.tpl new file mode 100644 index 00000000..dafac378 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/_helpers.tpl @@ -0,0 +1,996 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to +this (by the DNS naming spec). If release name contains chart name it will +be used as a full name. +*/}} +{{- define "vault.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "vault.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Expand the name of the chart. +*/}} +{{- define "vault.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Compute if the csi driver is enabled. +*/}} +{{- define "vault.csiEnabled" -}} +{{- $_ := set . "csiEnabled" (or + (eq (.Values.csi.enabled | toString) "true") + (and (eq (.Values.csi.enabled | toString) "-") (eq (.Values.global.enabled | toString) "true"))) -}} +{{- end -}} + +{{/* +Compute if the injector is enabled. +*/}} +{{- define "vault.injectorEnabled" -}} +{{- $_ := set . "injectorEnabled" (or + (eq (.Values.injector.enabled | toString) "true") + (and (eq (.Values.injector.enabled | toString) "-") (eq (.Values.global.enabled | toString) "true"))) -}} +{{- end -}} + +{{/* +Compute if the server is enabled. +*/}} +{{- define "vault.serverEnabled" -}} +{{- $_ := set . "serverEnabled" (or + (eq (.Values.server.enabled | toString) "true") + (and (eq (.Values.server.enabled | toString) "-") (eq (.Values.global.enabled | toString) "true"))) -}} +{{- end -}} + +{{/* +Compute if the server serviceaccount is enabled. +*/}} +{{- define "vault.serverServiceAccountEnabled" -}} +{{- $_ := set . "serverServiceAccountEnabled" + (and + (eq (.Values.server.serviceAccount.create | toString) "true" ) + (or + (eq (.Values.server.enabled | toString) "true") + (eq (.Values.global.enabled | toString) "true"))) -}} +{{- end -}} + +{{/* +Compute if the server auth delegator serviceaccount is enabled. +*/}} +{{- define "vault.serverAuthDelegator" -}} +{{- $_ := set . "serverAuthDelegator" + (and + (eq (.Values.server.authDelegator.enabled | toString) "true" ) + (or (eq (.Values.server.serviceAccount.create | toString) "true") + (not (eq .Values.server.serviceAccount.name ""))) + (or + (eq (.Values.server.enabled | toString) "true") + (eq (.Values.global.enabled | toString) "true"))) -}} +{{- end -}} + +{{/* +Compute if the server service is enabled. +*/}} +{{- define "vault.serverServiceEnabled" -}} +{{- template "vault.serverEnabled" . -}} +{{- $_ := set . "serverServiceEnabled" (and .serverEnabled (eq (.Values.server.service.enabled | toString) "true")) -}} +{{- end -}} + +{{/* +Compute if the ui is enabled. +*/}} +{{- define "vault.uiEnabled" -}} +{{- $_ := set . "uiEnabled" (or + (eq (.Values.ui.enabled | toString) "true") + (and (eq (.Values.ui.enabled | toString) "-") (eq (.Values.global.enabled | toString) "true"))) -}} +{{- end -}} + +{{/* +Compute the maximum number of unavailable replicas for the PodDisruptionBudget. +This defaults to (n/2)-1 where n is the number of members of the server cluster. +Add a special case for replicas=1, where it should default to 0 as well. +*/}} +{{- define "vault.pdb.maxUnavailable" -}} +{{- if eq (int .Values.server.ha.replicas) 1 -}} +{{ 0 }} +{{- else if .Values.server.ha.disruptionBudget.maxUnavailable -}} +{{ .Values.server.ha.disruptionBudget.maxUnavailable -}} +{{- else -}} +{{- div (sub (div (mul (int .Values.server.ha.replicas) 10) 2) 1) 10 -}} +{{- end -}} +{{- end -}} + +{{/* +Set the variable 'mode' to the server mode requested by the user to simplify +template logic. +*/}} +{{- define "vault.mode" -}} + {{- template "vault.serverEnabled" . -}} + {{- if or (.Values.injector.externalVaultAddr) (.Values.global.externalVaultAddr) -}} + {{- $_ := set . "mode" "external" -}} + {{- else if not .serverEnabled -}} + {{- $_ := set . "mode" "external" -}} + {{- else if eq (.Values.server.dev.enabled | toString) "true" -}} + {{- $_ := set . "mode" "dev" -}} + {{- else if eq (.Values.server.ha.enabled | toString) "true" -}} + {{- $_ := set . "mode" "ha" -}} + {{- else if or (eq (.Values.server.standalone.enabled | toString) "true") (eq (.Values.server.standalone.enabled | toString) "-") -}} + {{- $_ := set . "mode" "standalone" -}} + {{- else -}} + {{- $_ := set . "mode" "" -}} + {{- end -}} +{{- end -}} + +{{/* +Set's the replica count based on the different modes configured by user +*/}} +{{- define "vault.replicas" -}} + {{ if eq .mode "standalone" }} + {{- default 1 -}} + {{ else if eq .mode "ha" }} + {{- .Values.server.ha.replicas | default 3 -}} + {{ else }} + {{- default 1 -}} + {{ end }} +{{- end -}} + +{{/* +Set's up configmap mounts if this isn't a dev deployment and the user +defined a custom configuration. Additionally iterates over any +extra volumes the user may have specified (such as a secret with TLS). +*/}} +{{- define "vault.volumes" -}} + {{- if and (ne .mode "dev") (or (.Values.server.standalone.config) (.Values.server.ha.config)) }} + - name: config + configMap: + name: {{ template "vault.fullname" . }}-config + {{ end }} + {{- range .Values.server.extraVolumes }} + - name: userconfig-{{ .name }} + {{ .type }}: + {{- if (eq .type "configMap") }} + name: {{ .name }} + {{- else if (eq .type "secret") }} + secretName: {{ .name }} + {{- end }} + defaultMode: {{ .defaultMode | default 420 }} + {{- end }} + {{- if .Values.server.volumes }} + {{- toYaml .Values.server.volumes | nindent 8}} + {{- end }} + {{- if (and .Values.server.enterpriseLicense.secretName .Values.server.enterpriseLicense.secretKey) }} + - name: vault-license + secret: + secretName: {{ .Values.server.enterpriseLicense.secretName }} + defaultMode: 0440 + {{- end }} +{{- end -}} + +{{/* +Set's the args for custom command to render the Vault configuration +file with IP addresses to make the out of box experience easier +for users looking to use this chart with Consul Helm. +*/}} +{{- define "vault.args" -}} + {{ if or (eq .mode "standalone") (eq .mode "ha") }} + - | + cp /vault/config/extraconfig-from-values.hcl /tmp/storageconfig.hcl; + [ -n "${HOST_IP}" ] && sed -Ei "s|HOST_IP|${HOST_IP?}|g" /tmp/storageconfig.hcl; + [ -n "${POD_IP}" ] && sed -Ei "s|POD_IP|${POD_IP?}|g" /tmp/storageconfig.hcl; + [ -n "${HOSTNAME}" ] && sed -Ei "s|HOSTNAME|${HOSTNAME?}|g" /tmp/storageconfig.hcl; + [ -n "${API_ADDR}" ] && sed -Ei "s|API_ADDR|${API_ADDR?}|g" /tmp/storageconfig.hcl; + [ -n "${TRANSIT_ADDR}" ] && sed -Ei "s|TRANSIT_ADDR|${TRANSIT_ADDR?}|g" /tmp/storageconfig.hcl; + [ -n "${RAFT_ADDR}" ] && sed -Ei "s|RAFT_ADDR|${RAFT_ADDR?}|g" /tmp/storageconfig.hcl; + /usr/local/bin/docker-entrypoint.sh vault server -config=/tmp/storageconfig.hcl {{ .Values.server.extraArgs }} + {{ else if eq .mode "dev" }} + - | + /usr/local/bin/docker-entrypoint.sh vault server -dev {{ .Values.server.extraArgs }} + {{ end }} +{{- end -}} + +{{/* +Set's additional environment variables based on the mode. +*/}} +{{- define "vault.envs" -}} + {{ if eq .mode "dev" }} + - name: VAULT_DEV_ROOT_TOKEN_ID + value: {{ .Values.server.dev.devRootToken }} + - name: VAULT_DEV_LISTEN_ADDRESS + value: "[::]:8200" + {{ end }} +{{- end -}} + +{{/* +Set's which additional volumes should be mounted to the container +based on the mode configured. +*/}} +{{- define "vault.mounts" -}} + {{ if eq (.Values.server.auditStorage.enabled | toString) "true" }} + - name: audit + mountPath: {{ .Values.server.auditStorage.mountPath }} + {{ end }} + {{ if or (eq .mode "standalone") (and (eq .mode "ha") (eq (.Values.server.ha.raft.enabled | toString) "true")) }} + {{ if eq (.Values.server.dataStorage.enabled | toString) "true" }} + - name: data + mountPath: {{ .Values.server.dataStorage.mountPath }} + {{ end }} + {{ end }} + {{ if and (ne .mode "dev") (or (.Values.server.standalone.config) (.Values.server.ha.config)) }} + - name: config + mountPath: /vault/config + {{ end }} + {{- range .Values.server.extraVolumes }} + - name: userconfig-{{ .name }} + readOnly: true + mountPath: {{ .path | default "/vault/userconfig" }}/{{ .name }} + {{- end }} + {{- if .Values.server.volumeMounts }} + {{- toYaml .Values.server.volumeMounts | nindent 12}} + {{- end }} + {{- if (and .Values.server.enterpriseLicense.secretName .Values.server.enterpriseLicense.secretKey) }} + - name: vault-license + mountPath: /vault/license + readOnly: true + {{- end }} +{{- end -}} + +{{/* +Set's up the volumeClaimTemplates when data or audit storage is required. HA +might not use data storage since Consul is likely it's backend, however, audit +storage might be desired by the user. +*/}} +{{- define "vault.volumeclaims" -}} + {{- if and (ne .mode "dev") (or .Values.server.dataStorage.enabled .Values.server.auditStorage.enabled) }} + volumeClaimTemplates: + {{- if and (eq (.Values.server.dataStorage.enabled | toString) "true") (or (eq .mode "standalone") (eq (.Values.server.ha.raft.enabled | toString ) "true" )) }} + - metadata: + name: data + {{- include "vault.dataVolumeClaim.annotations" . | nindent 6 }} + spec: + accessModes: + - {{ .Values.server.dataStorage.accessMode | default "ReadWriteOnce" }} + resources: + requests: + storage: {{ .Values.server.dataStorage.size }} + {{- if .Values.server.dataStorage.storageClass }} + storageClassName: {{ .Values.server.dataStorage.storageClass }} + {{- end }} + {{ end }} + {{- if eq (.Values.server.auditStorage.enabled | toString) "true" }} + - metadata: + name: audit + {{- include "vault.auditVolumeClaim.annotations" . | nindent 6 }} + spec: + accessModes: + - {{ .Values.server.auditStorage.accessMode | default "ReadWriteOnce" }} + resources: + requests: + storage: {{ .Values.server.auditStorage.size }} + {{- if .Values.server.auditStorage.storageClass }} + storageClassName: {{ .Values.server.auditStorage.storageClass }} + {{- end }} + {{ end }} + {{ end }} +{{- end -}} + +{{/* +Set's the affinity for pod placement when running in standalone and HA modes. +*/}} +{{- define "vault.affinity" -}} + {{- if and (ne .mode "dev") .Values.server.affinity }} + affinity: + {{ $tp := typeOf .Values.server.affinity }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.affinity . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.server.affinity | nindent 8 }} + {{- end }} + {{ end }} +{{- end -}} + +{{/* +Sets the injector affinity for pod placement +*/}} +{{- define "injector.affinity" -}} + {{- if .Values.injector.affinity }} + affinity: + {{ $tp := typeOf .Values.injector.affinity }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.affinity . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.injector.affinity | nindent 8 }} + {{- end }} + {{ end }} +{{- end -}} + +{{/* +Sets the topologySpreadConstraints when running in standalone and HA modes. +*/}} +{{- define "vault.topologySpreadConstraints" -}} + {{- if and (ne .mode "dev") .Values.server.topologySpreadConstraints }} + topologySpreadConstraints: + {{ $tp := typeOf .Values.server.topologySpreadConstraints }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.topologySpreadConstraints . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.server.topologySpreadConstraints | nindent 8 }} + {{- end }} + {{ end }} +{{- end -}} + + +{{/* +Sets the injector topologySpreadConstraints for pod placement +*/}} +{{- define "injector.topologySpreadConstraints" -}} + {{- if .Values.injector.topologySpreadConstraints }} + topologySpreadConstraints: + {{ $tp := typeOf .Values.injector.topologySpreadConstraints }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.topologySpreadConstraints . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.injector.topologySpreadConstraints | nindent 8 }} + {{- end }} + {{ end }} +{{- end -}} + +{{/* +Sets the toleration for pod placement when running in standalone and HA modes. +*/}} +{{- define "vault.tolerations" -}} + {{- if and (ne .mode "dev") .Values.server.tolerations }} + tolerations: + {{- $tp := typeOf .Values.server.tolerations }} + {{- if eq $tp "string" }} + {{ tpl .Values.server.tolerations . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.server.tolerations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets the injector toleration for pod placement +*/}} +{{- define "injector.tolerations" -}} + {{- if .Values.injector.tolerations }} + tolerations: + {{- $tp := typeOf .Values.injector.tolerations }} + {{- if eq $tp "string" }} + {{ tpl .Values.injector.tolerations . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.injector.tolerations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Set's the node selector for pod placement when running in standalone and HA modes. +*/}} +{{- define "vault.nodeselector" -}} + {{- if and (ne .mode "dev") .Values.server.nodeSelector }} + nodeSelector: + {{- $tp := typeOf .Values.server.nodeSelector }} + {{- if eq $tp "string" }} + {{ tpl .Values.server.nodeSelector . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.server.nodeSelector | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets the injector node selector for pod placement +*/}} +{{- define "injector.nodeselector" -}} + {{- if .Values.injector.nodeSelector }} + nodeSelector: + {{- $tp := typeOf .Values.injector.nodeSelector }} + {{- if eq $tp "string" }} + {{ tpl .Values.injector.nodeSelector . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.injector.nodeSelector | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets the injector deployment update strategy +*/}} +{{- define "injector.strategy" -}} + {{- if .Values.injector.strategy }} + strategy: + {{- $tp := typeOf .Values.injector.strategy }} + {{- if eq $tp "string" }} + {{ tpl .Values.injector.strategy . | nindent 4 | trim }} + {{- else }} + {{- toYaml .Values.injector.strategy | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra pod annotations +*/}} +{{- define "vault.annotations" -}} + {{- if .Values.server.annotations }} + annotations: + {{- $tp := typeOf .Values.server.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.annotations . | nindent 8 }} + {{- else }} + {{- toYaml .Values.server.annotations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra injector pod annotations +*/}} +{{- define "injector.annotations" -}} + {{- if .Values.injector.annotations }} + annotations: + {{- $tp := typeOf .Values.injector.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.annotations . | nindent 8 }} + {{- else }} + {{- toYaml .Values.injector.annotations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra injector service annotations +*/}} +{{- define "injector.service.annotations" -}} + {{- if .Values.injector.service.annotations }} + annotations: + {{- $tp := typeOf .Values.injector.service.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.service.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.injector.service.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +securityContext for the injector pod level. +*/}} +{{- define "injector.securityContext.pod" -}} + {{- if .Values.injector.securityContext.pod }} + securityContext: + {{- $tp := typeOf .Values.injector.securityContext.pod }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.securityContext.pod . | nindent 8 }} + {{- else }} + {{- toYaml .Values.injector.securityContext.pod | nindent 8 }} + {{- end }} + {{- else if not .Values.global.openshift }} + securityContext: + runAsNonRoot: true + runAsGroup: {{ .Values.injector.gid | default 1000 }} + runAsUser: {{ .Values.injector.uid | default 100 }} + fsGroup: {{ .Values.injector.gid | default 1000 }} + {{- end }} +{{- end -}} + +{{/* +securityContext for the injector container level. +*/}} +{{- define "injector.securityContext.container" -}} + {{- if .Values.injector.securityContext.container}} + securityContext: + {{- $tp := typeOf .Values.injector.securityContext.container }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.securityContext.container . | nindent 12 }} + {{- else }} + {{- toYaml .Values.injector.securityContext.container | nindent 12 }} + {{- end }} + {{- else if not .Values.global.openshift }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + {{- end }} +{{- end -}} + +{{/* +securityContext for the statefulset pod template. +*/}} +{{- define "server.statefulSet.securityContext.pod" -}} + {{- if .Values.server.statefulSet.securityContext.pod }} + securityContext: + {{- $tp := typeOf .Values.server.statefulSet.securityContext.pod }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.statefulSet.securityContext.pod . | nindent 8 }} + {{- else }} + {{- toYaml .Values.server.statefulSet.securityContext.pod | nindent 8 }} + {{- end }} + {{- else if not .Values.global.openshift }} + securityContext: + runAsNonRoot: true + runAsGroup: {{ .Values.server.gid | default 1000 }} + runAsUser: {{ .Values.server.uid | default 100 }} + fsGroup: {{ .Values.server.gid | default 1000 }} + {{- end }} +{{- end -}} + +{{/* +securityContext for the statefulset vault container +*/}} +{{- define "server.statefulSet.securityContext.container" -}} + {{- if .Values.server.statefulSet.securityContext.container }} + securityContext: + {{- $tp := typeOf .Values.server.statefulSet.securityContext.container }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.statefulSet.securityContext.container . | nindent 12 }} + {{- else }} + {{- toYaml .Values.server.statefulSet.securityContext.container | nindent 12 }} + {{- end }} + {{- else if not .Values.global.openshift }} + securityContext: + allowPrivilegeEscalation: false + {{- end }} +{{- end -}} + + +{{/* +Sets extra injector service account annotations +*/}} +{{- define "injector.serviceAccount.annotations" -}} + {{- if and (ne .mode "dev") .Values.injector.serviceAccount.annotations }} + annotations: + {{- $tp := typeOf .Values.injector.serviceAccount.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.serviceAccount.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.injector.serviceAccount.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra injector webhook annotations +*/}} +{{- define "injector.webhookAnnotations" -}} + {{- if or (((.Values.injector.webhook)).annotations) (.Values.injector.webhookAnnotations) }} + annotations: + {{- $tp := typeOf (or (((.Values.injector.webhook)).annotations) (.Values.injector.webhookAnnotations)) }} + {{- if eq $tp "string" }} + {{- tpl (((.Values.injector.webhook)).annotations | default .Values.injector.webhookAnnotations) . | nindent 4 }} + {{- else }} + {{- toYaml (((.Values.injector.webhook)).annotations | default .Values.injector.webhookAnnotations) | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Set's the injector webhook objectSelector +*/}} +{{- define "injector.objectSelector" -}} + {{- $v := or (((.Values.injector.webhook)).objectSelector) (.Values.injector.objectSelector) -}} + {{ if $v }} + objectSelector: + {{- $tp := typeOf $v -}} + {{ if eq $tp "string" }} + {{ tpl $v . | indent 6 | trim }} + {{ else }} + {{ toYaml $v | indent 6 | trim }} + {{ end }} + {{ end }} +{{ end }} + +{{/* +Sets extra ui service annotations +*/}} +{{- define "vault.ui.annotations" -}} + {{- if .Values.ui.annotations }} + annotations: + {{- $tp := typeOf .Values.ui.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.ui.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.ui.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "vault.serviceAccount.name" -}} +{{- if .Values.server.serviceAccount.create -}} + {{ default (include "vault.fullname" .) .Values.server.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.server.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Sets extra service account annotations +*/}} +{{- define "vault.serviceAccount.annotations" -}} + {{- if and (ne .mode "dev") .Values.server.serviceAccount.annotations }} + annotations: + {{- $tp := typeOf .Values.server.serviceAccount.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.serviceAccount.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.serviceAccount.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra ingress annotations +*/}} +{{- define "vault.ingress.annotations" -}} + {{- if .Values.server.ingress.annotations }} + annotations: + {{- $tp := typeOf .Values.server.ingress.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.ingress.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.ingress.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra route annotations +*/}} +{{- define "vault.route.annotations" -}} + {{- if .Values.server.route.annotations }} + annotations: + {{- $tp := typeOf .Values.server.route.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.route.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.route.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra vault server Service annotations +*/}} +{{- define "vault.service.annotations" -}} + {{- if .Values.server.service.annotations }} + {{- $tp := typeOf .Values.server.service.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.service.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.service.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets PodSecurityPolicy annotations +*/}} +{{- define "vault.psp.annotations" -}} + {{- if .Values.global.psp.annotations }} + annotations: + {{- $tp := typeOf .Values.global.psp.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.global.psp.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.global.psp.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra statefulset annotations +*/}} +{{- define "vault.statefulSet.annotations" -}} + {{- if .Values.server.statefulSet.annotations }} + annotations: + {{- $tp := typeOf .Values.server.statefulSet.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.statefulSet.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.statefulSet.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets VolumeClaim annotations for data volume +*/}} +{{- define "vault.dataVolumeClaim.annotations" -}} + {{- if and (ne .mode "dev") (.Values.server.dataStorage.enabled) (.Values.server.dataStorage.annotations) }} + annotations: + {{- $tp := typeOf .Values.server.dataStorage.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.dataStorage.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.dataStorage.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets VolumeClaim annotations for audit volume +*/}} +{{- define "vault.auditVolumeClaim.annotations" -}} + {{- if and (ne .mode "dev") (.Values.server.auditStorage.enabled) (.Values.server.auditStorage.annotations) }} + annotations: + {{- $tp := typeOf .Values.server.auditStorage.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.auditStorage.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.auditStorage.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Set's the container resources if the user has set any. +*/}} +{{- define "vault.resources" -}} + {{- if .Values.server.resources -}} + resources: +{{ toYaml .Values.server.resources | indent 12}} + {{ end }} +{{- end -}} + +{{/* +Sets the container resources if the user has set any. +*/}} +{{- define "injector.resources" -}} + {{- if .Values.injector.resources -}} + resources: +{{ toYaml .Values.injector.resources | indent 12}} + {{ end }} +{{- end -}} + +{{/* +Sets the container resources if the user has set any. +*/}} +{{- define "csi.resources" -}} + {{- if .Values.csi.resources -}} + resources: +{{ toYaml .Values.csi.resources | indent 12}} + {{ end }} +{{- end -}} + +{{/* +Sets the container resources for CSI's Agent sidecar if the user has set any. +*/}} +{{- define "csi.agent.resources" -}} + {{- if .Values.csi.agent.resources -}} + resources: +{{ toYaml .Values.csi.agent.resources | indent 12}} + {{ end }} +{{- end -}} + +{{/* +Sets extra CSI daemonset annotations +*/}} +{{- define "csi.daemonSet.annotations" -}} + {{- if .Values.csi.daemonSet.annotations }} + annotations: + {{- $tp := typeOf .Values.csi.daemonSet.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.csi.daemonSet.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.csi.daemonSet.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets CSI daemonset securityContext for pod template +*/}} +{{- define "csi.daemonSet.securityContext.pod" -}} + {{- if .Values.csi.daemonSet.securityContext.pod }} + securityContext: + {{- $tp := typeOf .Values.csi.daemonSet.securityContext.pod }} + {{- if eq $tp "string" }} + {{- tpl .Values.csi.daemonSet.securityContext.pod . | nindent 8 }} + {{- else }} + {{- toYaml .Values.csi.daemonSet.securityContext.pod | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets CSI daemonset securityContext for container +*/}} +{{- define "csi.daemonSet.securityContext.container" -}} + {{- if .Values.csi.daemonSet.securityContext.container }} + securityContext: + {{- $tp := typeOf .Values.csi.daemonSet.securityContext.container }} + {{- if eq $tp "string" }} + {{- tpl .Values.csi.daemonSet.securityContext.container . | nindent 12 }} + {{- else }} + {{- toYaml .Values.csi.daemonSet.securityContext.container | nindent 12 }} + {{- end }} + {{- end }} +{{- end -}} + + +{{/* +Sets the injector toleration for pod placement +*/}} +{{- define "csi.pod.tolerations" -}} + {{- if .Values.csi.pod.tolerations }} + tolerations: + {{- $tp := typeOf .Values.csi.pod.tolerations }} + {{- if eq $tp "string" }} + {{ tpl .Values.csi.pod.tolerations . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.csi.pod.tolerations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets the CSI provider nodeSelector for pod placement +*/}} +{{- define "csi.pod.nodeselector" -}} + {{- if .Values.csi.pod.nodeSelector }} + nodeSelector: + {{- $tp := typeOf .Values.csi.pod.nodeSelector }} + {{- if eq $tp "string" }} + {{ tpl .Values.csi.pod.nodeSelector . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.csi.pod.nodeSelector | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} +{{/* +Sets the CSI provider affinity for pod placement. +*/}} +{{- define "csi.pod.affinity" -}} + {{- if .Values.csi.pod.affinity }} + affinity: + {{ $tp := typeOf .Values.csi.pod.affinity }} + {{- if eq $tp "string" }} + {{- tpl .Values.csi.pod.affinity . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.csi.pod.affinity | nindent 8 }} + {{- end }} + {{ end }} +{{- end -}} +{{/* +Sets extra CSI provider pod annotations +*/}} +{{- define "csi.pod.annotations" -}} + {{- if .Values.csi.pod.annotations }} + annotations: + {{- $tp := typeOf .Values.csi.pod.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.csi.pod.annotations . | nindent 8 }} + {{- else }} + {{- toYaml .Values.csi.pod.annotations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra CSI service account annotations +*/}} +{{- define "csi.serviceAccount.annotations" -}} + {{- if .Values.csi.serviceAccount.annotations }} + annotations: + {{- $tp := typeOf .Values.csi.serviceAccount.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.csi.serviceAccount.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.csi.serviceAccount.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Inject extra environment vars in the format key:value, if populated +*/}} +{{- define "vault.extraEnvironmentVars" -}} +{{- if .extraEnvironmentVars -}} +{{- range $key, $value := .extraEnvironmentVars }} +- name: {{ printf "%s" $key | replace "." "_" | upper | quote }} + value: {{ $value | quote }} +{{- end }} +{{- end -}} +{{- end -}} + +{{/* +Inject extra environment populated by secrets, if populated +*/}} +{{- define "vault.extraSecretEnvironmentVars" -}} +{{- if .extraSecretEnvironmentVars -}} +{{- range .extraSecretEnvironmentVars }} +- name: {{ .envName }} + valueFrom: + secretKeyRef: + name: {{ .secretName }} + key: {{ .secretKey }} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* Scheme for health check and local endpoint */}} +{{- define "vault.scheme" -}} +{{- if .Values.global.tlsDisable -}} +{{ "http" }} +{{- else -}} +{{ "https" }} +{{- end -}} +{{- end -}} + +{{/* +imagePullSecrets generates pull secrets from either string or map values. +A map value must be indexable by the key 'name'. +*/}} +{{- define "imagePullSecrets" -}} +{{- with .Values.global.imagePullSecrets -}} +imagePullSecrets: +{{- range . -}} +{{- if typeIs "string" . }} + - name: {{ . }} +{{- else if index . "name" }} + - name: {{ .name }} +{{- end }} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +externalTrafficPolicy sets a Service's externalTrafficPolicy if applicable. +Supported inputs are Values.server.service and Values.ui +*/}} +{{- define "service.externalTrafficPolicy" -}} +{{- $type := "" -}} +{{- if .serviceType -}} +{{- $type = .serviceType -}} +{{- else if .type -}} +{{- $type = .type -}} +{{- end -}} +{{- if and .externalTrafficPolicy (or (eq $type "LoadBalancer") (eq $type "NodePort")) }} + externalTrafficPolicy: {{ .externalTrafficPolicy }} +{{- else }} +{{- end }} +{{- end -}} + +{{/* +loadBalancer configuration for the the UI service. +Supported inputs are Values.ui +*/}} +{{- define "service.loadBalancer" -}} +{{- if eq (.serviceType | toString) "LoadBalancer" }} +{{- if .loadBalancerIP }} + loadBalancerIP: {{ .loadBalancerIP }} +{{- end }} +{{- with .loadBalancerSourceRanges }} + loadBalancerSourceRanges: +{{- range . }} + - {{ . }} +{{- end }} +{{- end -}} +{{- end }} +{{- end -}} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/csi-agent-configmap.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/csi-agent-configmap.yaml new file mode 100644 index 00000000..7af08e8f --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/csi-agent-configmap.yaml @@ -0,0 +1,34 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.csiEnabled" . -}} +{{- if and (.csiEnabled) (eq (.Values.csi.agent.enabled | toString) "true") -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "vault.fullname" . }}-csi-provider-agent-config + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +data: + config.hcl: | + vault { + {{- if .Values.global.externalVaultAddr }} + "address" = "{{ .Values.global.externalVaultAddr }}" + {{- else }} + "address" = "{{ include "vault.scheme" . }}://{{ template "vault.fullname" . }}.{{ .Release.Namespace }}.svc:{{ .Values.server.service.port }}" + {{- end }} + } + + cache {} + + listener "unix" { + address = "/var/run/vault/agent.sock" + tls_disable = true + } +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/csi-clusterrole.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/csi-clusterrole.yaml new file mode 100644 index 00000000..6d979ea4 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/csi-clusterrole.yaml @@ -0,0 +1,23 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.csiEnabled" . -}} +{{- if .csiEnabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "vault.fullname" . }}-csi-provider-clusterrole + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: + - "" + resources: + - serviceaccounts/token + verbs: + - create +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/csi-clusterrolebinding.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/csi-clusterrolebinding.yaml new file mode 100644 index 00000000..d5a93468 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/csi-clusterrolebinding.yaml @@ -0,0 +1,24 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.csiEnabled" . -}} +{{- if .csiEnabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "vault.fullname" . }}-csi-provider-clusterrolebinding + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "vault.fullname" . }}-csi-provider-clusterrole +subjects: +- kind: ServiceAccount + name: {{ template "vault.fullname" . }}-csi-provider + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/csi-daemonset.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/csi-daemonset.yaml new file mode 100644 index 00000000..28e7cd07 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/csi-daemonset.yaml @@ -0,0 +1,157 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.csiEnabled" . -}} +{{- if .csiEnabled -}} +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: {{ template "vault.fullname" . }}-csi-provider + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- if .Values.csi.daemonSet.extraLabels -}} + {{- toYaml .Values.csi.daemonSet.extraLabels | nindent 4 -}} + {{- end -}} + {{ template "csi.daemonSet.annotations" . }} +spec: + updateStrategy: + type: {{ .Values.csi.daemonSet.updateStrategy.type }} + {{- if .Values.csi.daemonSet.updateStrategy.maxUnavailable }} + rollingUpdate: + maxUnavailable: {{ .Values.csi.daemonSet.updateStrategy.maxUnavailable }} + {{- end }} + selector: + matchLabels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + template: + metadata: + labels: + app.kubernetes.io/name: {{ template "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + {{- if .Values.csi.pod.extraLabels -}} + {{- toYaml .Values.csi.pod.extraLabels | nindent 8 -}} + {{- end -}} + {{ template "csi.pod.annotations" . }} + spec: + {{ template "csi.daemonSet.securityContext.pod" . }} + {{- if .Values.csi.priorityClassName }} + priorityClassName: {{ .Values.csi.priorityClassName }} + {{- end }} + serviceAccountName: {{ template "vault.fullname" . }}-csi-provider + {{- template "csi.pod.tolerations" . }} + {{- template "csi.pod.nodeselector" . }} + {{- template "csi.pod.affinity" . }} + containers: + - name: {{ include "vault.name" . }}-csi-provider + {{ template "csi.resources" . }} + {{ template "csi.daemonSet.securityContext.container" . }} + image: "{{ .Values.csi.image.repository }}:{{ .Values.csi.image.tag }}" + imagePullPolicy: {{ .Values.csi.image.pullPolicy }} + args: + - --endpoint=/provider/vault.sock + - --debug={{ .Values.csi.debug }} + {{- if .Values.csi.hmacSecretName }} + - --hmac-secret-name={{ .Values.csi.hmacSecretName }} + {{- else }} + - --hmac-secret-name={{- include "vault.name" . }}-csi-provider-hmac-key + {{- end }} + {{- if .Values.csi.extraArgs }} + {{- toYaml .Values.csi.extraArgs | nindent 12 }} + {{- end }} + env: + - name: VAULT_ADDR + {{- if eq (.Values.csi.agent.enabled | toString) "true" }} + value: "unix:///var/run/vault/agent.sock" + {{- else if .Values.global.externalVaultAddr }} + value: "{{ .Values.global.externalVaultAddr }}" + {{- else }} + value: {{ include "vault.scheme" . }}://{{ template "vault.fullname" . }}.{{ .Release.Namespace }}.svc:{{ .Values.server.service.port }} + {{- end }} + volumeMounts: + - name: providervol + mountPath: "/provider" + {{- if eq (.Values.csi.agent.enabled | toString) "true" }} + - name: agent-unix-socket + mountPath: /var/run/vault + {{- end }} + {{- if .Values.csi.volumeMounts }} + {{- toYaml .Values.csi.volumeMounts | nindent 12}} + {{- end }} + livenessProbe: + httpGet: + path: /health/ready + port: 8080 + failureThreshold: {{ .Values.csi.livenessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.csi.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.csi.livenessProbe.periodSeconds }} + successThreshold: {{ .Values.csi.livenessProbe.successThreshold }} + timeoutSeconds: {{ .Values.csi.livenessProbe.timeoutSeconds }} + readinessProbe: + httpGet: + path: /health/ready + port: 8080 + failureThreshold: {{ .Values.csi.readinessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.csi.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.csi.readinessProbe.periodSeconds }} + successThreshold: {{ .Values.csi.readinessProbe.successThreshold }} + timeoutSeconds: {{ .Values.csi.readinessProbe.timeoutSeconds }} + {{- if eq (.Values.csi.agent.enabled | toString) "true" }} + - name: {{ include "vault.name" . }}-agent + image: "{{ .Values.csi.agent.image.repository }}:{{ .Values.csi.agent.image.tag }}" + imagePullPolicy: {{ .Values.csi.agent.image.pullPolicy }} + {{ template "csi.agent.resources" . }} + command: + - vault + args: + - agent + - -config=/etc/vault/config.hcl + {{- if .Values.csi.agent.extraArgs }} + {{- toYaml .Values.csi.agent.extraArgs | nindent 12 }} + {{- end }} + ports: + - containerPort: 8200 + env: + - name: VAULT_LOG_LEVEL + value: "{{ .Values.csi.agent.logLevel }}" + - name: VAULT_LOG_FORMAT + value: "{{ .Values.csi.agent.logFormat }}" + securityContext: + runAsNonRoot: true + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsUser: 100 + runAsGroup: 1000 + volumeMounts: + - name: agent-config + mountPath: /etc/vault/config.hcl + subPath: config.hcl + readOnly: true + - name: agent-unix-socket + mountPath: /var/run/vault + {{- if .Values.csi.volumeMounts }} + {{- toYaml .Values.csi.volumeMounts | nindent 12 }} + {{- end }} + {{- end }} + volumes: + - name: providervol + hostPath: + path: {{ .Values.csi.daemonSet.providersDir }} + {{- if eq (.Values.csi.agent.enabled | toString) "true" }} + - name: agent-config + configMap: + name: {{ template "vault.fullname" . }}-csi-provider-agent-config + - name: agent-unix-socket + emptyDir: + medium: Memory + {{- end }} + {{- if .Values.csi.volumes }} + {{- toYaml .Values.csi.volumes | nindent 8}} + {{- end }} + {{- include "imagePullSecrets" . | nindent 6 }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/csi-role.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/csi-role.yaml new file mode 100644 index 00000000..dd23af65 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/csi-role.yaml @@ -0,0 +1,31 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.csiEnabled" . -}} +{{- if .csiEnabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "vault.fullname" . }}-csi-provider-role + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get"] + resourceNames: + {{- if .Values.csi.hmacSecretName }} + - {{ .Values.csi.hmacSecretName }} + {{- else }} + - {{ include "vault.name" . }}-csi-provider-hmac-key + {{- end }} +# 'create' permissions cannot be restricted by resource name: +# https://kubernetes.io/docs/reference/access-authn-authz/rbac/#referring-to-resources +- apiGroups: [""] + resources: ["secrets"] + verbs: ["create"] +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/csi-rolebinding.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/csi-rolebinding.yaml new file mode 100644 index 00000000..e61f2dc2 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/csi-rolebinding.yaml @@ -0,0 +1,24 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.csiEnabled" . -}} +{{- if .csiEnabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "vault.fullname" . }}-csi-provider-rolebinding + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "vault.fullname" . }}-csi-provider-role +subjects: +- kind: ServiceAccount + name: {{ template "vault.fullname" . }}-csi-provider + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/csi-serviceaccount.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/csi-serviceaccount.yaml new file mode 100644 index 00000000..25e123ee --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/csi-serviceaccount.yaml @@ -0,0 +1,21 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.csiEnabled" . -}} +{{- if .csiEnabled -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "vault.fullname" . }}-csi-provider + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- if .Values.csi.serviceAccount.extraLabels -}} + {{- toYaml .Values.csi.serviceAccount.extraLabels | nindent 4 -}} + {{- end -}} + {{ template "csi.serviceAccount.annotations" . }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-certs-secret.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-certs-secret.yaml new file mode 100644 index 00000000..3e5ddb7b --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-certs-secret.yaml @@ -0,0 +1,19 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if and (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} +apiVersion: v1 +kind: Secret +metadata: + name: vault-injector-certs + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-clusterrole.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-clusterrole.yaml new file mode 100644 index 00000000..d5682dd7 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-clusterrole.yaml @@ -0,0 +1,24 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-clusterrole + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: ["admissionregistration.k8s.io"] + resources: ["mutatingwebhookconfigurations"] + verbs: + - "get" + - "list" + - "watch" + - "patch" +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-clusterrolebinding.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-clusterrolebinding.yaml new file mode 100644 index 00000000..9253e4f0 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-clusterrolebinding.yaml @@ -0,0 +1,24 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-binding + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "vault.fullname" . }}-agent-injector-clusterrole +subjects: +- kind: ServiceAccount + name: {{ template "vault.fullname" . }}-agent-injector + namespace: {{ .Release.Namespace }} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-deployment.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-deployment.yaml new file mode 100644 index 00000000..fbf32c09 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-deployment.yaml @@ -0,0 +1,179 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +# Deployment for the injector +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "vault.fullname" . }}-agent-injector + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + component: webhook +spec: + replicas: {{ .Values.injector.replicas }} + selector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + component: webhook + {{ template "injector.strategy" . }} + template: + metadata: + labels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + component: webhook + {{- if .Values.injector.extraLabels -}} + {{- toYaml .Values.injector.extraLabels | nindent 8 -}} + {{- end -}} + {{ template "injector.annotations" . }} + spec: + {{ template "injector.affinity" . }} + {{ template "injector.topologySpreadConstraints" . }} + {{ template "injector.tolerations" . }} + {{ template "injector.nodeselector" . }} + {{- if .Values.injector.priorityClassName }} + priorityClassName: {{ .Values.injector.priorityClassName }} + {{- end }} + serviceAccountName: "{{ template "vault.fullname" . }}-agent-injector" + {{ template "injector.securityContext.pod" . -}} + {{- if not .Values.global.openshift }} + hostNetwork: {{ .Values.injector.hostNetwork }} + {{- end }} + containers: + - name: sidecar-injector + {{ template "injector.resources" . }} + image: "{{ .Values.injector.image.repository }}:{{ .Values.injector.image.tag }}" + imagePullPolicy: "{{ .Values.injector.image.pullPolicy }}" + {{- template "injector.securityContext.container" . }} + env: + - name: AGENT_INJECT_LISTEN + value: {{ printf ":%v" .Values.injector.port }} + - name: AGENT_INJECT_LOG_LEVEL + value: {{ .Values.injector.logLevel | default "info" }} + - name: AGENT_INJECT_VAULT_ADDR + {{- if .Values.global.externalVaultAddr }} + value: "{{ .Values.global.externalVaultAddr }}" + {{- else if .Values.injector.externalVaultAddr }} + value: "{{ .Values.injector.externalVaultAddr }}" + {{- else }} + value: {{ include "vault.scheme" . }}://{{ template "vault.fullname" . }}.{{ .Release.Namespace }}.svc:{{ .Values.server.service.port }} + {{- end }} + - name: AGENT_INJECT_VAULT_AUTH_PATH + value: {{ .Values.injector.authPath }} + - name: AGENT_INJECT_VAULT_IMAGE + value: "{{ .Values.injector.agentImage.repository }}:{{ .Values.injector.agentImage.tag }}" + {{- if .Values.injector.certs.secretName }} + - name: AGENT_INJECT_TLS_CERT_FILE + value: "/etc/webhook/certs/{{ .Values.injector.certs.certName }}" + - name: AGENT_INJECT_TLS_KEY_FILE + value: "/etc/webhook/certs/{{ .Values.injector.certs.keyName }}" + {{- else }} + - name: AGENT_INJECT_TLS_AUTO + value: {{ template "vault.fullname" . }}-agent-injector-cfg + - name: AGENT_INJECT_TLS_AUTO_HOSTS + value: {{ template "vault.fullname" . }}-agent-injector-svc,{{ template "vault.fullname" . }}-agent-injector-svc.{{ .Release.Namespace }},{{ template "vault.fullname" . }}-agent-injector-svc.{{ .Release.Namespace }}.svc + {{- end }} + - name: AGENT_INJECT_LOG_FORMAT + value: {{ .Values.injector.logFormat | default "standard" }} + - name: AGENT_INJECT_REVOKE_ON_SHUTDOWN + value: "{{ .Values.injector.revokeOnShutdown | default false }}" + {{- if .Values.global.openshift }} + - name: AGENT_INJECT_SET_SECURITY_CONTEXT + value: "false" + {{- end }} + {{- if .Values.injector.metrics.enabled }} + - name: AGENT_INJECT_TELEMETRY_PATH + value: "/metrics" + {{- end }} + {{- if and (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} + - name: AGENT_INJECT_USE_LEADER_ELECTOR + value: "true" + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- end }} + - name: AGENT_INJECT_CPU_REQUEST + value: "{{ .Values.injector.agentDefaults.cpuRequest }}" + - name: AGENT_INJECT_CPU_LIMIT + value: "{{ .Values.injector.agentDefaults.cpuLimit }}" + - name: AGENT_INJECT_MEM_REQUEST + value: "{{ .Values.injector.agentDefaults.memRequest }}" + - name: AGENT_INJECT_MEM_LIMIT + value: "{{ .Values.injector.agentDefaults.memLimit }}" + {{- if .Values.injector.agentDefaults.ephemeralRequest }} + - name: AGENT_INJECT_EPHEMERAL_REQUEST + value: "{{ .Values.injector.agentDefaults.ephemeralRequest }}" + {{- end }} + {{- if .Values.injector.agentDefaults.ephemeralLimit }} + - name: AGENT_INJECT_EPHEMERAL_LIMIT + value: "{{ .Values.injector.agentDefaults.ephemeralLimit }}" + {{- end }} + - name: AGENT_INJECT_DEFAULT_TEMPLATE + value: "{{ .Values.injector.agentDefaults.template }}" + - name: AGENT_INJECT_TEMPLATE_CONFIG_EXIT_ON_RETRY_FAILURE + value: "{{ .Values.injector.agentDefaults.templateConfig.exitOnRetryFailure }}" + {{- if .Values.injector.agentDefaults.templateConfig.staticSecretRenderInterval }} + - name: AGENT_INJECT_TEMPLATE_STATIC_SECRET_RENDER_INTERVAL + value: "{{ .Values.injector.agentDefaults.templateConfig.staticSecretRenderInterval }}" + {{- end }} + {{- include "vault.extraEnvironmentVars" .Values.injector | nindent 12 }} + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + args: + - agent-inject + - 2>&1 + livenessProbe: + httpGet: + path: /health/ready + port: {{ .Values.injector.port }} + scheme: HTTPS + failureThreshold: {{ .Values.injector.livenessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.injector.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.injector.livenessProbe.periodSeconds }} + successThreshold: {{ .Values.injector.livenessProbe.successThreshold }} + timeoutSeconds: {{ .Values.injector.livenessProbe.timeoutSeconds }} + readinessProbe: + httpGet: + path: /health/ready + port: {{ .Values.injector.port }} + scheme: HTTPS + failureThreshold: {{ .Values.injector.readinessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.injector.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.injector.readinessProbe.periodSeconds }} + successThreshold: {{ .Values.injector.readinessProbe.successThreshold }} + timeoutSeconds: {{ .Values.injector.readinessProbe.timeoutSeconds }} + startupProbe: + httpGet: + path: /health/ready + port: {{ .Values.injector.port }} + scheme: HTTPS + failureThreshold: {{ .Values.injector.startupProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.injector.startupProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.injector.startupProbe.periodSeconds }} + successThreshold: {{ .Values.injector.startupProbe.successThreshold }} + timeoutSeconds: {{ .Values.injector.startupProbe.timeoutSeconds }} +{{- if .Values.injector.certs.secretName }} + volumeMounts: + - name: webhook-certs + mountPath: /etc/webhook/certs + readOnly: true +{{- end }} +{{- if .Values.injector.certs.secretName }} + volumes: + - name: webhook-certs + secret: + secretName: "{{ .Values.injector.certs.secretName }}" +{{- end }} + {{- include "imagePullSecrets" . | nindent 6 }} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-disruptionbudget.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-disruptionbudget.yaml new file mode 100644 index 00000000..6ae714ba --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-disruptionbudget.yaml @@ -0,0 +1,25 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- if .Values.injector.podDisruptionBudget }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ template "vault.fullname" . }}-agent-injector + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + component: webhook +spec: + selector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + component: webhook + {{- toYaml .Values.injector.podDisruptionBudget | nindent 2 }} +{{- end -}} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-mutating-webhook.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-mutating-webhook.yaml new file mode 100644 index 00000000..d03cd136 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-mutating-webhook.yaml @@ -0,0 +1,44 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if .Capabilities.APIVersions.Has "admissionregistration.k8s.io/v1" }} +apiVersion: admissionregistration.k8s.io/v1 +{{- else }} +apiVersion: admissionregistration.k8s.io/v1beta1 +{{- end }} +kind: MutatingWebhookConfiguration +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-cfg + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- template "injector.webhookAnnotations" . }} +webhooks: + - name: vault.hashicorp.com + failurePolicy: {{ ((.Values.injector.webhook)).failurePolicy | default .Values.injector.failurePolicy }} + matchPolicy: {{ ((.Values.injector.webhook)).matchPolicy | default "Exact" }} + sideEffects: None + timeoutSeconds: {{ ((.Values.injector.webhook)).timeoutSeconds | default "30" }} + admissionReviewVersions: ["v1", "v1beta1"] + clientConfig: + service: + name: {{ template "vault.fullname" . }}-agent-injector-svc + namespace: {{ .Release.Namespace }} + path: "/mutate" + caBundle: {{ .Values.injector.certs.caBundle | quote }} + rules: + - operations: ["CREATE", "UPDATE"] + apiGroups: [""] + apiVersions: ["v1"] + resources: ["pods"] +{{- if or (.Values.injector.namespaceSelector) (((.Values.injector.webhook)).namespaceSelector) }} + namespaceSelector: +{{ toYaml (((.Values.injector.webhook)).namespaceSelector | default .Values.injector.namespaceSelector) | indent 6}} +{{ end }} +{{- template "injector.objectSelector" . -}} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-network-policy.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-network-policy.yaml new file mode 100644 index 00000000..4c3b0878 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-network-policy.yaml @@ -0,0 +1,29 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if eq (.Values.global.openshift | toString) "true" }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ template "vault.fullname" . }}-agent-injector + labels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + component: webhook + ingress: + - from: + - namespaceSelector: {} + ports: + - port: 8080 + protocol: TCP +{{ end }} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-psp-role.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-psp-role.yaml new file mode 100644 index 00000000..65d8e9ba --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-psp-role.yaml @@ -0,0 +1,25 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if eq (.Values.global.psp.enable | toString) "true" }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-psp + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: ['policy'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + - {{ template "vault.fullname" . }}-agent-injector +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-psp-rolebinding.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-psp-rolebinding.yaml new file mode 100644 index 00000000..48a3a26a --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-psp-rolebinding.yaml @@ -0,0 +1,26 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if eq (.Values.global.psp.enable | toString) "true" }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-psp + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + kind: Role + name: {{ template "vault.fullname" . }}-agent-injector-psp + apiGroup: rbac.authorization.k8s.io +subjects: + - kind: ServiceAccount + name: {{ template "vault.fullname" . }}-agent-injector +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-psp.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-psp.yaml new file mode 100644 index 00000000..0eca9a87 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-psp.yaml @@ -0,0 +1,51 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if eq (.Values.global.psp.enable | toString) "true" }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "vault.fullname" . }}-agent-injector + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- template "vault.psp.annotations" . }} +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + volumes: + - configMap + - emptyDir + - projected + - secret + - downwardAPI + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + # Require the container to run without root privileges. + rule: MustRunAsNonRoot + seLinux: + # This policy assumes the nodes are using AppArmor rather than SELinux. + rule: RunAsAny + supplementalGroups: + rule: MustRunAs + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + fsGroup: + rule: MustRunAs + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + readOnlyRootFilesystem: false +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-role.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-role.yaml new file mode 100644 index 00000000..df7b0ed7 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-role.yaml @@ -0,0 +1,34 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if and (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-leader-elector-role + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: + - apiGroups: [""] + resources: ["secrets", "configmaps"] + verbs: + - "create" + - "get" + - "watch" + - "list" + - "update" + - apiGroups: [""] + resources: ["pods"] + verbs: + - "get" + - "patch" + - "delete" +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-rolebinding.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-rolebinding.yaml new file mode 100644 index 00000000..0848e43d --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-rolebinding.yaml @@ -0,0 +1,27 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if and (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-leader-elector-binding + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "vault.fullname" . }}-agent-injector-leader-elector-role +subjects: + - kind: ServiceAccount + name: {{ template "vault.fullname" . }}-agent-injector + namespace: {{ .Release.Namespace }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-service.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-service.yaml new file mode 100644 index 00000000..5b206928 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-service.yaml @@ -0,0 +1,27 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-svc + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{ template "injector.service.annotations" . }} +spec: + ports: + - name: https + port: 443 + targetPort: {{ .Values.injector.port }} + selector: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + component: webhook +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-serviceaccount.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-serviceaccount.yaml new file mode 100644 index 00000000..9b5c2f6e --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/injector-serviceaccount.yaml @@ -0,0 +1,18 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "vault.fullname" . }}-agent-injector + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{ template "injector.serviceAccount.annotations" . }} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/prometheus-prometheusrules.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/prometheus-prometheusrules.yaml new file mode 100644 index 00000000..7e58a0e5 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/prometheus-prometheusrules.yaml @@ -0,0 +1,31 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ if and (.Values.serverTelemetry.prometheusRules.rules) + (or (.Values.global.serverTelemetry.prometheusOperator) (.Values.serverTelemetry.prometheusRules.enabled) ) +}} +--- +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ template "vault.fullname" . }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- /* update the selectors docs in values.yaml whenever the defaults below change. */ -}} + {{- $selectors := .Values.serverTelemetry.prometheusRules.selectors }} + {{- if $selectors }} + {{- toYaml $selectors | nindent 4 }} + {{- else }} + release: prometheus + {{- end }} +spec: + groups: + - name: {{ include "vault.fullname" . }} + rules: + {{- toYaml .Values.serverTelemetry.prometheusRules.rules | nindent 6 }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/prometheus-servicemonitor.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/prometheus-servicemonitor.yaml new file mode 100644 index 00000000..60f2729a --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/prometheus-servicemonitor.yaml @@ -0,0 +1,49 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{ if or (.Values.global.serverTelemetry.prometheusOperator) (.Values.serverTelemetry.serviceMonitor.enabled) }} +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ template "vault.fullname" . }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- /* update the selectors docs in values.yaml whenever the defaults below change. */ -}} + {{- $selectors := .Values.serverTelemetry.serviceMonitor.selectors }} + {{- if $selectors }} + {{- toYaml $selectors | nindent 4 }} + {{- else }} + release: prometheus + {{- end }} +spec: + selector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + {{- if eq .mode "ha" }} + vault-active: "true" + {{- else }} + vault-internal: "true" + {{- end }} + endpoints: + - port: {{ include "vault.scheme" . }} + interval: {{ .Values.serverTelemetry.serviceMonitor.interval }} + scrapeTimeout: {{ .Values.serverTelemetry.serviceMonitor.scrapeTimeout }} + scheme: {{ include "vault.scheme" . | lower }} + path: /v1/sys/metrics + params: + format: + - prometheus + tlsConfig: + insecureSkipVerify: true + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/server-clusterrolebinding.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-clusterrolebinding.yaml new file mode 100644 index 00000000..b694129b --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-clusterrolebinding.yaml @@ -0,0 +1,29 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.serverAuthDelegator" . }} +{{- if .serverAuthDelegator -}} +{{- if .Capabilities.APIVersions.Has "rbac.authorization.k8s.io/v1" -}} +apiVersion: rbac.authorization.k8s.io/v1 +{{- else }} +apiVersion: rbac.authorization.k8s.io/v1beta1 +{{- end }} +kind: ClusterRoleBinding +metadata: + name: {{ template "vault.fullname" . }}-server-binding + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: +- kind: ServiceAccount + name: {{ template "vault.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{ end }} \ No newline at end of file diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/server-config-configmap.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-config-configmap.yaml new file mode 100644 index 00000000..5d29e98d --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-config-configmap.yaml @@ -0,0 +1,45 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if .serverEnabled -}} +{{- if ne .mode "dev" -}} +{{ if or (.Values.server.standalone.config) (.Values.server.ha.config) -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "vault.fullname" . }}-config + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +data: + extraconfig-from-values.hcl: |- + {{- if or (eq .mode "ha") (eq .mode "standalone") }} + {{- $type := typeOf (index .Values.server .mode).config }} + {{- if eq $type "string" }} + disable_mlock = true + {{- if eq .mode "standalone" }} + {{ tpl .Values.server.standalone.config . | nindent 4 | trim }} + {{- else if and (eq .mode "ha") (eq (.Values.server.ha.raft.enabled | toString) "false") }} + {{ tpl .Values.server.ha.config . | nindent 4 | trim }} + {{- else if and (eq .mode "ha") (eq (.Values.server.ha.raft.enabled | toString) "true") }} + {{ tpl .Values.server.ha.raft.config . | nindent 4 | trim }} + {{ end }} + {{- else }} + {{- if and (eq .mode "ha") (eq (.Values.server.ha.raft.enabled | toString) "true") }} +{{ merge (dict "disable_mlock" true) (index .Values.server .mode).raft.config | toPrettyJson | indent 4 }} + {{- else }} +{{ merge (dict "disable_mlock" true) (index .Values.server .mode).config | toPrettyJson | indent 4 }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/server-discovery-role.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-discovery-role.yaml new file mode 100644 index 00000000..adae42a2 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-discovery-role.yaml @@ -0,0 +1,26 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if .serverEnabled -}} +{{- if eq .mode "ha" }} +{{- if eq (.Values.server.serviceAccount.serviceDiscovery.enabled | toString) "true" }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + namespace: {{ .Release.Namespace }} + name: {{ template "vault.fullname" . }}-discovery-role + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: [""] + resources: ["pods"] + verbs: ["get", "watch", "list", "update", "patch"] +{{ end }} +{{ end }} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/server-discovery-rolebinding.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-discovery-rolebinding.yaml new file mode 100644 index 00000000..853ee870 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-discovery-rolebinding.yaml @@ -0,0 +1,34 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if .serverEnabled -}} +{{- if eq .mode "ha" }} +{{- if eq (.Values.server.serviceAccount.serviceDiscovery.enabled | toString) "true" }} +{{- if .Capabilities.APIVersions.Has "rbac.authorization.k8s.io/v1" -}} +apiVersion: rbac.authorization.k8s.io/v1 +{{- else }} +apiVersion: rbac.authorization.k8s.io/v1beta1 +{{- end }} +kind: RoleBinding +metadata: + name: {{ template "vault.fullname" . }}-discovery-rolebinding + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "vault.fullname" . }}-discovery-role +subjects: +- kind: ServiceAccount + name: {{ template "vault.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{ end }} +{{ end }} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/server-disruptionbudget.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-disruptionbudget.yaml new file mode 100644 index 00000000..3ff11099 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-disruptionbudget.yaml @@ -0,0 +1,31 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if ne .mode "external" -}} +{{- if .serverEnabled -}} +{{- if and (eq .mode "ha") (eq (.Values.server.ha.disruptionBudget.enabled | toString) "true") -}} +# PodDisruptionBudget to prevent degrading the server cluster through +# voluntary cluster changes. +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ template "vault.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +spec: + maxUnavailable: {{ template "vault.pdb.maxUnavailable" . }} + selector: + matchLabels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/server-ha-active-service.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-ha-active-service.yaml new file mode 100644 index 00000000..58d540fd --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-ha-active-service.yaml @@ -0,0 +1,55 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- template "vault.serverServiceEnabled" . -}} +{{- if .serverServiceEnabled -}} +{{- if eq .mode "ha" }} +{{- if eq (.Values.server.service.active.enabled | toString) "true" }} +# Service for active Vault pod +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }}-active + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + vault-active: "true" + annotations: +{{ template "vault.service.annotations" .}} +spec: + {{- if .Values.server.service.type}} + type: {{ .Values.server.service.type }} + {{- end}} + {{- if .Values.server.service.clusterIP }} + clusterIP: {{ .Values.server.service.clusterIP }} + {{- end }} + {{- include "service.externalTrafficPolicy" .Values.server.service }} + publishNotReadyAddresses: {{ .Values.server.service.publishNotReadyAddresses }} + ports: + - name: {{ include "vault.scheme" . }} + port: {{ .Values.server.service.port }} + targetPort: {{ .Values.server.service.targetPort }} + {{- if and (.Values.server.service.activeNodePort) (eq (.Values.server.service.type | toString) "NodePort") }} + nodePort: {{ .Values.server.service.activeNodePort }} + {{- end }} + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: {{ include "vault.name" . }} + {{- if eq (.Values.server.service.instanceSelector.enabled | toString) "true" }} + app.kubernetes.io/instance: {{ .Release.Name }} + {{- end }} + component: server + vault-active: "true" +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/server-ha-standby-service.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-ha-standby-service.yaml new file mode 100644 index 00000000..b9f64358 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-ha-standby-service.yaml @@ -0,0 +1,54 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- template "vault.serverServiceEnabled" . -}} +{{- if .serverServiceEnabled -}} +{{- if eq .mode "ha" }} +{{- if eq (.Values.server.service.standby.enabled | toString) "true" }} +# Service for standby Vault pod +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }}-standby + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + annotations: +{{ template "vault.service.annotations" .}} +spec: + {{- if .Values.server.service.type}} + type: {{ .Values.server.service.type }} + {{- end}} + {{- if .Values.server.service.clusterIP }} + clusterIP: {{ .Values.server.service.clusterIP }} + {{- end }} + {{- include "service.externalTrafficPolicy" .Values.server.service }} + publishNotReadyAddresses: {{ .Values.server.service.publishNotReadyAddresses }} + ports: + - name: {{ include "vault.scheme" . }} + port: {{ .Values.server.service.port }} + targetPort: {{ .Values.server.service.targetPort }} + {{- if and (.Values.server.service.standbyNodePort) (eq (.Values.server.service.type | toString) "NodePort") }} + nodePort: {{ .Values.server.service.standbyNodePort }} + {{- end }} + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: {{ include "vault.name" . }} + {{- if eq (.Values.server.service.instanceSelector.enabled | toString) "true" }} + app.kubernetes.io/instance: {{ .Release.Name }} + {{- end }} + component: server + vault-active: "false" +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/server-headless-service.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-headless-service.yaml new file mode 100644 index 00000000..42e1aa00 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-headless-service.yaml @@ -0,0 +1,39 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- template "vault.serverServiceEnabled" . -}} +{{- if .serverServiceEnabled -}} +# Service for Vault cluster +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }}-internal + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + vault-internal: "true" + annotations: +{{ template "vault.service.annotations" .}} +spec: + clusterIP: None + publishNotReadyAddresses: true + ports: + - name: "{{ include "vault.scheme" . }}" + port: {{ .Values.server.service.port }} + targetPort: {{ .Values.server.service.targetPort }} + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/server-ingress.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-ingress.yaml new file mode 100644 index 00000000..3aba6688 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-ingress.yaml @@ -0,0 +1,69 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- if not .Values.global.openshift }} +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if .Values.server.ingress.enabled -}} +{{- $extraPaths := .Values.server.ingress.extraPaths -}} +{{- $serviceName := include "vault.fullname" . -}} +{{- template "vault.serverServiceEnabled" . -}} +{{- if .serverServiceEnabled -}} +{{- if and (eq .mode "ha" ) (eq (.Values.server.ingress.activeService | toString) "true") }} +{{- $serviceName = printf "%s-%s" $serviceName "active" -}} +{{- end }} +{{- $servicePort := .Values.server.service.port -}} +{{- $pathType := .Values.server.ingress.pathType -}} +{{- $kubeVersion := .Capabilities.KubeVersion.Version }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ template "vault.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- with .Values.server.ingress.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- template "vault.ingress.annotations" . }} +spec: +{{- if .Values.server.ingress.tls }} + tls: + {{- range .Values.server.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} +{{- if .Values.server.ingress.ingressClassName }} + ingressClassName: {{ .Values.server.ingress.ingressClassName }} +{{- end }} + rules: + {{- range .Values.server.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: +{{ if $extraPaths }} +{{ toYaml $extraPaths | indent 10 }} +{{- end }} + {{- range (.paths | default (list "/")) }} + - path: {{ . }} + pathType: {{ $pathType }} + backend: + service: + name: {{ $serviceName }} + port: + number: {{ $servicePort }} + {{- end }} + {{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/server-network-policy.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-network-policy.yaml new file mode 100644 index 00000000..62d4ae1a --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-network-policy.yaml @@ -0,0 +1,31 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- if eq (.Values.server.networkPolicy.enabled | toString) "true" }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ template "vault.fullname" . }} + labels: + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + ingress: + - from: + - namespaceSelector: {} + ports: + - port: 8200 + protocol: TCP + - port: 8201 + protocol: TCP + {{- if .Values.server.networkPolicy.egress }} + egress: + {{- toYaml .Values.server.networkPolicy.egress | nindent 4 }} + {{ end }} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/server-psp-role.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-psp-role.yaml new file mode 100644 index 00000000..0c8c983e --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-psp-role.yaml @@ -0,0 +1,25 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if .serverEnabled -}} +{{- if and (ne .mode "") (eq (.Values.global.psp.enable | toString) "true") }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "vault.fullname" . }}-psp + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: ['policy'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + - {{ template "vault.fullname" . }} +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/server-psp-rolebinding.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-psp-rolebinding.yaml new file mode 100644 index 00000000..9b975d55 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-psp-rolebinding.yaml @@ -0,0 +1,26 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if .serverEnabled -}} +{{- if and (ne .mode "") (eq (.Values.global.psp.enable | toString) "true") }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "vault.fullname" . }}-psp + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + kind: Role + name: {{ template "vault.fullname" . }}-psp + apiGroup: rbac.authorization.k8s.io +subjects: + - kind: ServiceAccount + name: {{ template "vault.fullname" . }} +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/server-psp.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-psp.yaml new file mode 100644 index 00000000..567e6624 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-psp.yaml @@ -0,0 +1,54 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if .serverEnabled -}} +{{- if and (ne .mode "") (eq (.Values.global.psp.enable | toString) "true") }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "vault.fullname" . }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- template "vault.psp.annotations" . }} +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + volumes: + - configMap + - emptyDir + - projected + - secret + - downwardAPI + {{- if eq (.Values.server.dataStorage.enabled | toString) "true" }} + - persistentVolumeClaim + {{- end }} + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + # Require the container to run without root privileges. + rule: MustRunAsNonRoot + seLinux: + # This policy assumes the nodes are using AppArmor rather than SELinux. + rule: RunAsAny + supplementalGroups: + rule: MustRunAs + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + fsGroup: + rule: MustRunAs + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + readOnlyRootFilesystem: false +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/server-route.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-route.yaml new file mode 100644 index 00000000..3f35aefe --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-route.yaml @@ -0,0 +1,39 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{- if .Values.global.openshift }} +{{- if ne .mode "external" }} +{{- if .Values.server.route.enabled -}} +{{- $serviceName := include "vault.fullname" . -}} +{{- if and (eq .mode "ha" ) (eq (.Values.server.route.activeService | toString) "true") }} +{{- $serviceName = printf "%s-%s" $serviceName "active" -}} +{{- end }} +kind: Route +apiVersion: route.openshift.io/v1 +metadata: + name: {{ template "vault.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- with .Values.server.route.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- template "vault.route.annotations" . }} +spec: + host: {{ .Values.server.route.host }} + to: + kind: Service + name: {{ $serviceName }} + weight: 100 + port: + targetPort: 8200 + tls: + {{- toYaml .Values.server.route.tls | nindent 4 }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/server-service.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-service.yaml new file mode 100644 index 00000000..8e34c88c --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-service.yaml @@ -0,0 +1,51 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- template "vault.serverServiceEnabled" . -}} +{{- if .serverServiceEnabled -}} +# Service for Vault cluster +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + annotations: +{{ template "vault.service.annotations" .}} +spec: + {{- if .Values.server.service.type}} + type: {{ .Values.server.service.type }} + {{- end}} + {{- if .Values.server.service.clusterIP }} + clusterIP: {{ .Values.server.service.clusterIP }} + {{- end }} + {{- include "service.externalTrafficPolicy" .Values.server.service }} + # We want the servers to become available even if they're not ready + # since this DNS is also used for join operations. + publishNotReadyAddresses: {{ .Values.server.service.publishNotReadyAddresses }} + ports: + - name: {{ include "vault.scheme" . }} + port: {{ .Values.server.service.port }} + targetPort: {{ .Values.server.service.targetPort }} + {{- if and (.Values.server.service.nodePort) (eq (.Values.server.service.type | toString) "NodePort") }} + nodePort: {{ .Values.server.service.nodePort }} + {{- end }} + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: {{ include "vault.name" . }} + {{- if eq (.Values.server.service.instanceSelector.enabled | toString) "true" }} + app.kubernetes.io/instance: {{ .Release.Name }} + {{- end }} + component: server +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/server-serviceaccount.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-serviceaccount.yaml new file mode 100644 index 00000000..e154f8dc --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-serviceaccount.yaml @@ -0,0 +1,22 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.serverServiceAccountEnabled" . }} +{{- if .serverServiceAccountEnabled -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "vault.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- if .Values.server.serviceAccount.extraLabels -}} + {{- toYaml .Values.server.serviceAccount.extraLabels | nindent 4 -}} + {{- end -}} + {{ template "vault.serviceAccount.annotations" . }} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/server-statefulset.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-statefulset.yaml new file mode 100644 index 00000000..7ab7de8e --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/server-statefulset.yaml @@ -0,0 +1,217 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if ne .mode "" }} +{{- if .serverEnabled -}} +# StatefulSet to run the actual vault server cluster. +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ template "vault.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- template "vault.statefulSet.annotations" . }} +spec: + serviceName: {{ template "vault.fullname" . }}-internal + podManagementPolicy: Parallel + replicas: {{ template "vault.replicas" . }} + updateStrategy: + type: {{ .Values.server.updateStrategyType }} + selector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server + template: + metadata: + labels: + helm.sh/chart: {{ template "vault.chart" . }} + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server + {{- if .Values.server.extraLabels -}} + {{- toYaml .Values.server.extraLabels | nindent 8 -}} + {{- end -}} + {{ template "vault.annotations" . }} + spec: + {{ template "vault.affinity" . }} + {{ template "vault.topologySpreadConstraints" . }} + {{ template "vault.tolerations" . }} + {{ template "vault.nodeselector" . }} + {{- if .Values.server.priorityClassName }} + priorityClassName: {{ .Values.server.priorityClassName }} + {{- end }} + terminationGracePeriodSeconds: {{ .Values.server.terminationGracePeriodSeconds }} + serviceAccountName: {{ template "vault.serviceAccount.name" . }} + {{ if .Values.server.shareProcessNamespace }} + shareProcessNamespace: true + {{ end }} + {{- template "server.statefulSet.securityContext.pod" . }} + {{- if not .Values.global.openshift }} + hostNetwork: {{ .Values.server.hostNetwork }} + {{- end }} + volumes: + {{ template "vault.volumes" . }} + - name: home + emptyDir: {} + {{- if .Values.server.extraInitContainers }} + initContainers: + {{ toYaml .Values.server.extraInitContainers | nindent 8}} + {{- end }} + containers: + - name: vault + {{ template "vault.resources" . }} + image: {{ .Values.server.image.repository }}:{{ .Values.server.image.tag | default "latest" }} + imagePullPolicy: {{ .Values.server.image.pullPolicy }} + command: + - "/bin/sh" + - "-ec" + args: {{ template "vault.args" . }} + {{- template "server.statefulSet.securityContext.container" . }} + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: VAULT_K8S_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: VAULT_K8S_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: VAULT_ADDR + value: "{{ include "vault.scheme" . }}://127.0.0.1:8200" + - name: VAULT_API_ADDR + {{- if .Values.server.ha.apiAddr }} + value: {{ .Values.server.ha.apiAddr }} + {{- else }} + value: "{{ include "vault.scheme" . }}://$(POD_IP):8200" + {{- end }} + - name: SKIP_CHOWN + value: "true" + - name: SKIP_SETCAP + value: "true" + - name: HOSTNAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: VAULT_CLUSTER_ADDR + {{- if .Values.server.ha.clusterAddr }} + value: {{ .Values.server.ha.clusterAddr | quote }} + {{- else }} + value: "https://$(HOSTNAME).{{ template "vault.fullname" . }}-internal:8201" + {{- end }} + {{- if and (eq (.Values.server.ha.raft.enabled | toString) "true") (eq (.Values.server.ha.raft.setNodeId | toString) "true") }} + - name: VAULT_RAFT_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- end }} + - name: HOME + value: "/home/vault" + {{- if .Values.server.logLevel }} + - name: VAULT_LOG_LEVEL + value: "{{ .Values.server.logLevel }}" + {{- end }} + {{- if .Values.server.logFormat }} + - name: VAULT_LOG_FORMAT + value: "{{ .Values.server.logFormat }}" + {{- end }} + {{- if (and .Values.server.enterpriseLicense.secretName .Values.server.enterpriseLicense.secretKey) }} + - name: VAULT_LICENSE_PATH + value: /vault/license/{{ .Values.server.enterpriseLicense.secretKey }} + {{- end }} + {{ template "vault.envs" . }} + {{- include "vault.extraEnvironmentVars" .Values.server | nindent 12 }} + {{- include "vault.extraSecretEnvironmentVars" .Values.server | nindent 12 }} + volumeMounts: + {{ template "vault.mounts" . }} + - name: home + mountPath: /home/vault + ports: + - containerPort: 8200 + name: {{ include "vault.scheme" . }} + - containerPort: 8201 + name: https-internal + - containerPort: 8202 + name: {{ include "vault.scheme" . }}-rep + {{- if .Values.server.extraPorts -}} + {{ toYaml .Values.server.extraPorts | nindent 12}} + {{- end }} + {{- if .Values.server.readinessProbe.enabled }} + readinessProbe: + {{- if .Values.server.readinessProbe.path }} + httpGet: + path: {{ .Values.server.readinessProbe.path | quote }} + port: {{ .Values.server.readinessProbe.port }} + scheme: {{ include "vault.scheme" . | upper }} + {{- else }} + # Check status; unsealed vault servers return 0 + # The exit code reflects the seal status: + # 0 - unsealed + # 1 - error + # 2 - sealed + exec: + command: ["/bin/sh", "-ec", "vault status -tls-skip-verify"] + {{- end }} + failureThreshold: {{ .Values.server.readinessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.server.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.server.readinessProbe.periodSeconds }} + successThreshold: {{ .Values.server.readinessProbe.successThreshold }} + timeoutSeconds: {{ .Values.server.readinessProbe.timeoutSeconds }} + {{- end }} + {{- if .Values.server.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: {{ .Values.server.livenessProbe.path | quote }} + port: {{ .Values.server.livenessProbe.port }} + scheme: {{ include "vault.scheme" . | upper }} + failureThreshold: {{ .Values.server.livenessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.server.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.server.livenessProbe.periodSeconds }} + successThreshold: {{ .Values.server.livenessProbe.successThreshold }} + timeoutSeconds: {{ .Values.server.livenessProbe.timeoutSeconds }} + {{- end }} + lifecycle: + # Vault container doesn't receive SIGTERM from Kubernetes + # and after the grace period ends, Kube sends SIGKILL. This + # causes issues with graceful shutdowns such as deregistering itself + # from Consul (zombie services). + preStop: + exec: + command: [ + "/bin/sh", "-c", + # Adding a sleep here to give the pod eviction a + # chance to propagate, so requests will not be made + # to this pod while it's terminating + "sleep {{ .Values.server.preStopSleepSeconds }} && kill -SIGTERM $(pidof vault)", + ] + {{- if .Values.server.postStart }} + postStart: + exec: + command: + {{- range (.Values.server.postStart) }} + - {{ . | quote }} + {{- end }} + {{- end }} + {{- if .Values.server.extraContainers }} + {{ toYaml .Values.server.extraContainers | nindent 8}} + {{- end }} + {{- include "imagePullSecrets" . | nindent 6 }} + {{ template "vault.volumeclaims" . }} +{{ end }} +{{ end }} +{{ end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/tests/server-test.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/tests/server-test.yaml new file mode 100644 index 00000000..59b15010 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/tests/server-test.yaml @@ -0,0 +1,56 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if .serverEnabled -}} +apiVersion: v1 +kind: Pod +metadata: + name: "{{ .Release.Name }}-server-test" + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": test +spec: + {{- include "imagePullSecrets" . | nindent 2 }} + containers: + - name: {{ .Release.Name }}-server-test + image: {{ .Values.server.image.repository }}:{{ .Values.server.image.tag | default "latest" }} + imagePullPolicy: {{ .Values.server.image.pullPolicy }} + env: + - name: VAULT_ADDR + value: {{ include "vault.scheme" . }}://{{ template "vault.fullname" . }}.{{ .Release.Namespace }}.svc:{{ .Values.server.service.port }} + {{- include "vault.extraEnvironmentVars" .Values.server | nindent 8 }} + command: + - /bin/sh + - -c + - | + echo "Checking for sealed info in 'vault status' output" + ATTEMPTS=10 + n=0 + until [ "$n" -ge $ATTEMPTS ] + do + echo "Attempt" $n... + vault status -format yaml | grep -E '^sealed: (true|false)' && break + n=$((n+1)) + sleep 5 + done + if [ $n -ge $ATTEMPTS ]; then + echo "timed out looking for sealed info in 'vault status' output" + exit 1 + fi + + exit 0 + volumeMounts: + {{- if .Values.server.volumeMounts }} + {{- toYaml .Values.server.volumeMounts | nindent 8}} + {{- end }} + volumes: + {{- if .Values.server.volumes }} + {{- toYaml .Values.server.volumes | nindent 4}} + {{- end }} + restartPolicy: Never +{{- end }} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/templates/ui-service.yaml b/charts/partners/hashicorp/vault/0.25.0/src/templates/ui-service.yaml new file mode 100644 index 00000000..4b2e8f7e --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/templates/ui-service.yaml @@ -0,0 +1,42 @@ +{{/* +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 +*/}} + +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- template "vault.uiEnabled" . -}} +{{- if .uiEnabled -}} + +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }}-ui + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }}-ui + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- template "vault.ui.annotations" . }} +spec: + selector: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server + {{- if and (.Values.ui.activeVaultPodOnly) (eq .mode "ha") }} + vault-active: "true" + {{- end }} + publishNotReadyAddresses: {{ .Values.ui.publishNotReadyAddresses }} + ports: + - name: {{ include "vault.scheme" . }} + port: {{ .Values.ui.externalPort }} + targetPort: {{ .Values.ui.targetPort }} + {{- if .Values.ui.serviceNodePort }} + nodePort: {{ .Values.ui.serviceNodePort }} + {{- end }} + type: {{ .Values.ui.serviceType }} + {{- include "service.externalTrafficPolicy" .Values.ui }} + {{- include "service.loadBalancer" .Values.ui }} +{{- end -}} +{{- end }} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/values.openshift.yaml b/charts/partners/hashicorp/vault/0.25.0/src/values.openshift.yaml new file mode 100644 index 00000000..6e575e4d --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/values.openshift.yaml @@ -0,0 +1,21 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +# These overrides are appropriate defaults for deploying this chart on OpenShift + +global: + openshift: true + +injector: + image: + repository: "registry.connect.redhat.com/hashicorp/vault-k8s" + tag: "1.2.1-ubi" + + agentImage: + repository: "registry.connect.redhat.com/hashicorp/vault" + tag: "1.14.0-ubi" + +server: + image: + repository: "registry.connect.redhat.com/hashicorp/vault" + tag: "1.14.0-ubi" diff --git a/charts/partners/hashicorp/vault/0.25.0/src/values.schema.json b/charts/partners/hashicorp/vault/0.25.0/src/values.schema.json new file mode 100644 index 00000000..ecb97dec --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/values.schema.json @@ -0,0 +1,1144 @@ +{ + "$schema": "http://json-schema.org/schema#", + "type": "object", + "properties": { + "csi": { + "type": "object", + "properties": { + "agent": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "extraArgs": { + "type": "array" + }, + "image": { + "type": "object", + "properties": { + "pullPolicy": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "logFormat": { + "type": "string" + }, + "logLevel": { + "type": "string" + }, + "resources": { + "type": "object" + } + } + }, + "daemonSet": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "extraLabels": { + "type": "object" + }, + "kubeletRootDir": { + "type": "string" + }, + "providersDir": { + "type": "string" + }, + "securityContext": { + "type": "object", + "properties": { + "container": { + "type": [ + "object", + "string" + ] + }, + "pod": { + "type": [ + "object", + "string" + ] + } + } + }, + "updateStrategy": { + "type": "object", + "properties": { + "maxUnavailable": { + "type": "string" + }, + "type": { + "type": "string" + } + } + } + } + }, + "debug": { + "type": "boolean" + }, + "enabled": { + "type": [ + "boolean", + "string" + ] + }, + "extraArgs": { + "type": "array" + }, + "image": { + "type": "object", + "properties": { + "pullPolicy": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "livenessProbe": { + "type": "object", + "properties": { + "failureThreshold": { + "type": "integer" + }, + "initialDelaySeconds": { + "type": "integer" + }, + "periodSeconds": { + "type": "integer" + }, + "successThreshold": { + "type": "integer" + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "pod": { + "type": "object", + "properties": { + "affinity": { + "type": [ + "null", + "object", + "string" + ] + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "extraLabels": { + "type": "object" + }, + "nodeSelector": { + "type": [ + "null", + "object", + "string" + ] + }, + "tolerations": { + "type": [ + "null", + "array", + "string" + ] + } + } + }, + "priorityClassName": { + "type": "string" + }, + "readinessProbe": { + "type": "object", + "properties": { + "failureThreshold": { + "type": "integer" + }, + "initialDelaySeconds": { + "type": "integer" + }, + "periodSeconds": { + "type": "integer" + }, + "successThreshold": { + "type": "integer" + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "resources": { + "type": "object" + }, + "serviceAccount": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "extraLabels": { + "type": "object" + } + } + }, + "volumeMounts": { + "type": [ + "null", + "array" + ] + }, + "volumes": { + "type": [ + "null", + "array" + ] + } + } + }, + "global": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "externalVaultAddr": { + "type": "string" + }, + "imagePullSecrets": { + "type": "array" + }, + "openshift": { + "type": "boolean" + }, + "psp": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enable": { + "type": "boolean" + } + } + }, + "tlsDisable": { + "type": "boolean" + } + } + }, + "injector": { + "type": "object", + "properties": { + "affinity": { + "type": [ + "object", + "string" + ] + }, + "agentDefaults": { + "type": "object", + "properties": { + "cpuLimit": { + "type": "string" + }, + "cpuRequest": { + "type": "string" + }, + "memLimit": { + "type": "string" + }, + "memRequest": { + "type": "string" + }, + "ephemeralLimit": { + "type": "string" + }, + "ephemeralRequest": { + "type": "string" + }, + "template": { + "type": "string" + }, + "templateConfig": { + "type": "object", + "properties": { + "exitOnRetryFailure": { + "type": "boolean" + }, + "staticSecretRenderInterval": { + "type": "string" + } + } + } + } + }, + "agentImage": { + "type": "object", + "properties": { + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "authPath": { + "type": "string" + }, + "certs": { + "type": "object", + "properties": { + "caBundle": { + "type": "string" + }, + "certName": { + "type": "string" + }, + "keyName": { + "type": "string" + }, + "secretName": { + "type": [ + "null", + "string" + ] + } + } + }, + "enabled": { + "type": [ + "boolean", + "string" + ] + }, + "externalVaultAddr": { + "type": "string" + }, + "extraEnvironmentVars": { + "type": "object" + }, + "extraLabels": { + "type": "object" + }, + "failurePolicy": { + "type": "string" + }, + "hostNetwork": { + "type": "boolean" + }, + "image": { + "type": "object", + "properties": { + "pullPolicy": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "leaderElector": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "logFormat": { + "type": "string" + }, + "logLevel": { + "type": "string" + }, + "metrics": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "namespaceSelector": { + "type": "object" + }, + "nodeSelector": { + "type": [ + "null", + "object", + "string" + ] + }, + "objectSelector": { + "type": [ + "object", + "string" + ] + }, + "podDisruptionBudget": { + "type": "object" + }, + "port": { + "type": "integer" + }, + "priorityClassName": { + "type": "string" + }, + "replicas": { + "type": "integer" + }, + "resources": { + "type": "object" + }, + "revokeOnShutdown": { + "type": "boolean" + }, + "securityContext": { + "type": "object", + "properties": { + "container": { + "type": [ + "object", + "string" + ] + }, + "pod": { + "type": [ + "object", + "string" + ] + } + } + }, + "service": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + } + } + }, + "serviceAccount": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + } + } + }, + "strategy": { + "type": [ + "object", + "string" + ] + }, + "tolerations": { + "type": [ + "null", + "array", + "string" + ] + }, + "topologySpreadConstraints": { + "type": [ + "null", + "array", + "string" + ] + }, + "webhook": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "failurePolicy": { + "type": "string" + }, + "matchPolicy": { + "type": "string" + }, + "namespaceSelector": { + "type": "object" + }, + "objectSelector": { + "type": [ + "object", + "string" + ] + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "webhookAnnotations": { + "type": [ + "object", + "string" + ] + } + } + }, + "server": { + "type": "object", + "properties": { + "affinity": { + "type": [ + "object", + "string" + ] + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "auditStorage": { + "type": "object", + "properties": { + "accessMode": { + "type": "string" + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": [ + "boolean", + "string" + ] + }, + "mountPath": { + "type": "string" + }, + "size": { + "type": "string" + }, + "storageClass": { + "type": [ + "null", + "string" + ] + } + } + }, + "authDelegator": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "dataStorage": { + "type": "object", + "properties": { + "accessMode": { + "type": "string" + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": [ + "boolean", + "string" + ] + }, + "mountPath": { + "type": "string" + }, + "size": { + "type": "string" + }, + "storageClass": { + "type": [ + "null", + "string" + ] + } + } + }, + "dev": { + "type": "object", + "properties": { + "devRootToken": { + "type": "string" + }, + "enabled": { + "type": "boolean" + } + } + }, + "enabled": { + "type": [ + "boolean", + "string" + ] + }, + "enterpriseLicense": { + "type": "object", + "properties": { + "secretKey": { + "type": "string" + }, + "secretName": { + "type": "string" + } + } + }, + "extraArgs": { + "type": "string" + }, + "extraPorts": { + "type": [ + "null", + "array" + ] + }, + "extraContainers": { + "type": [ + "null", + "array" + ] + }, + "extraEnvironmentVars": { + "type": "object" + }, + "extraInitContainers": { + "type": [ + "null", + "array" + ] + }, + "extraLabels": { + "type": "object" + }, + "extraSecretEnvironmentVars": { + "type": "array" + }, + "extraVolumes": { + "type": "array" + }, + "ha": { + "type": "object", + "properties": { + "apiAddr": { + "type": [ + "null", + "string" + ] + }, + "clusterAddr": { + "type": [ + "null", + "string" + ] + }, + "config": { + "type": [ + "string", + "object" + ] + }, + "disruptionBudget": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "maxUnavailable": { + "type": [ + "null", + "integer" + ] + } + } + }, + "enabled": { + "type": "boolean" + }, + "raft": { + "type": "object", + "properties": { + "config": { + "type": [ + "string", + "object" + ] + }, + "enabled": { + "type": "boolean" + }, + "setNodeId": { + "type": "boolean" + } + } + }, + "replicas": { + "type": "integer" + } + } + }, + "image": { + "type": "object", + "properties": { + "pullPolicy": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "ingress": { + "type": "object", + "properties": { + "activeService": { + "type": "boolean" + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": "boolean" + }, + "extraPaths": { + "type": "array" + }, + "hosts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "host": { + "type": "string" + }, + "paths": { + "type": "array" + } + } + } + }, + "ingressClassName": { + "type": "string" + }, + "labels": { + "type": "object" + }, + "pathType": { + "type": "string" + }, + "tls": { + "type": "array" + } + } + }, + "livenessProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "failureThreshold": { + "type": "integer" + }, + "initialDelaySeconds": { + "type": "integer" + }, + "path": { + "type": "string" + }, + "periodSeconds": { + "type": "integer" + }, + "successThreshold": { + "type": "integer" + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "logFormat": { + "type": "string" + }, + "logLevel": { + "type": "string" + }, + "networkPolicy": { + "type": "object", + "properties": { + "egress": { + "type": "array" + }, + "enabled": { + "type": "boolean" + } + } + }, + "nodeSelector": { + "type": [ + "null", + "object", + "string" + ] + }, + "postStart": { + "type": "array" + }, + "preStopSleepSeconds": { + "type": "integer" + }, + "priorityClassName": { + "type": "string" + }, + "readinessProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "failureThreshold": { + "type": "integer" + }, + "initialDelaySeconds": { + "type": "integer" + }, + "periodSeconds": { + "type": "integer" + }, + "successThreshold": { + "type": "integer" + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "resources": { + "type": "object" + }, + "route": { + "type": "object", + "properties": { + "activeService": { + "type": "boolean" + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": "boolean" + }, + "host": { + "type": "string" + }, + "labels": { + "type": "object" + }, + "tls": { + "type": "object" + } + } + }, + "service": { + "type": "object", + "properties": { + "active": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": "boolean" + }, + "externalTrafficPolicy": { + "type": "string" + }, + "instanceSelector": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "port": { + "type": "integer" + }, + "publishNotReadyAddresses": { + "type": "boolean" + }, + "standby": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "targetPort": { + "type": "integer" + }, + "nodePort": { + "type": "integer" + }, + "activeNodePort": { + "type": "integer" + }, + "standbyNodePort": { + "type": "integer" + } + } + }, + "serviceAccount": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "create": { + "type": "boolean" + }, + "extraLabels": { + "type": "object" + }, + "name": { + "type": "string" + }, + "serviceDiscovery": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + } + } + }, + "shareProcessNamespace": { + "type": "boolean" + }, + "standalone": { + "type": "object", + "properties": { + "config": { + "type": [ + "string", + "object" + ] + }, + "enabled": { + "type": [ + "string", + "boolean" + ] + } + } + }, + "statefulSet": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "securityContext": { + "type": "object", + "properties": { + "container": { + "type": [ + "object", + "string" + ] + }, + "pod": { + "type": [ + "object", + "string" + ] + } + } + } + } + }, + "terminationGracePeriodSeconds": { + "type": "integer" + }, + "tolerations": { + "type": [ + "null", + "array", + "string" + ] + }, + "topologySpreadConstraints": { + "type": [ + "null", + "array", + "string" + ] + }, + "updateStrategyType": { + "type": "string" + }, + "volumeMounts": { + "type": [ + "null", + "array" + ] + }, + "volumes": { + "type": [ + "null", + "array" + ] + }, + "hostNetwork": { + "type": "boolean" + } + } + }, + "serverTelemetry": { + "type": "object", + "properties": { + "prometheusRules": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "rules": { + "type": "array" + }, + "selectors": { + "type": "object" + } + } + } + } + }, + "ui": { + "type": "object", + "properties": { + "activeVaultPodOnly": { + "type": "boolean" + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": [ + "boolean", + "string" + ] + }, + "externalPort": { + "type": "integer" + }, + "externalTrafficPolicy": { + "type": "string" + }, + "publishNotReadyAddresses": { + "type": "boolean" + }, + "serviceNodePort": { + "type": [ + "null", + "integer" + ] + }, + "serviceType": { + "type": "string" + }, + "targetPort": { + "type": "integer" + } + } + } + } +} diff --git a/charts/partners/hashicorp/vault/0.25.0/src/values.yaml b/charts/partners/hashicorp/vault/0.25.0/src/values.yaml new file mode 100644 index 00000000..3756ed97 --- /dev/null +++ b/charts/partners/hashicorp/vault/0.25.0/src/values.yaml @@ -0,0 +1,1106 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +# Available parameters and their default values for the Vault chart. + +global: + # enabled is the master enabled switch. Setting this to true or false + # will enable or disable all the components within this chart by default. + enabled: true + # Image pull secret to use for registry authentication. + # Alternatively, the value may be specified as an array of strings. + imagePullSecrets: [] + # imagePullSecrets: + # - name: image-pull-secret + + # TLS for end-to-end encrypted transport + tlsDisable: true + # External vault server address for the injector and CSI provider to use. + # Setting this will disable deployment of a vault server. + externalVaultAddr: "" + # If deploying to OpenShift + openshift: true + # Create PodSecurityPolicy for pods + psp: + enable: false + # Annotation for PodSecurityPolicy. + # This is a multi-line templated string map, and can also be set as YAML. + annotations: | + seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default,runtime/default + apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default + seccomp.security.alpha.kubernetes.io/defaultProfileName: runtime/default + apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default + serverTelemetry: + # Enable integration with the Prometheus Operator + # See the top level serverTelemetry section below before enabling this feature. + prometheusOperator: false +injector: + # True if you want to enable vault agent injection. + # @default: global.enabled + enabled: "-" + replicas: 1 + # Configures the port the injector should listen on + port: 8080 + # If multiple replicas are specified, by default a leader will be determined + # so that only one injector attempts to create TLS certificates. + leaderElector: + enabled: true + # If true, will enable a node exporter metrics endpoint at /metrics. + metrics: + enabled: false + # Deprecated: Please use global.externalVaultAddr instead. + externalVaultAddr: "" + # image sets the repo and tag of the vault-k8s image to use for the injector. + image: + repository: "registry.connect.redhat.com/hashicorp/vault-k8s" + tag: "1.2.1-ubi" + pullPolicy: IfNotPresent + # agentImage sets the repo and tag of the Vault image to use for the Vault Agent + # containers. This should be set to the official Vault image. Vault 1.3.1+ is + # required. + agentImage: + repository: "registry.connect.redhat.com/hashicorp/vault" + tag: "1.14.0-ubi" + # The default values for the injected Vault Agent containers. + agentDefaults: + # For more information on configuring resources, see the K8s documentation: + # https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + cpuLimit: "500m" + cpuRequest: "250m" + memLimit: "128Mi" + memRequest: "64Mi" + # ephemeralLimit: "128Mi" + # ephemeralRequest: "64Mi" + + # Default template type for secrets when no custom template is specified. + # Possible values include: "json" and "map". + template: "map" + # Default values within Agent's template_config stanza. + templateConfig: + exitOnRetryFailure: true + staticSecretRenderInterval: "" + # Used to define custom livenessProbe settings + livenessProbe: + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 2 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 5 + # How often (in seconds) to perform the probe + periodSeconds: 2 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 5 + # Used to define custom readinessProbe settings + readinessProbe: + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 2 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 5 + # How often (in seconds) to perform the probe + periodSeconds: 2 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 5 + # Used to define custom startupProbe settings + startupProbe: + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 12 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 5 + # How often (in seconds) to perform the probe + periodSeconds: 5 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 5 + # Mount Path of the Vault Kubernetes Auth Method. + authPath: "auth/kubernetes" + # Configures the log verbosity of the injector. + # Supported log levels include: trace, debug, info, warn, error + logLevel: "info" + # Configures the log format of the injector. Supported log formats: "standard", "json". + logFormat: "standard" + # Configures all Vault Agent sidecars to revoke their token when shutting down + revokeOnShutdown: false + webhook: + # Configures failurePolicy of the webhook. The "unspecified" default behaviour depends on the + # API Version of the WebHook. + # To block pod creation while the webhook is unavailable, set the policy to `Fail` below. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy + # + failurePolicy: Ignore + # matchPolicy specifies the approach to accepting changes based on the rules of + # the MutatingWebhookConfiguration. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy + # for more details. + # + matchPolicy: Exact + # timeoutSeconds is the amount of seconds before the webhook request will be ignored + # or fails. + # If it is ignored or fails depends on the failurePolicy + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#timeouts + # for more details. + # + timeoutSeconds: 30 + # namespaceSelector is the selector for restricting the webhook to only + # specific namespaces. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector + # for more details. + # Example: + # namespaceSelector: + # matchLabels: + # sidecar-injector: enabled + namespaceSelector: {} + # objectSelector is the selector for restricting the webhook to only + # specific labels. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector + # for more details. + # Example: + # objectSelector: + # matchLabels: + # vault-sidecar-injector: enabled + objectSelector: | + matchExpressions: + - key: app.kubernetes.io/name + operator: NotIn + values: + - {{ template "vault.name" . }}-agent-injector + # Extra annotations to attach to the webhook + annotations: {} + # Deprecated: please use 'webhook.failurePolicy' instead + # Configures failurePolicy of the webhook. The "unspecified" default behaviour depends on the + # API Version of the WebHook. + # To block pod creation while webhook is unavailable, set the policy to `Fail` below. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy + # + failurePolicy: Ignore + # Deprecated: please use 'webhook.namespaceSelector' instead + # namespaceSelector is the selector for restricting the webhook to only + # specific namespaces. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector + # for more details. + # Example: + # namespaceSelector: + # matchLabels: + # sidecar-injector: enabled + namespaceSelector: {} + # Deprecated: please use 'webhook.objectSelector' instead + # objectSelector is the selector for restricting the webhook to only + # specific labels. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector + # for more details. + # Example: + # objectSelector: + # matchLabels: + # vault-sidecar-injector: enabled + objectSelector: {} + # Deprecated: please use 'webhook.annotations' instead + # Extra annotations to attach to the webhook + webhookAnnotations: {} + certs: + # secretName is the name of the secret that has the TLS certificate and + # private key to serve the injector webhook. If this is null, then the + # injector will default to its automatic management mode that will assign + # a service account to the injector to generate its own certificates. + secretName: null + # caBundle is a base64-encoded PEM-encoded certificate bundle for the CA + # that signed the TLS certificate that the webhook serves. This must be set + # if secretName is non-null unless an external service like cert-manager is + # keeping the caBundle updated. + caBundle: "" + # certName and keyName are the names of the files within the secret for + # the TLS cert and private key, respectively. These have reasonable + # defaults but can be customized if necessary. + certName: tls.crt + keyName: tls.key + # Security context for the pod template and the injector container + # The default pod securityContext is: + # runAsNonRoot: true + # runAsGroup: {{ .Values.injector.gid | default 1000 }} + # runAsUser: {{ .Values.injector.uid | default 100 }} + # fsGroup: {{ .Values.injector.gid | default 1000 }} + # and for container is + # allowPrivilegeEscalation: false + # capabilities: + # drop: + # - ALL + securityContext: + pod: {} + container: {} + resources: {} + # resources: + # requests: + # memory: 256Mi + # cpu: 250m + # limits: + # memory: 256Mi + # cpu: 250m + + # extraEnvironmentVars is a list of extra environment variables to set in the + # injector deployment. + extraEnvironmentVars: {} + # KUBERNETES_SERVICE_HOST: kubernetes.default.svc + + # Affinity Settings for injector pods + # This can either be a multi-line string or YAML matching the PodSpec's affinity field. + # Commenting out or setting as empty the affinity variable, will allow + # deployment of multiple replicas to single node services such as Minikube. + affinity: | + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: "{{ .Release.Name }}" + component: webhook + topologyKey: kubernetes.io/hostname + # Topology settings for injector pods + # ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ + # This should be either a multi-line string or YAML matching the topologySpreadConstraints array + # in a PodSpec. + topologySpreadConstraints: [] + # Toleration Settings for injector pods + # This should be either a multi-line string or YAML matching the Toleration array + # in a PodSpec. + tolerations: [] + # nodeSelector labels for server pod assignment, formatted as a multi-line string or YAML map. + # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + # Example: + # nodeSelector: + # beta.kubernetes.io/arch: amd64 + nodeSelector: {} + # Priority class for injector pods + priorityClassName: "" + # Extra annotations to attach to the injector pods + # This can either be YAML or a YAML-formatted multi-line templated string map + # of the annotations to apply to the injector pods + annotations: {} + # Extra labels to attach to the agent-injector + # This should be a YAML map of the labels to apply to the injector + extraLabels: {} + # Should the injector pods run on the host network (useful when using + # an alternate CNI in EKS) + hostNetwork: false + # Injector service specific config + service: + # Extra annotations to attach to the injector service + annotations: {} + # Injector serviceAccount specific config + serviceAccount: + # Extra annotations to attach to the injector serviceAccount + annotations: {} + # A disruption budget limits the number of pods of a replicated application + # that are down simultaneously from voluntary disruptions + podDisruptionBudget: {} + # podDisruptionBudget: + # maxUnavailable: 1 + + # strategy for updating the deployment. This can be a multi-line string or a + # YAML map. + strategy: {} + # strategy: | + # rollingUpdate: + # maxSurge: 25% + # maxUnavailable: 25% + # type: RollingUpdate +server: + # If true, or "-" with global.enabled true, Vault server will be installed. + # See vault.mode in _helpers.tpl for implementation details. + enabled: "-" + # [Enterprise Only] This value refers to a Kubernetes secret that you have + # created that contains your enterprise license. If you are not using an + # enterprise image or if you plan to introduce the license key via another + # route, then leave secretName blank ("") or set it to null. + # Requires Vault Enterprise 1.8 or later. + enterpriseLicense: + # The name of the Kubernetes secret that holds the enterprise license. The + # secret must be in the same namespace that Vault is installed into. + secretName: "" + # The key within the Kubernetes secret that holds the enterprise license. + secretKey: "license" + # Resource requests, limits, etc. for the server cluster placement. This + # should map directly to the value of the resources field for a PodSpec. + # By default no direct resource request is made. + image: + repository: "registry.connect.redhat.com/hashicorp/vault" + tag: "1.14.0-ubi" + # Overrides the default Image Pull Policy + pullPolicy: IfNotPresent + # Configure the Update Strategy Type for the StatefulSet + # See https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies + updateStrategyType: "OnDelete" + # Configure the logging verbosity for the Vault server. + # Supported log levels include: trace, debug, info, warn, error + logLevel: "" + # Configure the logging format for the Vault server. + # Supported log formats include: standard, json + logFormat: "" + resources: {} + # resources: + # requests: + # memory: 256Mi + # cpu: 250m + # limits: + # memory: 256Mi + # cpu: 250m + + # Ingress allows ingress services to be created to allow external access + # from Kubernetes to access Vault pods. + # If deployment is on OpenShift, the following block is ignored. + # In order to expose the service, use the route section below + ingress: + enabled: false + labels: {} + # traffic: external + annotations: {} + # | + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + # or + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + + # Optionally use ingressClassName instead of deprecated annotation. + # See: https://kubernetes.io/docs/concepts/services-networking/ingress/#deprecated-annotation + ingressClassName: "" + # As of Kubernetes 1.19, all Ingress Paths must have a pathType configured. The default value below should be sufficient in most cases. + # See: https://kubernetes.io/docs/concepts/services-networking/ingress/#path-types for other possible values. + pathType: Prefix + # When HA mode is enabled and K8s service registration is being used, + # configure the ingress to point to the Vault active service. + activeService: true + hosts: + - host: chart-example.local + paths: [] + ## Extra paths to prepend to the host configuration. This is useful when working with annotation based services. + extraPaths: [] + # - path: /* + # backend: + # service: + # name: ssl-redirect + # port: + # number: use-annotation + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + # OpenShift only - create a route to expose the service + # By default the created route will be of type passthrough + route: + enabled: false + # When HA mode is enabled and K8s service registration is being used, + # configure the route to point to the Vault active service. + activeService: true + labels: {} + annotations: {} + host: chart-example.local + # tls will be passed directly to the route's TLS config, which + # can be used to configure other termination methods that terminate + # TLS at the router + tls: + termination: passthrough + # authDelegator enables a cluster role binding to be attached to the service + # account. This cluster role binding can be used to setup Kubernetes auth + # method. https://www.vaultproject.io/docs/auth/kubernetes.html + authDelegator: + enabled: true + # extraInitContainers is a list of init containers. Specified as a YAML list. + # This is useful if you need to run a script to provision TLS certificates or + # write out configuration files in a dynamic way. + extraInitContainers: null + # # This example installs a plugin pulled from github into the /usr/local/libexec/vault/oauthapp folder, + # # which is defined in the volumes value. + # - name: oauthapp + # image: "alpine" + # command: [sh, -c] + # args: + # - cd /tmp && + # wget https://github.com/puppetlabs/vault-plugin-secrets-oauthapp/releases/download/v1.2.0/vault-plugin-secrets-oauthapp-v1.2.0-linux-amd64.tar.xz -O oauthapp.xz && + # tar -xf oauthapp.xz && + # mv vault-plugin-secrets-oauthapp-v1.2.0-linux-amd64 /usr/local/libexec/vault/oauthapp && + # chmod +x /usr/local/libexec/vault/oauthapp + # volumeMounts: + # - name: plugins + # mountPath: /usr/local/libexec/vault + + # extraContainers is a list of sidecar containers. Specified as a YAML list. + extraContainers: null + # shareProcessNamespace enables process namespace sharing between Vault and the extraContainers + # This is useful if Vault must be signaled, e.g. to send a SIGHUP for a log rotation + shareProcessNamespace: false + # extraArgs is a string containing additional Vault server arguments. + extraArgs: "" + # extraPorts is a list of extra ports. Specified as a YAML list. + # This is useful if you need to add additional ports to the statefulset in dynamic way. + extraPorts: null + # - containerPort: 8300 + # name: http-monitoring + + # Used to define custom readinessProbe settings + readinessProbe: + enabled: true + # If you need to use a http path instead of the default exec + # path: /v1/sys/health?standbyok=true + + # Port number on which readinessProbe will be checked. + port: 8200 + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 2 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 5 + # How often (in seconds) to perform the probe + periodSeconds: 5 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 3 + # Used to enable a livenessProbe for the pods + livenessProbe: + enabled: false + path: "/v1/sys/health?standbyok=true" + # Port number on which livenessProbe will be checked. + port: 8200 + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 2 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 60 + # How often (in seconds) to perform the probe + periodSeconds: 5 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 3 + # Optional duration in seconds the pod needs to terminate gracefully. + # See: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/ + terminationGracePeriodSeconds: 10 + # Used to set the sleep time during the preStop step + preStopSleepSeconds: 5 + # Used to define commands to run after the pod is ready. + # This can be used to automate processes such as initialization + # or boostrapping auth methods. + postStart: [] + # - /bin/sh + # - -c + # - /vault/userconfig/myscript/run.sh + + # extraEnvironmentVars is a list of extra environment variables to set with the stateful set. These could be + # used to include variables required for auto-unseal. + extraEnvironmentVars: {} + # GOOGLE_REGION: global + # GOOGLE_PROJECT: myproject + # GOOGLE_APPLICATION_CREDENTIALS: /vault/userconfig/myproject/myproject-creds.json + + # extraSecretEnvironmentVars is a list of extra environment variables to set with the stateful set. + # These variables take value from existing Secret objects. + extraSecretEnvironmentVars: [] + # - envName: AWS_SECRET_ACCESS_KEY + # secretName: vault + # secretKey: AWS_SECRET_ACCESS_KEY + + # Deprecated: please use 'volumes' instead. + # extraVolumes is a list of extra volumes to mount. These will be exposed + # to Vault in the path `/vault/userconfig//`. The value below is + # an array of objects, examples are shown below. + extraVolumes: [] + # - type: secret (or "configMap") + # name: my-secret + # path: null # default is `/vault/userconfig` + + # volumes is a list of volumes made available to all containers. These are rendered + # via toYaml rather than pre-processed like the extraVolumes value. + # The purpose is to make it easy to share volumes between containers. + volumes: null + # - name: plugins + # emptyDir: {} + + # volumeMounts is a list of volumeMounts for the main server container. These are rendered + # via toYaml rather than pre-processed like the extraVolumes value. + # The purpose is to make it easy to share volumes between containers. + volumeMounts: null + # - mountPath: /usr/local/libexec/vault + # name: plugins + # readOnly: true + + # Affinity Settings + # Commenting out or setting as empty the affinity variable, will allow + # deployment to single node services such as Minikube + # This should be either a multi-line string or YAML matching the PodSpec's affinity field. + affinity: | + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: "{{ .Release.Name }}" + component: server + topologyKey: kubernetes.io/hostname + # Topology settings for server pods + # ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ + # This should be either a multi-line string or YAML matching the topologySpreadConstraints array + # in a PodSpec. + topologySpreadConstraints: [] + # Toleration Settings for server pods + # This should be either a multi-line string or YAML matching the Toleration array + # in a PodSpec. + tolerations: [] + # nodeSelector labels for server pod assignment, formatted as a multi-line string or YAML map. + # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + # Example: + # nodeSelector: + # beta.kubernetes.io/arch: amd64 + nodeSelector: {} + # Enables network policy for server pods + networkPolicy: + enabled: false + egress: [] + # egress: + # - to: + # - ipBlock: + # cidr: 10.0.0.0/24 + # ports: + # - protocol: TCP + # port: 443 + # Priority class for server pods + priorityClassName: "" + # Extra labels to attach to the server pods + # This should be a YAML map of the labels to apply to the server pods + extraLabels: {} + # Extra annotations to attach to the server pods + # This can either be YAML or a YAML-formatted multi-line templated string map + # of the annotations to apply to the server pods + annotations: {} + # Enables a headless service to be used by the Vault Statefulset + service: + enabled: true + # Enable or disable the vault-active service, which selects Vault pods that + # have labelled themselves as the cluster leader with `vault-active: "true"` + active: + enabled: true + # Enable or disable the vault-standby service, which selects Vault pods that + # have labelled themselves as a cluster follower with `vault-active: "false"` + standby: + enabled: true + # If enabled, the service selectors will include `app.kubernetes.io/instance: {{ .Release.Name }}` + # When disabled, services may select Vault pods not deployed from the chart. + # Does not affect the headless vault-internal service with `ClusterIP: None` + instanceSelector: + enabled: true + # clusterIP controls whether a Cluster IP address is attached to the + # Vault service within Kubernetes. By default, the Vault service will + # be given a Cluster IP address, set to None to disable. When disabled + # Kubernetes will create a "headless" service. Headless services can be + # used to communicate with pods directly through DNS instead of a round-robin + # load balancer. + # clusterIP: None + + # Configures the service type for the main Vault service. Can be ClusterIP + # or NodePort. + #type: ClusterIP + + # Do not wait for pods to be ready before including them in the services' + # targets. Does not apply to the headless service, which is used for + # cluster-internal communication. + publishNotReadyAddresses: true + # The externalTrafficPolicy can be set to either Cluster or Local + # and is only valid for LoadBalancer and NodePort service types. + # The default value is Cluster. + # ref: https://kubernetes.io/docs/concepts/services-networking/service/#external-traffic-policy + externalTrafficPolicy: Cluster + # If type is set to "NodePort", a specific nodePort value can be configured, + # will be random if left blank. + #nodePort: 30000 + + # When HA mode is enabled + # If type is set to "NodePort", a specific nodePort value can be configured, + # will be random if left blank. + #activeNodePort: 30001 + + # When HA mode is enabled + # If type is set to "NodePort", a specific nodePort value can be configured, + # will be random if left blank. + #standbyNodePort: 30002 + + # Port on which Vault server is listening + port: 8200 + # Target port to which the service should be mapped to + targetPort: 8200 + # Extra annotations for the service definition. This can either be YAML or a + # YAML-formatted multi-line templated string map of the annotations to apply + # to the service. + annotations: {} + # This configures the Vault Statefulset to create a PVC for data + # storage when using the file or raft backend storage engines. + # See https://www.vaultproject.io/docs/configuration/storage/index.html to know more + dataStorage: + enabled: true + # Size of the PVC created + size: 10Gi + # Location where the PVC will be mounted. + mountPath: "/vault/data" + # Name of the storage class to use. If null it will use the + # configured default Storage Class. + storageClass: null + # Access Mode of the storage device being used for the PVC + accessMode: ReadWriteOnce + # Annotations to apply to the PVC + annotations: {} + # This configures the Vault Statefulset to create a PVC for audit + # logs. Once Vault is deployed, initialized, and unsealed, Vault must + # be configured to use this for audit logs. This will be mounted to + # /vault/audit + # See https://www.vaultproject.io/docs/audit/index.html to know more + auditStorage: + enabled: false + # Size of the PVC created + size: 10Gi + # Location where the PVC will be mounted. + mountPath: "/vault/audit" + # Name of the storage class to use. If null it will use the + # configured default Storage Class. + storageClass: null + # Access Mode of the storage device being used for the PVC + accessMode: ReadWriteOnce + # Annotations to apply to the PVC + annotations: {} + # Run Vault in "dev" mode. This requires no further setup, no state management, + # and no initialization. This is useful for experimenting with Vault without + # needing to unseal, store keys, et. al. All data is lost on restart - do not + # use dev mode for anything other than experimenting. + # See https://www.vaultproject.io/docs/concepts/dev-server.html to know more + dev: + enabled: false + # Set VAULT_DEV_ROOT_TOKEN_ID value + devRootToken: "root" + # Run Vault in "standalone" mode. This is the default mode that will deploy if + # no arguments are given to helm. This requires a PVC for data storage to use + # the "file" backend. This mode is not highly available and should not be scaled + # past a single replica. + standalone: + enabled: "-" + # config is a raw string of default configuration when using a Stateful + # deployment. Default is to use a PersistentVolumeClaim mounted at /vault/data + # and store data there. This is only used when using a Replica count of 1, and + # using a stateful set. This should be HCL. + + # Note: Configuration files are stored in ConfigMaps so sensitive data + # such as passwords should be either mounted through extraSecretEnvironmentVars + # or through a Kube secret. For more information see: + # https://www.vaultproject.io/docs/platform/k8s/helm/run#protecting-sensitive-vault-configurations + config: | + ui = true + + listener "tcp" { + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + # Enable unauthenticated metrics access (necessary for Prometheus Operator) + #telemetry { + # unauthenticated_metrics_access = "true" + #} + } + storage "file" { + path = "/vault/data" + } + + # Example configuration for using auto-unseal, using Google Cloud KMS. The + # GKMS keys must already exist, and the cluster must have a service account + # that is authorized to access GCP KMS. + #seal "gcpckms" { + # project = "vault-helm-dev" + # region = "global" + # key_ring = "vault-helm-unseal-kr" + # crypto_key = "vault-helm-unseal-key" + #} + + # Example configuration for enabling Prometheus metrics in your config. + #telemetry { + # prometheus_retention_time = "30s" + # disable_hostname = true + #} + # Run Vault in "HA" mode. There are no storage requirements unless the audit log + # persistence is required. In HA mode Vault will configure itself to use Consul + # for its storage backend. The default configuration provided will work the Consul + # Helm project by default. It is possible to manually configure Vault to use a + # different HA backend. + ha: + enabled: false + replicas: 3 + # Set the api_addr configuration for Vault HA + # See https://www.vaultproject.io/docs/configuration#api_addr + # If set to null, this will be set to the Pod IP Address + apiAddr: null + # Set the cluster_addr confuguration for Vault HA + # See https://www.vaultproject.io/docs/configuration#cluster_addr + # If set to null, this will be set to https://$(HOSTNAME).{{ template "vault.fullname" . }}-internal:8201 + clusterAddr: null + # Enables Vault's integrated Raft storage. Unlike the typical HA modes where + # Vault's persistence is external (such as Consul), enabling Raft mode will create + # persistent volumes for Vault to store data according to the configuration under server.dataStorage. + # The Vault cluster will coordinate leader elections and failovers internally. + raft: + # Enables Raft integrated storage + enabled: false + # Set the Node Raft ID to the name of the pod + setNodeId: false + # Note: Configuration files are stored in ConfigMaps so sensitive data + # such as passwords should be either mounted through extraSecretEnvironmentVars + # or through a Kube secret. For more information see: + # https://www.vaultproject.io/docs/platform/k8s/helm/run#protecting-sensitive-vault-configurations + config: | + ui = true + + listener "tcp" { + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + # Enable unauthenticated metrics access (necessary for Prometheus Operator) + #telemetry { + # unauthenticated_metrics_access = "true" + #} + } + + storage "raft" { + path = "/vault/data" + } + + service_registration "kubernetes" {} + # config is a raw string of default configuration when using a Stateful + # deployment. Default is to use a Consul for its HA storage backend. + # This should be HCL. + + # Note: Configuration files are stored in ConfigMaps so sensitive data + # such as passwords should be either mounted through extraSecretEnvironmentVars + # or through a Kube secret. For more information see: + # https://www.vaultproject.io/docs/platform/k8s/helm/run#protecting-sensitive-vault-configurations + config: | + ui = true + + listener "tcp" { + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + } + storage "consul" { + path = "vault" + address = "HOST_IP:8500" + } + + service_registration "kubernetes" {} + + # Example configuration for using auto-unseal, using Google Cloud KMS. The + # GKMS keys must already exist, and the cluster must have a service account + # that is authorized to access GCP KMS. + #seal "gcpckms" { + # project = "vault-helm-dev-246514" + # region = "global" + # key_ring = "vault-helm-unseal-kr" + # crypto_key = "vault-helm-unseal-key" + #} + + # Example configuration for enabling Prometheus metrics. + # If you are using Prometheus Operator you can enable a ServiceMonitor resource below. + # You may wish to enable unauthenticated metrics in the listener block above. + #telemetry { + # prometheus_retention_time = "30s" + # disable_hostname = true + #} + # A disruption budget limits the number of pods of a replicated application + # that are down simultaneously from voluntary disruptions + disruptionBudget: + enabled: true + # maxUnavailable will default to (n/2)-1 where n is the number of + # replicas. If you'd like a custom value, you can specify an override here. + maxUnavailable: null + # Definition of the serviceAccount used to run Vault. + # These options are also used when using an external Vault server to validate + # Kubernetes tokens. + serviceAccount: + # Specifies whether a service account should be created + create: true + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + # Extra annotations for the serviceAccount definition. This can either be + # YAML or a YAML-formatted multi-line templated string map of the + # annotations to apply to the serviceAccount. + annotations: {} + # Extra labels to attach to the serviceAccount + # This should be a YAML map of the labels to apply to the serviceAccount + extraLabels: {} + # Enable or disable a service account role binding with the permissions required for + # Vault's Kubernetes service_registration config option. + # See https://developer.hashicorp.com/vault/docs/configuration/service-registration/kubernetes + serviceDiscovery: + enabled: true + # Settings for the statefulSet used to run Vault. + statefulSet: + # Extra annotations for the statefulSet. This can either be YAML or a + # YAML-formatted multi-line templated string map of the annotations to apply + # to the statefulSet. + annotations: {} + # Set the pod and container security contexts. + # If not set, these will default to, and for *not* OpenShift: + # pod: + # runAsNonRoot: true + # runAsGroup: {{ .Values.server.gid | default 1000 }} + # runAsUser: {{ .Values.server.uid | default 100 }} + # fsGroup: {{ .Values.server.gid | default 1000 }} + # container: + # allowPrivilegeEscalation: false + # + # If not set, these will default to, and for OpenShift: + # pod: {} + # container: {} + securityContext: + pod: {} + container: {} + # Should the server pods run on the host network + hostNetwork: false +# Vault UI +ui: + # True if you want to create a Service entry for the Vault UI. + # + # serviceType can be used to control the type of service created. For + # example, setting this to "LoadBalancer" will create an external load + # balancer (for supported K8S installations) to access the UI. + enabled: false + publishNotReadyAddresses: true + # The service should only contain selectors for active Vault pod + activeVaultPodOnly: false + serviceType: "ClusterIP" + serviceNodePort: null + externalPort: 8200 + targetPort: 8200 + # The externalTrafficPolicy can be set to either Cluster or Local + # and is only valid for LoadBalancer and NodePort service types. + # The default value is Cluster. + # ref: https://kubernetes.io/docs/concepts/services-networking/service/#external-traffic-policy + externalTrafficPolicy: Cluster + #loadBalancerSourceRanges: + # - 10.0.0.0/16 + # - 1.78.23.3/32 + + # loadBalancerIP: + + # Extra annotations to attach to the ui service + # This can either be YAML or a YAML-formatted multi-line templated string map + # of the annotations to apply to the ui service + annotations: {} +# secrets-store-csi-driver-provider-vault +csi: + # True if you want to install a secrets-store-csi-driver-provider-vault daemonset. + # + # Requires installing the secrets-store-csi-driver separately, see: + # https://github.com/kubernetes-sigs/secrets-store-csi-driver#install-the-secrets-store-csi-driver + # + # With the driver and provider installed, you can mount Vault secrets into volumes + # similar to the Vault Agent injector, and you can also sync those secrets into + # Kubernetes secrets. + enabled: false + image: + repository: "hashicorp/vault-csi-provider" + tag: "1.4.0" + pullPolicy: IfNotPresent + # volumes is a list of volumes made available to all containers. These are rendered + # via toYaml rather than pre-processed like the extraVolumes value. + # The purpose is to make it easy to share volumes between containers. + volumes: null + # - name: tls + # secret: + # secretName: vault-tls + + # volumeMounts is a list of volumeMounts for the main server container. These are rendered + # via toYaml rather than pre-processed like the extraVolumes value. + # The purpose is to make it easy to share volumes between containers. + volumeMounts: null + # - name: tls + # mountPath: "/vault/tls" + # readOnly: true + + resources: {} + # resources: + # requests: + # cpu: 50m + # memory: 128Mi + # limits: + # cpu: 50m + # memory: 128Mi + + # Override the default secret name for the CSI Provider's HMAC key used for + # generating secret versions. + hmacSecretName: "" + # Settings for the daemonSet used to run the provider. + daemonSet: + updateStrategy: + type: RollingUpdate + maxUnavailable: "" + # Extra annotations for the daemonSet. This can either be YAML or a + # YAML-formatted multi-line templated string map of the annotations to apply + # to the daemonSet. + annotations: {} + # Provider host path (must match the CSI provider's path) + providersDir: "/etc/kubernetes/secrets-store-csi-providers" + # Kubelet host path + kubeletRootDir: "/var/lib/kubelet" + # Extra labels to attach to the vault-csi-provider daemonSet + # This should be a YAML map of the labels to apply to the csi provider daemonSet + extraLabels: {} + # security context for the pod template and container in the csi provider daemonSet + securityContext: + pod: {} + container: {} + pod: + # Extra annotations for the provider pods. This can either be YAML or a + # YAML-formatted multi-line templated string map of the annotations to apply + # to the pod. + annotations: {} + # Toleration Settings for provider pods + # This should be either a multi-line string or YAML matching the Toleration array + # in a PodSpec. + tolerations: [] + # nodeSelector labels for csi pod assignment, formatted as a multi-line string or YAML map. + # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + # Example: + # nodeSelector: + # beta.kubernetes.io/arch: amd64 + nodeSelector: {} + # Affinity Settings + # This should be either a multi-line string or YAML matching the PodSpec's affinity field. + affinity: {} + # Extra labels to attach to the vault-csi-provider pod + # This should be a YAML map of the labels to apply to the csi provider pod + extraLabels: {} + agent: + enabled: true + extraArgs: [] + image: + repository: "hashicorp/vault" + tag: "1.14.0" + pullPolicy: IfNotPresent + logFormat: standard + logLevel: info + resources: {} + # resources: + # requests: + # memory: 256Mi + # cpu: 250m + # limits: + # memory: 256Mi + # cpu: 250m + # Priority class for csi pods + priorityClassName: "" + serviceAccount: + # Extra annotations for the serviceAccount definition. This can either be + # YAML or a YAML-formatted multi-line templated string map of the + # annotations to apply to the serviceAccount. + annotations: {} + # Extra labels to attach to the vault-csi-provider serviceAccount + # This should be a YAML map of the labels to apply to the csi provider serviceAccount + extraLabels: {} + # Used to configure readinessProbe for the pods. + readinessProbe: + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 2 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 5 + # How often (in seconds) to perform the probe + periodSeconds: 5 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 3 + # Used to configure livenessProbe for the pods. + livenessProbe: + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 2 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 5 + # How often (in seconds) to perform the probe + periodSeconds: 5 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 3 + # Enables debug logging. + debug: false + # Pass arbitrary additional arguments to vault-csi-provider. + # See https://www.vaultproject.io/docs/platform/k8s/csi/configurations#command-line-arguments + # for the available command line flags. + extraArgs: [] +# Vault is able to collect and publish various runtime metrics. +# Enabling this feature requires setting adding `telemetry{}` stanza to +# the Vault configuration. There are a few examples included in the `config` sections above. +# +# For more information see: +# https://www.vaultproject.io/docs/configuration/telemetry +# https://www.vaultproject.io/docs/internals/telemetry +serverTelemetry: + # Enable support for the Prometheus Operator. Currently, this chart does not support + # authenticating to Vault's metrics endpoint, so the following `telemetry{}` must be included + # in the `listener "tcp"{}` stanza + # telemetry { + # unauthenticated_metrics_access = "true" + # } + # + # See the `standalone.config` for a more complete example of this. + # + # In addition, a top level `telemetry{}` stanza must also be included in the Vault configuration: + # + # example: + # telemetry { + # prometheus_retention_time = "30s" + # disable_hostname = true + # } + # + # Configuration for monitoring the Vault server. + serviceMonitor: + # The Prometheus operator *must* be installed before enabling this feature, + # if not the chart will fail to install due to missing CustomResourceDefinitions + # provided by the operator. + # + # Instructions on how to install the Helm chart can be found here: + # https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack + # More information can be found here: + # https://github.com/prometheus-operator/prometheus-operator + # https://github.com/prometheus-operator/kube-prometheus + + # Enable deployment of the Vault Server ServiceMonitor CustomResource. + enabled: false + # Selector labels to add to the ServiceMonitor. + # When empty, defaults to: + # release: prometheus + selectors: {} + # Interval at which Prometheus scrapes metrics + interval: 30s + # Timeout for Prometheus scrapes + scrapeTimeout: 10s + prometheusRules: + # The Prometheus operator *must* be installed before enabling this feature, + # if not the chart will fail to install due to missing CustomResourceDefinitions + # provided by the operator. + + # Deploy the PrometheusRule custom resource for AlertManager based alerts. + # Requires that AlertManager is properly deployed. + enabled: false + # Selector labels to add to the PrometheusRules. + # When empty, defaults to: + # release: prometheus + selectors: {} + # Some example rules. + rules: [] + # - alert: vault-HighResponseTime + # annotations: + # message: The response time of Vault is over 500ms on average over the last 5 minutes. + # expr: vault_core_handle_request{quantile="0.5", namespace="mynamespace"} > 500 + # for: 5m + # labels: + # severity: warning + # - alert: vault-HighResponseTime + # annotations: + # message: The response time of Vault is over 1s on average over the last 5 minutes. + # expr: vault_core_handle_request{quantile="0.5", namespace="mynamespace"} > 1000 + # for: 5m + # labels: + # severity: critical diff --git a/charts/partners/ntest/test-helm-project1/OWNERS b/charts/partners/ntest/test-helm-project1/OWNERS new file mode 100644 index 00000000..00720bbe --- /dev/null +++ b/charts/partners/ntest/test-helm-project1/OWNERS @@ -0,0 +1,9 @@ +chart: + name: test-helm-project1 + shortDescription: unknown +providerDelivery: false +publicPgpKey: unknown +users: [] +vendor: + label: ntest + name: ypresa-uat1 diff --git a/charts/redhat/redhat/developer-hub/0.2.0/developer-hub-0.2.0.tgz b/charts/redhat/redhat/developer-hub/0.2.0/developer-hub-0.2.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..40626e6f00691e393b662d003f07baf1a6c716d7 GIT binary patch literal 138746 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î`-8 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