├── CODEOWNERS ├── test ├── terraform │ ├── .gitignore │ ├── outputs.tf │ ├── variables.tf │ └── main.tf ├── acceptance │ ├── csi-test │ │ ├── vault-policy.hcl │ │ ├── vault-kv-secretproviderclass.yaml │ │ └── nginx.yaml │ ├── injector-test │ │ ├── pgdump-policy.hcl │ │ ├── job.yaml │ │ ├── pg-deployment.yaml │ │ ├── bootstrap.sh │ │ └── bootstrap-cross-namespace.sh │ ├── server-test │ │ ├── annotations-overrides.yaml │ │ ├── vault-telemetry.yaml │ │ └── vault-server.yaml │ ├── helm-test.bats │ ├── injector-leader-elector.bats │ ├── server-annotations.bats │ ├── setup_suite.bash │ ├── injector.bats │ ├── server-dev.bats │ ├── server-telemetry.bats │ ├── injector-cross-namespace.bats │ ├── csi.bats │ ├── server.bats │ ├── server-ha.bats │ ├── server-ha-raft.bats │ ├── server-ha-enterprise-perf.bats │ └── server-ha-enterprise-dr.bats ├── unit │ ├── _helpers.bash │ ├── csi-clusterrole.bats │ ├── csi-rolebinding.bats │ ├── injector-clusterrolebinding.bats │ ├── schema.bats │ ├── injector-serviceaccount.bats │ ├── server-network-policy.bats │ ├── injector-clusterrole.bats │ ├── csi-role.bats │ ├── injector-psp-role.bats │ ├── injector-psp-rolebinding.bats │ ├── server-discovery-role.bats │ ├── server-discovery-rolebinding.bats │ ├── csi-clusterrolebinding.bats │ ├── csi-agent-configmap.bats │ ├── injector-disruptionbudget.bats │ ├── injector-psp.bats │ ├── injector-service.bats │ ├── server-serviceaccount-secret.bats │ ├── prometheus-prometheusrules.bats │ ├── csi-serviceaccount.bats │ ├── server-clusterrolebinding.bats │ ├── server-headless-service.bats │ ├── server-psp-role.bats │ ├── server-ha-disruptionbudget.bats │ ├── server-psp-rolebinding.bats │ └── server-serviceaccount.bats ├── kind │ └── config.yaml ├── chart │ ├── _helpers.bash │ └── verifier.bats ├── docker │ └── Test.dockerfile └── README.md ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature_request.md │ └── bug_report.md ├── dependabot.yml ├── workflows │ ├── actionlint.yml │ ├── jira.yaml │ ├── tests.yaml │ ├── update-helm-charts-index.yml │ └── acceptance.yaml ├── pull_request_template.md └── actions │ ├── setup-test-tools │ └── action.yaml │ └── acceptance-test │ └── action.yaml ├── .gitignore ├── templates ├── NOTES.txt ├── injector-serviceaccount.yaml ├── csi-clusterrole.yaml ├── injector-certs-secret.yaml ├── csi-serviceaccount.yaml ├── server-serviceaccount-secret.yaml ├── server-psp-role.yaml ├── injector-psp-role.yaml ├── server-serviceaccount.yaml ├── csi-clusterrolebinding.yaml ├── injector-clusterrolebinding.yaml ├── server-psp-rolebinding.yaml ├── csi-rolebinding.yaml ├── server-discovery-role.yaml ├── server-network-policy.yaml ├── injector-psp-rolebinding.yaml ├── injector-network-policy.yaml ├── injector-service.yaml ├── injector-disruptionbudget.yaml ├── injector-clusterrole.yaml ├── server-clusterrolebinding.yaml ├── server-config-configmap.yaml ├── injector-rolebinding.yaml ├── csi-role.yaml ├── server-disruptionbudget.yaml ├── injector-role.yaml ├── prometheus-prometheusrules.yaml ├── csi-agent-configmap.yaml ├── server-discovery-rolebinding.yaml ├── server-route.yaml ├── injector-psp.yaml ├── server-psp.yaml ├── server-headless-service.yaml ├── ui-service.yaml ├── injector-mutating-webhook.yaml ├── tests │ └── server-test.yaml ├── server-service.yaml ├── server-ingress.yaml ├── prometheus-servicemonitor.yaml ├── server-ha-standby-service.yaml └── server-ha-active-service.yaml ├── .helmignore ├── Chart.yaml ├── values.openshift.yaml ├── README.md └── Makefile /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @hashicorp/vault-ecosystem 2 | -------------------------------------------------------------------------------- /test/terraform/.gitignore: -------------------------------------------------------------------------------- 1 | vault-helm-dev-creds.json 2 | -------------------------------------------------------------------------------- /test/acceptance/csi-test/vault-policy.hcl: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | path "secret/data/kv1" { 5 | capabilities = ["read"] 6 | } -------------------------------------------------------------------------------- /test/acceptance/injector-test/pgdump-policy.hcl: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | path "database/creds/db-backup" { 5 | capabilities = ["read"] 6 | } 7 | -------------------------------------------------------------------------------- /test/unit/_helpers.bash: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | # chart_dir returns the directory for the chart 5 | chart_dir() { 6 | echo ${BATS_TEST_DIRNAME}/../.. 7 | } 8 | -------------------------------------------------------------------------------- /test/terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | output "cluster_id" { 5 | value = "${google_container_cluster.cluster.id}" 6 | } 7 | 8 | output "cluster_name" { 9 | value = "${google_container_cluster.cluster.name}" 10 | } 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | contact_links: 5 | - name: Ask a question 6 | url: https://discuss.hashicorp.com/c/vault 7 | about: For increased visibility, please post questions on the discussion forum, and tag with `k8s` 8 | -------------------------------------------------------------------------------- /test/acceptance/server-test/annotations-overrides.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | server: 5 | annotations: | 6 | environment: production 7 | milk: oat 8 | myName: "{{ .Release.Name }}" 9 | service: 10 | annotations: 11 | active: sometimes 12 | pickMe: please 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .terraform/ 3 | .terraform.tfstate* 4 | terraform.tfstate* 5 | terraform.tfvars 6 | values.dev.yaml 7 | vaul-helm-dev-creds.json 8 | ./test/acceptance/vaul-helm-dev-creds.json 9 | ./test/terraform/vaul-helm-dev-creds.json 10 | ./test/unit/vaul-helm-dev-creds.json 11 | ./test/acceptance/values.yaml 12 | ./test/acceptance/values.yml 13 | .idea 14 | scratch/ 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "daily" 8 | labels: ["dependencies"] 9 | groups: 10 | github-actions-breaking: 11 | update-types: 12 | - major 13 | github-actions-backward-compatible: 14 | update-types: 15 | - minor 16 | - patch 17 | -------------------------------------------------------------------------------- /templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 2 | Thank you for installing HashiCorp Vault! 3 | 4 | Now that you have deployed Vault, you should look over the docs on using 5 | Vault with Kubernetes available here: 6 | 7 | https://developer.hashicorp.com/vault/docs 8 | 9 | 10 | Your release is named {{ .Release.Name }}. To learn more about the release, try: 11 | 12 | $ helm status {{ .Release.Name }} 13 | $ helm get manifest {{ .Release.Name }} 14 | 15 | -------------------------------------------------------------------------------- /.github/workflows/actionlint.yml: -------------------------------------------------------------------------------- 1 | # If the repository is public, be sure to change to GitHub hosted runners 2 | name: Lint GitHub Actions Workflows 3 | on: 4 | push: 5 | paths: 6 | - .github/workflows/**.yml 7 | pull_request: 8 | paths: 9 | - .github/workflows/**.yml 10 | permissions: 11 | contents: read 12 | jobs: 13 | actionlint: 14 | uses: hashicorp/vault-workflows-common/.github/workflows/actionlint.yaml@main 15 | -------------------------------------------------------------------------------- /test/acceptance/csi-test/vault-kv-secretproviderclass.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | # The "Hello World" Vault SecretProviderClass 5 | apiVersion: secrets-store.csi.x-k8s.io/v1 6 | kind: SecretProviderClass 7 | metadata: 8 | name: vault-kv 9 | spec: 10 | provider: vault 11 | parameters: 12 | roleName: "kv-role" 13 | objects: | 14 | - objectName: "bar" 15 | secretPath: "secret/data/kv1" 16 | secretKey: "bar1" 17 | -------------------------------------------------------------------------------- /.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .terraform/ 9 | .bzr/ 10 | .bzrignore 11 | .hg/ 12 | .hgignore 13 | .svn/ 14 | # Common backup files 15 | *.swp 16 | *.bak 17 | *.tmp 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | 24 | # CI and test 25 | .circleci/ 26 | .github/ 27 | .gitlab-ci.yml 28 | test/ 29 | scratch/ 30 | -------------------------------------------------------------------------------- /test/acceptance/server-test/vault-telemetry.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | serverTelemetry: 5 | serviceMonitor: 6 | enabled: true 7 | interval: 15s 8 | tlsConfig: 9 | ca: 10 | secret: 11 | name: vault-metrics-client 12 | key: ca.crt 13 | authorization: 14 | credentials: 15 | name: vault-metrics-client 16 | key: token 17 | metricRelabelings: 18 | - sourceLabels: [cluster] 19 | targetLabel: vault_cluster 20 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## PCI review checklist 2 | 3 | 4 | 5 | - [ ] I have documented a clear reason for, and description of, the change I am making. 6 | 7 | - [ ] If applicable, I've documented a plan to revert these changes if they require more than reverting the pull request. 8 | 9 | - [ ] If applicable, I've documented the impact of any changes to security controls. 10 | 11 | Examples of changes to security controls include using new access control methods, adding or removing logging pipelines, etc. 12 | -------------------------------------------------------------------------------- /templates/injector-serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{- template "vault.injectorEnabled" . -}} 7 | {{- if .injectorEnabled -}} 8 | apiVersion: v1 9 | kind: ServiceAccount 10 | metadata: 11 | name: {{ template "vault.fullname" . }}-agent-injector 12 | namespace: {{ include "vault.namespace" . }} 13 | labels: 14 | app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector 15 | app.kubernetes.io/instance: {{ .Release.Name }} 16 | app.kubernetes.io/managed-by: {{ .Release.Service }} 17 | {{ template "injector.serviceAccount.annotations" . }} 18 | {{ end }} 19 | -------------------------------------------------------------------------------- /.github/workflows/jira.yaml: -------------------------------------------------------------------------------- 1 | name: Jira Sync 2 | on: 3 | issues: 4 | types: [opened, closed, deleted, reopened] 5 | pull_request_target: 6 | types: [opened, closed, reopened] 7 | issue_comment: # Also triggers when commenting on a PR from the conversation view 8 | types: [created] 9 | jobs: 10 | sync: 11 | uses: hashicorp/vault-workflows-common/.github/workflows/jira.yaml@main 12 | secrets: 13 | JIRA_SYNC_BASE_URL: ${{ secrets.JIRA_SYNC_BASE_URL }} 14 | JIRA_SYNC_USER_EMAIL: ${{ secrets.JIRA_SYNC_USER_EMAIL }} 15 | JIRA_SYNC_API_TOKEN: ${{ secrets.JIRA_SYNC_API_TOKEN }} 16 | with: 17 | teams-array: '["vault-eco-infra"]' 18 | -------------------------------------------------------------------------------- /templates/csi-clusterrole.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{- template "vault.csiEnabled" . -}} 7 | {{- if .csiEnabled -}} 8 | apiVersion: rbac.authorization.k8s.io/v1 9 | kind: ClusterRole 10 | metadata: 11 | name: {{ template "vault.fullname" . }}-csi-provider-clusterrole 12 | labels: 13 | app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider 14 | app.kubernetes.io/instance: {{ .Release.Name }} 15 | app.kubernetes.io/managed-by: {{ .Release.Service }} 16 | rules: 17 | - apiGroups: 18 | - "" 19 | resources: 20 | - serviceaccounts/token 21 | verbs: 22 | - create 23 | {{- end }} 24 | -------------------------------------------------------------------------------- /templates/injector-certs-secret.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{- template "vault.injectorEnabled" . -}} 7 | {{- if .injectorEnabled -}} 8 | {{- if and (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} 9 | apiVersion: v1 10 | kind: Secret 11 | metadata: 12 | name: vault-injector-certs 13 | namespace: {{ include "vault.namespace" . }} 14 | labels: 15 | app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector 16 | app.kubernetes.io/instance: {{ .Release.Name }} 17 | app.kubernetes.io/managed-by: {{ .Release.Service }} 18 | {{- end }} 19 | {{- end }} -------------------------------------------------------------------------------- /test/acceptance/server-test/vault-server.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | global: 5 | tlsDisable: false 6 | server: 7 | dev: 8 | enabled: true 9 | # >- to convert to a single line with no line breaks. 10 | extraArgs: >- 11 | -dev-tls 12 | -dev-tls-cert-dir=/var/run/tls 13 | -dev-tls-san=vault 14 | -dev-tls-san=$POD_IP 15 | extraEnvironmentVars: 16 | VAULT_CACERT: /var/run/tls/vault-ca.pem 17 | VAULT_LOCAL_CONFIG: '{"telemetry":{"prometheus_retention_time":"30s","disable_hostname":true}}' 18 | volumes: 19 | - name: tls 20 | emptyDir: {} 21 | volumeMounts: 22 | - mountPath: /var/run/tls 23 | name: tls 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /Chart.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | apiVersion: v2 5 | name: vault 6 | version: 0.31.0 7 | appVersion: 1.20.4 8 | kubeVersion: ">= 1.20.0-0" 9 | description: Official HashiCorp Vault Chart 10 | home: https://www.vaultproject.io 11 | icon: https://github.com/hashicorp/vault/raw/f22d202cde2018f9455dec755118a9b84586e082/Vault_PrimaryLogo_Black.png 12 | keywords: ["vault", "security", "encryption", "secrets", "management", "automation", "infrastructure"] 13 | sources: 14 | - https://github.com/hashicorp/vault 15 | - https://github.com/hashicorp/vault-helm 16 | - https://github.com/hashicorp/vault-k8s 17 | - https://github.com/hashicorp/vault-csi-provider 18 | annotations: 19 | charts.openshift.io/name: HashiCorp Vault 20 | -------------------------------------------------------------------------------- /.github/actions/setup-test-tools/action.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | name: Setup common testing tools 5 | description: Install bats and python-yq 6 | runs: 7 | using: "composite" 8 | steps: 9 | - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0 10 | with: 11 | node-version: '20' 12 | - run: sudo npm install -g bats@${BATS_VERSION} 13 | shell: bash 14 | env: 15 | BATS_VERSION: '1.12.0' 16 | - run: bats -v 17 | shell: bash 18 | - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 19 | with: 20 | python-version: '3.12' 21 | - run: pip install yq 22 | shell: bash 23 | permissions: 24 | contents: read 25 | -------------------------------------------------------------------------------- /templates/csi-serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{- template "vault.csiEnabled" . -}} 7 | {{- if .csiEnabled -}} 8 | apiVersion: v1 9 | kind: ServiceAccount 10 | metadata: 11 | name: {{ template "vault.fullname" . }}-csi-provider 12 | namespace: {{ include "vault.namespace" . }} 13 | labels: 14 | app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider 15 | app.kubernetes.io/instance: {{ .Release.Name }} 16 | app.kubernetes.io/managed-by: {{ .Release.Service }} 17 | {{- if .Values.csi.serviceAccount.extraLabels -}} 18 | {{- toYaml .Values.csi.serviceAccount.extraLabels | nindent 4 -}} 19 | {{- end -}} 20 | {{ template "csi.serviceAccount.annotations" . }} 21 | {{- end }} 22 | -------------------------------------------------------------------------------- /test/acceptance/csi-test/nginx.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | --- 5 | apiVersion: v1 6 | kind: ServiceAccount 7 | metadata: 8 | name: nginx 9 | --- 10 | kind: Pod 11 | apiVersion: v1 12 | metadata: 13 | name: nginx 14 | spec: 15 | terminationGracePeriodSeconds: 0 16 | serviceAccountName: nginx 17 | containers: 18 | - image: docker.mirror.hashicorp.services/nginx 19 | name: nginx 20 | volumeMounts: 21 | - name: secrets-store-inline 22 | mountPath: "/mnt/secrets-store" 23 | readOnly: true 24 | volumes: 25 | - name: secrets-store-inline 26 | csi: 27 | driver: secrets-store.csi.k8s.io 28 | readOnly: true 29 | volumeAttributes: 30 | secretProviderClass: "vault-kv" 31 | -------------------------------------------------------------------------------- /test/kind/config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | kind: Cluster 5 | apiVersion: kind.x-k8s.io/v1alpha4 6 | nodes: 7 | - role: control-plane 8 | - role: worker 9 | - role: worker 10 | - role: worker 11 | # These apiServer settings are included for running the CSI provider on K8s 12 | # prior to 1.21 13 | kubeadmConfigPatches: 14 | - | 15 | apiVersion: kubeadm.k8s.io/v1beta2 16 | kind: ClusterConfiguration 17 | metadata: 18 | name: config 19 | apiServer: 20 | extraArgs: 21 | "service-account-issuer": "https://kubernetes.default.svc.cluster.local" 22 | "service-account-signing-key-file": "/etc/kubernetes/pki/sa.key" 23 | "service-account-api-audiences": "https://kubernetes.default.svc.cluster.local" 24 | -------------------------------------------------------------------------------- /templates/server-serviceaccount-secret.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{ template "vault.serverServiceAccountSecretCreationEnabled" . }} 7 | {{- if .serverServiceAccountSecretCreationEnabled -}} 8 | apiVersion: v1 9 | kind: Secret 10 | metadata: 11 | name: {{ template "vault.serviceAccount.name" . }}-token 12 | namespace: {{ include "vault.namespace" . }} 13 | annotations: 14 | kubernetes.io/service-account.name: {{ template "vault.serviceAccount.name" . }} 15 | labels: 16 | helm.sh/chart: {{ include "vault.chart" . }} 17 | app.kubernetes.io/name: {{ include "vault.name" . }} 18 | app.kubernetes.io/instance: {{ .Release.Name }} 19 | app.kubernetes.io/managed-by: {{ .Release.Service }} 20 | type: kubernetes.io/service-account-token 21 | {{ end }} -------------------------------------------------------------------------------- /templates/server-psp-role.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{ template "vault.mode" . }} 7 | {{- if .serverEnabled -}} 8 | {{- if and (ne .mode "") (eq (.Values.global.psp.enable | toString) "true") }} 9 | apiVersion: rbac.authorization.k8s.io/v1 10 | kind: Role 11 | metadata: 12 | name: {{ template "vault.fullname" . }}-psp 13 | namespace: {{ include "vault.namespace" . }} 14 | labels: 15 | app.kubernetes.io/name: {{ include "vault.name" . }} 16 | app.kubernetes.io/instance: {{ .Release.Name }} 17 | app.kubernetes.io/managed-by: {{ .Release.Service }} 18 | rules: 19 | - apiGroups: ['policy'] 20 | resources: ['podsecuritypolicies'] 21 | verbs: ['use'] 22 | resourceNames: 23 | - {{ template "vault.fullname" . }} 24 | {{- end }} 25 | {{- end }} 26 | -------------------------------------------------------------------------------- /test/chart/_helpers.bash: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | # chart_dir returns the directory for the chart 5 | chart_dir() { 6 | echo "${BATS_TEST_DIRNAME}"/../.. 7 | } 8 | 9 | # check_result checks if the specified test passed 10 | # results schema example: 11 | # { 12 | # "check": "has-minkubeversion", 13 | # "type": "Mandatory", 14 | # "outcome": "PASS", 15 | # "reason": "Minimum Kubernetes version specified" 16 | # } 17 | check_result() { 18 | local -r var="$1" 19 | local -r check=$(jq -r ".results[] | select(.check==\"${var}\")" < "$VERIFY_OUTPUT") 20 | local -r outcome=$(jq -r .outcome <<< "$check") 21 | local -r reason=$(jq -r .reason <<< "$check") 22 | 23 | # print the reason if this fails 24 | echo "reason: ${reason}" 25 | 26 | [ "$outcome" = "PASS" ] 27 | } 28 | -------------------------------------------------------------------------------- /templates/injector-psp-role.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{- template "vault.injectorEnabled" . -}} 7 | {{- if .injectorEnabled -}} 8 | {{- if eq (.Values.global.psp.enable | toString) "true" }} 9 | apiVersion: rbac.authorization.k8s.io/v1 10 | kind: Role 11 | metadata: 12 | name: {{ template "vault.fullname" . }}-agent-injector-psp 13 | namespace: {{ include "vault.namespace" . }} 14 | labels: 15 | app.kubernetes.io/name: {{ include "vault.name" . }} 16 | app.kubernetes.io/instance: {{ .Release.Name }} 17 | app.kubernetes.io/managed-by: {{ .Release.Service }} 18 | rules: 19 | - apiGroups: ['policy'] 20 | resources: ['podsecuritypolicies'] 21 | verbs: ['use'] 22 | resourceNames: 23 | - {{ template "vault.fullname" . }}-agent-injector 24 | {{- end }} 25 | {{- end }} 26 | -------------------------------------------------------------------------------- /templates/server-serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{ template "vault.serverServiceAccountEnabled" . }} 7 | {{- if .serverServiceAccountEnabled -}} 8 | apiVersion: v1 9 | kind: ServiceAccount 10 | metadata: 11 | name: {{ template "vault.serviceAccount.name" . }} 12 | namespace: {{ include "vault.namespace" . }} 13 | labels: 14 | helm.sh/chart: {{ include "vault.chart" . }} 15 | app.kubernetes.io/name: {{ include "vault.name" . }} 16 | app.kubernetes.io/instance: {{ .Release.Name }} 17 | app.kubernetes.io/managed-by: {{ .Release.Service }} 18 | {{- if .Values.server.serviceAccount.extraLabels -}} 19 | {{- toYaml .Values.server.serviceAccount.extraLabels | nindent 4 -}} 20 | {{- end -}} 21 | {{ template "vault.serviceAccount.annotations" . }} 22 | {{ end }} 23 | -------------------------------------------------------------------------------- /templates/csi-clusterrolebinding.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{- template "vault.csiEnabled" . -}} 7 | {{- if .csiEnabled -}} 8 | apiVersion: rbac.authorization.k8s.io/v1 9 | kind: ClusterRoleBinding 10 | metadata: 11 | name: {{ template "vault.fullname" . }}-csi-provider-clusterrolebinding 12 | labels: 13 | app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider 14 | app.kubernetes.io/instance: {{ .Release.Name }} 15 | app.kubernetes.io/managed-by: {{ .Release.Service }} 16 | roleRef: 17 | apiGroup: rbac.authorization.k8s.io 18 | kind: ClusterRole 19 | name: {{ template "vault.fullname" . }}-csi-provider-clusterrole 20 | subjects: 21 | - kind: ServiceAccount 22 | name: {{ template "vault.fullname" . }}-csi-provider 23 | namespace: {{ include "vault.namespace" . }} 24 | {{- end }} 25 | -------------------------------------------------------------------------------- /templates/injector-clusterrolebinding.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{- template "vault.injectorEnabled" . -}} 7 | {{- if .injectorEnabled -}} 8 | apiVersion: rbac.authorization.k8s.io/v1 9 | kind: ClusterRoleBinding 10 | metadata: 11 | name: {{ template "vault.fullname" . }}-agent-injector-binding 12 | labels: 13 | app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector 14 | app.kubernetes.io/instance: {{ .Release.Name }} 15 | app.kubernetes.io/managed-by: {{ .Release.Service }} 16 | roleRef: 17 | apiGroup: rbac.authorization.k8s.io 18 | kind: ClusterRole 19 | name: {{ template "vault.fullname" . }}-agent-injector-clusterrole 20 | subjects: 21 | - kind: ServiceAccount 22 | name: {{ template "vault.fullname" . }}-agent-injector 23 | namespace: {{ include "vault.namespace" . }} 24 | {{ end }} 25 | -------------------------------------------------------------------------------- /templates/server-psp-rolebinding.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{ template "vault.mode" . }} 7 | {{- if .serverEnabled -}} 8 | {{- if and (ne .mode "") (eq (.Values.global.psp.enable | toString) "true") }} 9 | apiVersion: rbac.authorization.k8s.io/v1 10 | kind: RoleBinding 11 | metadata: 12 | name: {{ template "vault.fullname" . }}-psp 13 | namespace: {{ include "vault.namespace" . }} 14 | labels: 15 | app.kubernetes.io/name: {{ include "vault.name" . }} 16 | app.kubernetes.io/instance: {{ .Release.Name }} 17 | app.kubernetes.io/managed-by: {{ .Release.Service }} 18 | roleRef: 19 | kind: Role 20 | name: {{ template "vault.fullname" . }}-psp 21 | apiGroup: rbac.authorization.k8s.io 22 | subjects: 23 | - kind: ServiceAccount 24 | name: {{ template "vault.fullname" . }} 25 | {{- end }} 26 | {{- end }} 27 | -------------------------------------------------------------------------------- /templates/csi-rolebinding.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{- template "vault.csiEnabled" . -}} 7 | {{- if .csiEnabled -}} 8 | apiVersion: rbac.authorization.k8s.io/v1 9 | kind: RoleBinding 10 | metadata: 11 | name: {{ template "vault.fullname" . }}-csi-provider-rolebinding 12 | namespace: {{ include "vault.namespace" . }} 13 | labels: 14 | app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider 15 | app.kubernetes.io/instance: {{ .Release.Name }} 16 | app.kubernetes.io/managed-by: {{ .Release.Service }} 17 | roleRef: 18 | apiGroup: rbac.authorization.k8s.io 19 | kind: Role 20 | name: {{ template "vault.fullname" . }}-csi-provider-role 21 | subjects: 22 | - kind: ServiceAccount 23 | name: {{ template "vault.fullname" . }}-csi-provider 24 | namespace: {{ include "vault.namespace" . }} 25 | {{- end }} 26 | -------------------------------------------------------------------------------- /templates/server-discovery-role.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{ template "vault.mode" . }} 7 | {{- if .serverEnabled -}} 8 | {{- if eq .mode "ha" }} 9 | {{- if eq (.Values.server.serviceAccount.serviceDiscovery.enabled | toString) "true" }} 10 | apiVersion: rbac.authorization.k8s.io/v1 11 | kind: Role 12 | metadata: 13 | namespace: {{ include "vault.namespace" . }} 14 | name: {{ template "vault.fullname" . }}-discovery-role 15 | labels: 16 | helm.sh/chart: {{ include "vault.chart" . }} 17 | app.kubernetes.io/name: {{ include "vault.name" . }} 18 | app.kubernetes.io/instance: {{ .Release.Name }} 19 | app.kubernetes.io/managed-by: {{ .Release.Service }} 20 | rules: 21 | - apiGroups: [""] 22 | resources: ["pods"] 23 | verbs: ["get", "watch", "list", "update", "patch"] 24 | {{ end }} 25 | {{ end }} 26 | {{ end }} 27 | -------------------------------------------------------------------------------- /test/acceptance/helm-test.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "helm/test: running helm test" { 6 | cd `chart_dir` 7 | 8 | kubectl delete namespace acceptance --ignore-not-found=true 9 | kubectl create namespace acceptance 10 | kubectl config set-context --current --namespace=acceptance 11 | eval "${PRE_CHART_CMDS}" 12 | 13 | helm install "$(name_prefix)" . ${SET_CHART_VALUES} 14 | check_vault_versions "$(name_prefix)" 15 | wait_for_running "$(name_prefix)-0" 16 | 17 | helm test "$(name_prefix)" 18 | } 19 | 20 | # Clean up 21 | teardown() { 22 | if [[ ${CLEANUP:-true} == "true" ]] 23 | then 24 | echo "helm/pvc teardown" 25 | helm delete vault 26 | kubectl delete --all pvc 27 | kubectl delete namespace acceptance --ignore-not-found=true 28 | kubectl config unset contexts."$(kubectl config current-context)".namespace 29 | fi 30 | } 31 | -------------------------------------------------------------------------------- /templates/server-network-policy.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{- if eq (.Values.server.networkPolicy.enabled | toString) "true" }} 7 | apiVersion: networking.k8s.io/v1 8 | kind: NetworkPolicy 9 | metadata: 10 | name: {{ template "vault.fullname" . }} 11 | namespace: {{ include "vault.namespace" . }} 12 | labels: 13 | app.kubernetes.io/name: {{ template "vault.name" . }} 14 | app.kubernetes.io/instance: {{ .Release.Name }} 15 | spec: 16 | podSelector: 17 | matchLabels: 18 | app.kubernetes.io/name: {{ template "vault.name" . }} 19 | app.kubernetes.io/instance: {{ .Release.Name }} 20 | ingress: {{- toYaml .Values.server.networkPolicy.ingress | nindent 4 }} 21 | {{- if .Values.server.networkPolicy.egress }} 22 | egress: 23 | {{- toYaml .Values.server.networkPolicy.egress | nindent 4 }} 24 | {{ end }} 25 | {{ end }} 26 | -------------------------------------------------------------------------------- /templates/injector-psp-rolebinding.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{- template "vault.injectorEnabled" . -}} 7 | {{- if .injectorEnabled -}} 8 | {{- if eq (.Values.global.psp.enable | toString) "true" }} 9 | apiVersion: rbac.authorization.k8s.io/v1 10 | kind: RoleBinding 11 | metadata: 12 | name: {{ template "vault.fullname" . }}-agent-injector-psp 13 | namespace: {{ include "vault.namespace" . }} 14 | labels: 15 | app.kubernetes.io/name: {{ include "vault.name" . }} 16 | app.kubernetes.io/instance: {{ .Release.Name }} 17 | app.kubernetes.io/managed-by: {{ .Release.Service }} 18 | roleRef: 19 | kind: Role 20 | name: {{ template "vault.fullname" . }}-agent-injector-psp 21 | apiGroup: rbac.authorization.k8s.io 22 | subjects: 23 | - kind: ServiceAccount 24 | name: {{ template "vault.fullname" . }}-agent-injector 25 | {{- end }} 26 | {{- end }} -------------------------------------------------------------------------------- /test/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | variable "project" { 5 | default = "vault-helm-dev-246514" 6 | 7 | description = < 0' | tee /dev/stderr) 11 | [ "${actual}" = "false" ] 12 | } 13 | 14 | @test "csi/ClusterRole: enabled with csi.enabled" { 15 | cd `chart_dir` 16 | local actual=$(helm template \ 17 | --show-only templates/csi-clusterrole.yaml \ 18 | --set 'csi.enabled=true' \ 19 | . | tee /dev/stderr | 20 | yq 'length > 0' | tee /dev/stderr) 21 | [ "${actual}" = "true" ] 22 | } 23 | 24 | # ClusterRole name 25 | @test "csi/ClusterRole: name" { 26 | cd `chart_dir` 27 | local actual=$(helm template \ 28 | --show-only templates/csi-clusterrole.yaml \ 29 | --set "csi.enabled=true" \ 30 | . | tee /dev/stderr | 31 | yq -r '.metadata.name' | tee /dev/stderr) 32 | [ "${actual}" = "release-name-vault-csi-provider-clusterrole" ] 33 | } -------------------------------------------------------------------------------- /templates/injector-rolebinding.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{- template "vault.injectorEnabled" . -}} 7 | {{- if .injectorEnabled -}} 8 | {{- if and (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} 9 | apiVersion: rbac.authorization.k8s.io/v1 10 | kind: RoleBinding 11 | metadata: 12 | name: {{ template "vault.fullname" . }}-agent-injector-leader-elector-binding 13 | namespace: {{ include "vault.namespace" . }} 14 | labels: 15 | app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector 16 | app.kubernetes.io/instance: {{ .Release.Name }} 17 | app.kubernetes.io/managed-by: {{ .Release.Service }} 18 | roleRef: 19 | apiGroup: rbac.authorization.k8s.io 20 | kind: Role 21 | name: {{ template "vault.fullname" . }}-agent-injector-leader-elector-role 22 | subjects: 23 | - kind: ServiceAccount 24 | name: {{ template "vault.fullname" . }}-agent-injector 25 | namespace: {{ include "vault.namespace" . }} 26 | {{- end }} 27 | {{- end }} -------------------------------------------------------------------------------- /templates/csi-role.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{- template "vault.csiEnabled" . -}} 7 | {{- if .csiEnabled -}} 8 | apiVersion: rbac.authorization.k8s.io/v1 9 | kind: Role 10 | metadata: 11 | name: {{ template "vault.fullname" . }}-csi-provider-role 12 | namespace: {{ include "vault.namespace" . }} 13 | labels: 14 | app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider 15 | app.kubernetes.io/instance: {{ .Release.Name }} 16 | app.kubernetes.io/managed-by: {{ .Release.Service }} 17 | rules: 18 | - apiGroups: [""] 19 | resources: ["secrets"] 20 | verbs: ["get"] 21 | resourceNames: 22 | {{- if .Values.csi.hmacSecretName }} 23 | - {{ .Values.csi.hmacSecretName }} 24 | {{- else }} 25 | - {{ include "vault.name" . }}-csi-provider-hmac-key 26 | {{- end }} 27 | # 'create' permissions cannot be restricted by resource name: 28 | # https://kubernetes.io/docs/reference/access-authn-authz/rbac/#referring-to-resources 29 | - apiGroups: [""] 30 | resources: ["secrets"] 31 | verbs: ["create"] 32 | {{- end }} 33 | -------------------------------------------------------------------------------- /templates/server-disruptionbudget.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{ template "vault.mode" . }} 7 | {{- if ne .mode "external" -}} 8 | {{- if .serverEnabled -}} 9 | {{- if and (eq .mode "ha") (eq (.Values.server.ha.disruptionBudget.enabled | toString) "true") -}} 10 | # PodDisruptionBudget to prevent degrading the server cluster through 11 | # voluntary cluster changes. 12 | apiVersion: policy/v1 13 | kind: PodDisruptionBudget 14 | metadata: 15 | name: {{ template "vault.fullname" . }} 16 | namespace: {{ include "vault.namespace" . }} 17 | labels: 18 | helm.sh/chart: {{ include "vault.chart" . }} 19 | app.kubernetes.io/name: {{ include "vault.name" . }} 20 | app.kubernetes.io/instance: {{ .Release.Name }} 21 | app.kubernetes.io/managed-by: {{ .Release.Service }} 22 | spec: 23 | maxUnavailable: {{ template "vault.pdb.maxUnavailable" . }} 24 | selector: 25 | matchLabels: 26 | app.kubernetes.io/name: {{ include "vault.name" . }} 27 | app.kubernetes.io/instance: {{ .Release.Name }} 28 | component: server 29 | {{- end -}} 30 | {{- end -}} 31 | {{- end -}} 32 | -------------------------------------------------------------------------------- /templates/injector-role.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{- template "vault.injectorEnabled" . -}} 7 | {{- if .injectorEnabled -}} 8 | {{- if and (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} 9 | apiVersion: rbac.authorization.k8s.io/v1 10 | kind: Role 11 | metadata: 12 | name: {{ template "vault.fullname" . }}-agent-injector-leader-elector-role 13 | namespace: {{ include "vault.namespace" . }} 14 | labels: 15 | app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector 16 | app.kubernetes.io/instance: {{ .Release.Name }} 17 | app.kubernetes.io/managed-by: {{ .Release.Service }} 18 | rules: 19 | - apiGroups: [""] 20 | resources: ["secrets", "configmaps"] 21 | verbs: 22 | - "create" 23 | - "get" 24 | - "watch" 25 | - "list" 26 | - "update" 27 | - apiGroups: [""] 28 | resources: ["configmaps"] 29 | verbs: 30 | - "delete" 31 | - apiGroups: [""] 32 | resources: ["pods"] 33 | verbs: 34 | - "get" 35 | - "patch" 36 | - "delete" 37 | {{- end }} 38 | {{- end }} 39 | -------------------------------------------------------------------------------- /templates/prometheus-prometheusrules.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{ if and (.Values.serverTelemetry.prometheusRules.rules) 7 | (or (.Values.global.serverTelemetry.prometheusOperator) (.Values.serverTelemetry.prometheusRules.enabled) ) 8 | }} 9 | --- 10 | apiVersion: monitoring.coreos.com/v1 11 | kind: PrometheusRule 12 | metadata: 13 | name: {{ template "vault.fullname" . }} 14 | labels: 15 | helm.sh/chart: {{ include "vault.chart" . }} 16 | app.kubernetes.io/name: {{ include "vault.name" . }} 17 | app.kubernetes.io/instance: {{ .Release.Name }} 18 | app.kubernetes.io/managed-by: {{ .Release.Service }} 19 | {{- /* update the selectors docs in values.yaml whenever the defaults below change. */ -}} 20 | {{- $selectors := .Values.serverTelemetry.prometheusRules.selectors }} 21 | {{- if $selectors }} 22 | {{- toYaml $selectors | nindent 4 }} 23 | {{- else }} 24 | release: prometheus 25 | {{- end }} 26 | spec: 27 | groups: 28 | - name: {{ include "vault.fullname" . }} 29 | rules: 30 | {{- toYaml .Values.serverTelemetry.prometheusRules.rules | nindent 6 }} 31 | {{- end }} 32 | -------------------------------------------------------------------------------- /templates/csi-agent-configmap.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{- template "vault.csiEnabled" . -}} 7 | {{- if and (.csiEnabled) (eq (.Values.csi.agent.enabled | toString) "true") -}} 8 | apiVersion: v1 9 | kind: ConfigMap 10 | metadata: 11 | name: {{ template "vault.fullname" . }}-csi-provider-agent-config 12 | namespace: {{ include "vault.namespace" . }} 13 | labels: 14 | helm.sh/chart: {{ include "vault.chart" . }} 15 | app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider 16 | app.kubernetes.io/instance: {{ .Release.Name }} 17 | app.kubernetes.io/managed-by: {{ .Release.Service }} 18 | data: 19 | config.hcl: | 20 | vault { 21 | {{- if .Values.global.externalVaultAddr }} 22 | "address" = "{{ .Values.global.externalVaultAddr }}" 23 | {{- else }} 24 | "address" = "{{ include "vault.scheme" . }}://{{ template "vault.fullname" . }}.{{ include "vault.namespace" . }}.svc:{{ .Values.server.service.port }}" 25 | {{- end }} 26 | } 27 | 28 | cache {} 29 | 30 | listener "unix" { 31 | address = "/var/run/vault/agent.sock" 32 | tls_disable = true 33 | } 34 | {{- end }} 35 | -------------------------------------------------------------------------------- /templates/server-discovery-rolebinding.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{ template "vault.mode" . }} 7 | {{- if .serverEnabled -}} 8 | {{- if eq .mode "ha" }} 9 | {{- if eq (.Values.server.serviceAccount.serviceDiscovery.enabled | toString) "true" }} 10 | {{- if .Capabilities.APIVersions.Has "rbac.authorization.k8s.io/v1" -}} 11 | apiVersion: rbac.authorization.k8s.io/v1 12 | {{- else }} 13 | apiVersion: rbac.authorization.k8s.io/v1beta1 14 | {{- end }} 15 | kind: RoleBinding 16 | metadata: 17 | name: {{ template "vault.fullname" . }}-discovery-rolebinding 18 | namespace: {{ include "vault.namespace" . }} 19 | labels: 20 | helm.sh/chart: {{ include "vault.chart" . }} 21 | app.kubernetes.io/name: {{ include "vault.name" . }} 22 | app.kubernetes.io/instance: {{ .Release.Name }} 23 | app.kubernetes.io/managed-by: {{ .Release.Service }} 24 | roleRef: 25 | apiGroup: rbac.authorization.k8s.io 26 | kind: Role 27 | name: {{ template "vault.fullname" . }}-discovery-role 28 | subjects: 29 | - kind: ServiceAccount 30 | name: {{ template "vault.serviceAccount.name" . }} 31 | namespace: {{ include "vault.namespace" . }} 32 | {{ end }} 33 | {{ end }} 34 | {{ end }} 35 | -------------------------------------------------------------------------------- /templates/server-route.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{- if .Values.global.openshift }} 7 | {{- if ne .mode "external" }} 8 | {{- if .Values.server.route.enabled -}} 9 | {{- $serviceName := include "vault.fullname" . -}} 10 | {{- if and (eq .mode "ha" ) (eq (.Values.server.route.activeService | toString) "true") }} 11 | {{- $serviceName = printf "%s-%s" $serviceName "active" -}} 12 | {{- end }} 13 | kind: Route 14 | apiVersion: route.openshift.io/v1 15 | metadata: 16 | name: {{ template "vault.fullname" . }} 17 | namespace: {{ include "vault.namespace" . }} 18 | labels: 19 | helm.sh/chart: {{ include "vault.chart" . }} 20 | app.kubernetes.io/name: {{ include "vault.name" . }} 21 | app.kubernetes.io/instance: {{ .Release.Name }} 22 | app.kubernetes.io/managed-by: {{ .Release.Service }} 23 | {{- with .Values.server.route.labels }} 24 | {{- toYaml . | nindent 4 }} 25 | {{- end }} 26 | {{- template "vault.route.annotations" . }} 27 | spec: 28 | host: {{ .Values.server.route.host }} 29 | to: 30 | kind: Service 31 | name: {{ $serviceName }} 32 | weight: 100 33 | port: 34 | targetPort: 8200 35 | tls: 36 | {{- toYaml .Values.server.route.tls | nindent 4 }} 37 | {{- end }} 38 | {{- end }} 39 | {{- end }} 40 | -------------------------------------------------------------------------------- /test/acceptance/injector-test/job.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | --- 5 | apiVersion: v1 6 | kind: ServiceAccount 7 | metadata: 8 | name: pgdump 9 | labels: 10 | app: pgdump 11 | --- 12 | apiVersion: batch/v1 13 | kind: Job 14 | metadata: 15 | name: pgdump 16 | spec: 17 | backoffLimit: 0 18 | template: 19 | metadata: 20 | name: pgdump 21 | labels: 22 | app: pgdump 23 | annotations: 24 | vault.hashicorp.com/agent-inject: "true" 25 | vault.hashicorp.com/agent-inject-secret-db-creds: "database/creds/db-backup" 26 | vault.hashicorp.com/agent-inject-template-db-creds: | 27 | {{- with secret "database/creds/db-backup" -}} 28 | postgresql://{{ .Data.username }}:{{ .Data.password }}@postgres.acceptance.svc.cluster.local:5432/mydb 29 | {{- end }} 30 | vault.hashicorp.com/role: "db-backup" 31 | vault.hashicorp.com/agent-pre-populate-only: "true" 32 | spec: 33 | serviceAccountName: pgdump 34 | containers: 35 | - name: pgdump 36 | image: postgres:11.5 37 | command: 38 | - "/bin/sh" 39 | - "-ec" 40 | args: 41 | - "/usr/bin/pg_dump $(cat /vault/secrets/db-creds) --no-owner > /dev/stdout" 42 | restartPolicy: Never 43 | -------------------------------------------------------------------------------- /test/unit/csi-rolebinding.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "csi/RoleBinding: disabled by default" { 6 | cd `chart_dir` 7 | local actual=$( (helm template \ 8 | --show-only templates/csi-rolebinding.yaml \ 9 | . || echo "---") | tee /dev/stderr | 10 | yq 'length > 0' | tee /dev/stderr) 11 | [ "${actual}" = "false" ] 12 | } 13 | 14 | @test "csi/RoleBinding: name" { 15 | cd `chart_dir` 16 | local actual=$(helm template \ 17 | --show-only templates/csi-rolebinding.yaml \ 18 | --set "csi.enabled=true" \ 19 | . | tee /dev/stderr | 20 | yq -r '.metadata.name' | tee /dev/stderr) 21 | [ "${actual}" = "release-name-vault-csi-provider-rolebinding" ] 22 | } 23 | 24 | @test "csi/RoleBinding: namespace" { 25 | cd `chart_dir` 26 | local actual=$(helm template \ 27 | --show-only templates/csi-rolebinding.yaml \ 28 | --set "csi.enabled=true" \ 29 | --namespace foo \ 30 | . | tee /dev/stderr | 31 | yq -r '.metadata.namespace' | tee /dev/stderr) 32 | [ "${actual}" = "foo" ] 33 | local actual=$(helm template \ 34 | --show-only templates/csi-rolebinding.yaml \ 35 | --set "csi.enabled=true" \ 36 | --set 'global.namespace=bar' \ 37 | --namespace foo \ 38 | . | tee /dev/stderr | 39 | yq -r '.metadata.namespace' | tee /dev/stderr) 40 | [ "${actual}" = "bar" ] 41 | } -------------------------------------------------------------------------------- /test/unit/injector-clusterrolebinding.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "injector/ClusterRoleBinding: enabled by default" { 6 | cd `chart_dir` 7 | local actual=$(helm template \ 8 | --show-only templates/injector-clusterrolebinding.yaml \ 9 | . | tee /dev/stderr | 10 | yq 'length > 0' | tee /dev/stderr) 11 | [ "${actual}" = "true" ] 12 | } 13 | 14 | @test "injector/ClusterRoleBinding: disable with global.enabled" { 15 | cd `chart_dir` 16 | local actual=$( (helm template \ 17 | --show-only templates/injector-clusterrolebinding.yaml \ 18 | --set 'global.enabled=false' \ 19 | . || echo "---") | tee /dev/stderr | 20 | yq 'length > 0' | tee /dev/stderr) 21 | [ "${actual}" = "false" ] 22 | } 23 | 24 | @test "injector/ClusterRoleBinding: service account namespace" { 25 | cd `chart_dir` 26 | local actual=$(helm template \ 27 | --show-only templates/injector-clusterrolebinding.yaml \ 28 | --set "injector.enabled=true" \ 29 | --namespace foo \ 30 | . | tee /dev/stderr | 31 | yq -r '.subjects[0].namespace' | tee /dev/stderr) 32 | [ "${actual}" = "foo" ] 33 | local actual=$(helm template \ 34 | --show-only templates/injector-clusterrolebinding.yaml \ 35 | --set "injector.enabled=true" \ 36 | --set 'global.namespace=bar' \ 37 | --namespace foo \ 38 | . | tee /dev/stderr | 39 | yq -r '.subjects[0].namespace' | tee /dev/stderr) 40 | [ "${actual}" = "bar" ] 41 | } -------------------------------------------------------------------------------- /templates/injector-psp.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{- template "vault.injectorEnabled" . -}} 7 | {{- if .injectorEnabled -}} 8 | {{- if eq (.Values.global.psp.enable | toString) "true" }} 9 | apiVersion: policy/v1beta1 10 | kind: PodSecurityPolicy 11 | metadata: 12 | name: {{ template "vault.fullname" . }}-agent-injector 13 | labels: 14 | app.kubernetes.io/name: {{ include "vault.name" . }} 15 | app.kubernetes.io/instance: {{ .Release.Name }} 16 | app.kubernetes.io/managed-by: {{ .Release.Service }} 17 | {{- template "vault.psp.annotations" . }} 18 | spec: 19 | privileged: false 20 | # Required to prevent escalations to root. 21 | allowPrivilegeEscalation: false 22 | volumes: 23 | - configMap 24 | - emptyDir 25 | - projected 26 | - secret 27 | - downwardAPI 28 | hostNetwork: false 29 | hostIPC: false 30 | hostPID: false 31 | runAsUser: 32 | # Require the container to run without root privileges. 33 | rule: MustRunAsNonRoot 34 | seLinux: 35 | # This policy assumes the nodes are using AppArmor rather than SELinux. 36 | rule: RunAsAny 37 | supplementalGroups: 38 | rule: MustRunAs 39 | ranges: 40 | # Forbid adding the root group. 41 | - min: 1 42 | max: 65535 43 | fsGroup: 44 | rule: MustRunAs 45 | ranges: 46 | # Forbid adding the root group. 47 | - min: 1 48 | max: 65535 49 | readOnlyRootFilesystem: false 50 | {{- end }} 51 | {{- end }} -------------------------------------------------------------------------------- /test/unit/schema.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | # These tests are just to verify there is a schema file used in the chart. Since 6 | # .enabled is defined as a boolean type for each of the top-level blocks in the 7 | # schema, setting it as a string fails 'helm template'. 8 | @test "schema: csi enabled datatype" { 9 | cd "$(chart_dir)" 10 | run helm template . --set csi.enabled="123" 11 | [ "$status" -eq 1 ] 12 | [ "${lines[2]}" = "- at '/csi/enabled': got number, want boolean or string" ] 13 | 14 | run helm template . --set csi.enabled=true 15 | [ "$status" -eq 0 ] 16 | } 17 | 18 | @test "schema: injector enabled datatype" { 19 | cd "$(chart_dir)" 20 | run helm template . --set injector.enabled="123" 21 | [ "$status" -eq 1 ] 22 | [ "${lines[2]}" = "- at '/injector/enabled': got number, want boolean or string" ] 23 | 24 | run helm template . --set injector.enabled=true 25 | [ "$status" -eq 0 ] 26 | } 27 | 28 | @test "schema: server enabled datatype" { 29 | cd "$(chart_dir)" 30 | run helm template . --set server.enabled="123" 31 | [ "$status" -eq 1 ] 32 | [ "${lines[2]}" = "- at '/server/enabled': got number, want boolean or string" ] 33 | 34 | run helm template . --set server.enabled=true 35 | [ "$status" -eq 0 ] 36 | } 37 | 38 | @test "schema: ui enabled datatype" { 39 | cd "$(chart_dir)" 40 | run helm template . --set ui.enabled="123" 41 | [ "$status" -eq 1 ] 42 | [ "${lines[2]}" = "- at '/ui/enabled': got number, want boolean or string" ] 43 | 44 | run helm template . --set ui.enabled=true 45 | [ "$status" -eq 0 ] 46 | } 47 | -------------------------------------------------------------------------------- /templates/server-psp.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{ template "vault.mode" . }} 7 | {{- if .serverEnabled -}} 8 | {{- if and (ne .mode "") (eq (.Values.global.psp.enable | toString) "true") }} 9 | apiVersion: policy/v1beta1 10 | kind: PodSecurityPolicy 11 | metadata: 12 | name: {{ template "vault.fullname" . }} 13 | labels: 14 | app.kubernetes.io/name: {{ include "vault.name" . }} 15 | app.kubernetes.io/instance: {{ .Release.Name }} 16 | app.kubernetes.io/managed-by: {{ .Release.Service }} 17 | {{- template "vault.psp.annotations" . }} 18 | spec: 19 | privileged: false 20 | # Required to prevent escalations to root. 21 | allowPrivilegeEscalation: false 22 | volumes: 23 | - configMap 24 | - emptyDir 25 | - projected 26 | - secret 27 | - downwardAPI 28 | {{- if eq (.Values.server.dataStorage.enabled | toString) "true" }} 29 | - persistentVolumeClaim 30 | {{- end }} 31 | hostNetwork: false 32 | hostIPC: false 33 | hostPID: false 34 | runAsUser: 35 | # Require the container to run without root privileges. 36 | rule: MustRunAsNonRoot 37 | seLinux: 38 | # This policy assumes the nodes are using AppArmor rather than SELinux. 39 | rule: RunAsAny 40 | supplementalGroups: 41 | rule: MustRunAs 42 | ranges: 43 | # Forbid adding the root group. 44 | - min: 1 45 | max: 65535 46 | fsGroup: 47 | rule: MustRunAs 48 | ranges: 49 | # Forbid adding the root group. 50 | - min: 1 51 | max: 65535 52 | readOnlyRootFilesystem: false 53 | {{- end }} 54 | {{- end }} 55 | -------------------------------------------------------------------------------- /.github/workflows/update-helm-charts-index.yml: -------------------------------------------------------------------------------- 1 | name: update-helm-charts-index 2 | on: 3 | push: 4 | tags: 5 | - 'v[0-9]+.[0-9]+.[0-9]+' 6 | 7 | permissions: 8 | contents: read 9 | 10 | jobs: 11 | update-helm-charts-index: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 15 | - name: verify Chart version matches tag version 16 | run: |- 17 | export TAG=${{ github.ref_name }} 18 | git_tag="${TAG#v}" 19 | chart_tag=$(yq -r '.version' Chart.yaml) 20 | if [ "${git_tag}" != "${chart_tag}" ]; then 21 | echo "chart version (${chart_tag}) did not match git version (${git_tag})" 22 | exit 1 23 | fi 24 | - name: update helm-charts index 25 | id: update 26 | env: 27 | GH_TOKEN: ${{ secrets.HELM_CHARTS_GITHUB_TOKEN }} 28 | run: |- 29 | gh workflow run publish-charts.yml \ 30 | --repo hashicorp/helm-charts \ 31 | --ref main \ 32 | -f SOURCE_TAG="${{ github.ref_name }}" \ 33 | -f SOURCE_REPO="${{ github.repository }}" 34 | - uses: hashicorp/actions-slack-status@v2 35 | if: ${{always()}} 36 | with: 37 | success-message: "vault-helm charts index update triggered successfully. View the run ." 38 | failure-message: "vault-helm charts index update trigger failed." 39 | status: ${{job.status}} 40 | slack-webhook-url: ${{secrets.SLACK_WEBHOOK_URL}} 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Let us know about a bug! 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 17 | 18 | **Describe the bug** 19 | A clear and concise description of what the bug is. 20 | 21 | **To Reproduce** 22 | Steps to reproduce the behavior: 23 | 1. Install chart 24 | 2. Run vault command 25 | 3. See error (vault logs, etc.) 26 | 27 | Other useful info to include: vault pod logs, `kubectl describe statefulset vault` and `kubectl get statefulset vault -o yaml` output 28 | 29 | **Expected behavior** 30 | A clear and concise description of what you expected to happen. 31 | 32 | **Environment** 33 | * Kubernetes version: 34 | * Distribution or cloud vendor (OpenShift, EKS, GKE, AKS, etc.): 35 | * Other configuration options or runtime services (istio, etc.): 36 | * vault-helm version: 37 | 38 | Chart values: 39 | 40 | ```yaml 41 | # Paste your user-supplied values here (`helm get values `). 42 | # Be sure to scrub any sensitive values! 43 | ``` 44 | 45 | **Additional context** 46 | Add any other context about the problem here. 47 | -------------------------------------------------------------------------------- /test/acceptance/injector-leader-elector.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "injector: testing leader elector" { 6 | cd `chart_dir` 7 | 8 | eval "${PRE_CHART_CMDS}" 9 | helm install "$(name_prefix)" . \ 10 | --wait \ 11 | --timeout=5m \ 12 | --set="injector.replicas=3" \ 13 | ${SET_CHART_VALUES} 14 | check_vault_versions "$(name_prefix)" 15 | kubectl wait --for condition=Ready pod -l app.kubernetes.io/name=vault-agent-injector --timeout=5m 16 | 17 | pods=($(kubectl get pods -l app.kubernetes.io/name=vault-agent-injector -o json | jq -r '.items[] | .metadata.name')) 18 | [ "${#pods[@]}" == 3 ] 19 | 20 | leader='' 21 | tries=0 22 | until [ $tries -ge 60 ] 23 | do 24 | owner=$(kubectl get configmaps vault-k8s-leader -o json | jq -r .metadata.ownerReferences\[0\].name) 25 | leader=$(kubectl get pods $owner -o json | jq -r .metadata.name) 26 | [ -n "${leader}" ] && [ "${leader}" != "null" ] && break 27 | ((++tries)) 28 | sleep .5 29 | done 30 | 31 | # Check the leader name is valid - i.e. one of the 3 pods 32 | [[ " ${pods[@]} " =~ " ${leader} " ]] 33 | 34 | } 35 | 36 | setup() { 37 | kubectl delete namespace acceptance --ignore-not-found=true 38 | kubectl create namespace acceptance 39 | kubectl config set-context --current --namespace=acceptance 40 | } 41 | 42 | # Clean up 43 | teardown() { 44 | if [[ ${CLEANUP:-true} == "true" ]] 45 | then 46 | echo "helm/pvc teardown" 47 | helm delete vault 48 | kubectl delete --all pvc 49 | kubectl delete namespace acceptance 50 | kubectl config unset contexts."$(kubectl config current-context)".namespace 51 | fi 52 | } 53 | -------------------------------------------------------------------------------- /templates/server-headless-service.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{ template "vault.mode" . }} 7 | {{- if ne .mode "external" }} 8 | {{- template "vault.serverServiceEnabled" . -}} 9 | {{- if .serverServiceEnabled -}} 10 | # Service for Vault cluster 11 | apiVersion: v1 12 | kind: Service 13 | metadata: 14 | name: {{ template "vault.fullname" . }}-internal 15 | namespace: {{ include "vault.namespace" . }} 16 | labels: 17 | helm.sh/chart: {{ include "vault.chart" . }} 18 | app.kubernetes.io/name: {{ include "vault.name" . }} 19 | app.kubernetes.io/instance: {{ .Release.Name }} 20 | app.kubernetes.io/managed-by: {{ .Release.Service }} 21 | vault-internal: "true" 22 | annotations: 23 | {{ template "vault.service.annotations" .}} 24 | spec: 25 | {{- if (semverCompare ">= 1.23-0" .Capabilities.KubeVersion.Version) }} 26 | {{- if .Values.server.service.ipFamilyPolicy }} 27 | ipFamilyPolicy: {{ .Values.server.service.ipFamilyPolicy }} 28 | {{- end }} 29 | {{- if .Values.server.service.ipFamilies }} 30 | ipFamilies: {{ .Values.server.service.ipFamilies | toYaml | nindent 2 }} 31 | {{- end }} 32 | {{- end }} 33 | clusterIP: None 34 | publishNotReadyAddresses: true 35 | ports: 36 | - name: "{{ include "vault.scheme" . }}" 37 | port: {{ .Values.server.service.port }} 38 | targetPort: {{ .Values.server.service.targetPort }} 39 | - name: https-internal 40 | port: 8201 41 | targetPort: 8201 42 | selector: 43 | app.kubernetes.io/name: {{ include "vault.name" . }} 44 | app.kubernetes.io/instance: {{ .Release.Name }} 45 | component: server 46 | {{- end }} 47 | {{- end }} 48 | -------------------------------------------------------------------------------- /test/unit/injector-serviceaccount.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "injector/ServiceAccount: enabled by default" { 6 | cd `chart_dir` 7 | local actual=$(helm template \ 8 | --show-only templates/injector-serviceaccount.yaml \ 9 | . | tee /dev/stderr | 10 | yq 'length > 0' | tee /dev/stderr) 11 | [ "${actual}" = "true" ] 12 | } 13 | 14 | @test "injector/ServiceAccount: disable with global.enabled" { 15 | cd `chart_dir` 16 | local actual=$( (helm template \ 17 | --show-only templates/injector-serviceaccount.yaml \ 18 | --set 'global.enabled=false' \ 19 | . || echo "---") | tee /dev/stderr | 20 | yq 'length > 0' | tee /dev/stderr) 21 | [ "${actual}" = "false" ] 22 | } 23 | 24 | @test "injector/ServiceAccount: namespace" { 25 | cd `chart_dir` 26 | local actual=$(helm template \ 27 | --show-only templates/injector-serviceaccount.yaml \ 28 | --namespace foo \ 29 | . | tee /dev/stderr | 30 | yq -r '.metadata.namespace' | tee /dev/stderr) 31 | [ "${actual}" = "foo" ] 32 | local actual=$(helm template \ 33 | --show-only templates/injector-serviceaccount.yaml \ 34 | --set 'global.namespace=bar' \ 35 | --namespace foo \ 36 | . | tee /dev/stderr | 37 | yq -r '.metadata.namespace' | tee /dev/stderr) 38 | [ "${actual}" = "bar" ] 39 | } 40 | 41 | @test "injector/ServiceAccount: generic annotations" { 42 | cd `chart_dir` 43 | local actual=$(helm template \ 44 | --show-only templates/injector-serviceaccount.yaml \ 45 | --set 'injector.serviceAccount.annotations=vaultIsAwesome: true' \ 46 | . | tee /dev/stderr | 47 | yq -r '.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) 48 | [ "${actual}" = "true" ] 49 | } 50 | -------------------------------------------------------------------------------- /test/acceptance/injector-test/pg-deployment.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | --- 5 | apiVersion: v1 6 | kind: Service 7 | metadata: 8 | name: postgres 9 | labels: 10 | app: postgres 11 | spec: 12 | type: ClusterIP 13 | ports: 14 | - port: 5432 15 | targetPort: 5432 16 | selector: 17 | app: postgres 18 | --- 19 | apiVersion: apps/v1 20 | kind: Deployment 21 | metadata: 22 | name: postgres 23 | spec: 24 | replicas: 1 25 | selector: 26 | matchLabels: 27 | app: postgres 28 | template: 29 | metadata: 30 | labels: 31 | service: postgres 32 | app: postgres 33 | spec: 34 | containers: 35 | - name: postgres 36 | image: postgres:11.5 37 | ports: 38 | - containerPort: 5432 39 | env: 40 | - name: POSTGRES_DB 41 | value: mydb 42 | - name: POSTGRES_USER 43 | value: postgres 44 | - name: POSTGRES_PASSWORD 45 | value: password 46 | volumeMounts: 47 | - mountPath: "/var/lib/postgresql" 48 | name: "pgdata" 49 | - mountPath: "/docker-entrypoint-initdb.d" 50 | name: "pgconf" 51 | volumes: 52 | - name: pgdata 53 | emptyDir: {} 54 | - name: pgconf 55 | configMap: 56 | name: "pg-init" 57 | --- 58 | apiVersion: v1 59 | kind: ConfigMap 60 | metadata: 61 | name: pg-init 62 | labels: 63 | app: postgres 64 | data: 65 | setup.sql: | 66 | CREATE ROLE vault; 67 | ALTER ROLE vault WITH SUPERUSER LOGIN PASSWORD 'vault'; 68 | 69 | \c mydb 70 | CREATE SCHEMA app; 71 | CREATE TABLE app.inventory(id int); 72 | INSERT INTO app.inventory(id) VALUES (0); 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vault Helm Chart 2 | 3 | > :warning: **Please note**: We take Vault's security and our users' trust very seriously. If 4 | you believe you have found a security issue in Vault Helm, _please responsibly disclose_ 5 | by contacting us at [security@hashicorp.com](mailto:security@hashicorp.com). 6 | 7 | This repository contains the official HashiCorp Helm chart for installing 8 | and configuring Vault on Kubernetes. This chart supports multiple use 9 | cases of Vault on Kubernetes depending on the values provided. 10 | 11 | For full documentation on this Helm chart along with all the ways you can 12 | use Vault with Kubernetes, please see the 13 | [Vault and Kubernetes documentation](https://developer.hashicorp.com/vault/docs/platform/k8s). 14 | 15 | ## Prerequisites 16 | 17 | To use the charts here, [Helm](https://helm.sh/) must be configured for your 18 | Kubernetes cluster. Setting up Kubernetes and Helm is outside the scope of 19 | this README. Please refer to the Kubernetes and Helm documentation. 20 | 21 | The versions required are: 22 | 23 | * **Helm 3.6+** 24 | * **Kubernetes 1.29+** - This is the earliest version of Kubernetes tested. 25 | It is possible that this chart works with earlier versions but it is 26 | untested. 27 | 28 | ## Usage 29 | 30 | To install the latest version of this chart, add the Hashicorp helm repository 31 | and run `helm install`: 32 | 33 | ```console 34 | $ helm repo add hashicorp https://helm.releases.hashicorp.com 35 | "hashicorp" has been added to your repositories 36 | 37 | $ helm install vault hashicorp/vault 38 | ``` 39 | 40 | Please see the many options supported in the `values.yaml` file. These are also 41 | fully documented directly on the [Vault 42 | website](https://developer.hashicorp.com/vault/docs/platform/k8s/helm) along with more 43 | detailed installation instructions. 44 | -------------------------------------------------------------------------------- /test/acceptance/server-annotations.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "server/annotations: testing yaml and yaml-formatted string formats" { 6 | cd `chart_dir` 7 | kubectl delete namespace acceptance --ignore-not-found=true 8 | kubectl create namespace acceptance 9 | kubectl config set-context --current --namespace=acceptance 10 | 11 | eval "${PRE_CHART_CMDS}" 12 | helm install "$(name_prefix)" -f ./test/acceptance/server-test/annotations-overrides.yaml . ${SET_CHART_VALUES} 13 | check_vault_versions "$(name_prefix)" 14 | wait_for_running $(name_prefix)-0 15 | 16 | # service annotations 17 | local awesome=$(kubectl get service "$(name_prefix)" --output json | 18 | jq -r '.metadata.annotations.active') 19 | [ "${awesome}" == "sometimes" ] 20 | 21 | local pickMe=$(kubectl get service "$(name_prefix)" --output json | 22 | jq -r '.metadata.annotations.pickMe') 23 | [ "${pickMe}" == "please" ] 24 | 25 | local environment=$(kubectl get statefulset "$(name_prefix)" --output json | 26 | jq -r '.spec.template.metadata.annotations.environment') 27 | [ "${environment}" == "production" ] 28 | 29 | local milk=$(kubectl get statefulset "$(name_prefix)" --output json | 30 | jq -r '.spec.template.metadata.annotations.milk') 31 | [ "${milk}" == "oat" ] 32 | 33 | local myName=$(kubectl get statefulset "$(name_prefix)" --output json | 34 | jq -r '.spec.template.metadata.annotations.myName') 35 | [ "${myName}" == "$(name_prefix)" ] 36 | 37 | } 38 | 39 | # Clean up 40 | teardown() { 41 | if [[ ${CLEANUP:-true} == "true" ]] 42 | then 43 | echo "helm/pvc teardown" 44 | helm delete $(name_prefix) 45 | kubectl delete --all pvc 46 | kubectl delete namespace acceptance --ignore-not-found=true 47 | kubectl config unset contexts."$(kubectl config current-context)".namespace 48 | fi 49 | } 50 | -------------------------------------------------------------------------------- /templates/ui-service.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{ template "vault.mode" . }} 7 | {{- if ne .mode "external" }} 8 | {{- template "vault.uiEnabled" . -}} 9 | {{- if .uiEnabled -}} 10 | 11 | apiVersion: v1 12 | kind: Service 13 | metadata: 14 | name: {{ template "vault.fullname" . }}-ui 15 | namespace: {{ include "vault.namespace" . }} 16 | labels: 17 | helm.sh/chart: {{ include "vault.chart" . }} 18 | app.kubernetes.io/name: {{ include "vault.name" . }}-ui 19 | app.kubernetes.io/instance: {{ .Release.Name }} 20 | app.kubernetes.io/managed-by: {{ .Release.Service }} 21 | {{- template "vault.ui.annotations" . }} 22 | spec: 23 | {{- if (semverCompare ">= 1.23-0" .Capabilities.KubeVersion.Version) }} 24 | {{- if .Values.ui.serviceIPFamilyPolicy }} 25 | ipFamilyPolicy: {{ .Values.ui.serviceIPFamilyPolicy }} 26 | {{- end }} 27 | {{- if .Values.ui.serviceIPFamilies }} 28 | ipFamilies: {{ .Values.ui.serviceIPFamilies | toYaml | nindent 2 }} 29 | {{- end }} 30 | {{- end }} 31 | selector: 32 | app.kubernetes.io/name: {{ include "vault.name" . }} 33 | app.kubernetes.io/instance: {{ .Release.Name }} 34 | component: server 35 | {{- if and (.Values.ui.activeVaultPodOnly) (eq .mode "ha") }} 36 | vault-active: "true" 37 | {{- end }} 38 | publishNotReadyAddresses: {{ .Values.ui.publishNotReadyAddresses }} 39 | ports: 40 | - name: {{ include "vault.scheme" . }} 41 | port: {{ .Values.ui.externalPort }} 42 | targetPort: {{ .Values.ui.targetPort }} 43 | {{- if .Values.ui.serviceNodePort }} 44 | nodePort: {{ .Values.ui.serviceNodePort }} 45 | {{- end }} 46 | type: {{ .Values.ui.serviceType }} 47 | {{- include "service.externalTrafficPolicy" .Values.ui }} 48 | {{- include "service.loadBalancer" .Values.ui }} 49 | {{- end -}} 50 | {{- end }} 51 | -------------------------------------------------------------------------------- /test/acceptance/injector-test/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (c) HashiCorp, Inc. 3 | # SPDX-License-Identifier: MPL-2.0 4 | 5 | 6 | OUTPUT=/tmp/output.txt 7 | 8 | vault operator init -n 1 -t 1 >> ${OUTPUT?} 9 | 10 | unseal=$(cat ${OUTPUT?} | grep "Unseal Key 1:" | sed -e "s/Unseal Key 1: //g") 11 | root=$(cat ${OUTPUT?} | grep "Initial Root Token:" | sed -e "s/Initial Root Token: //g") 12 | 13 | vault operator unseal ${unseal?} 14 | 15 | vault login -no-print ${root?} 16 | 17 | vault policy write db-backup /vault/userconfig/test/pgdump-policy.hcl 18 | 19 | vault auth enable kubernetes 20 | 21 | vault write auth/kubernetes/config \ 22 | token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \ 23 | kubernetes_host=https://${KUBERNETES_PORT_443_TCP_ADDR}:443 \ 24 | kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt 25 | 26 | vault write auth/kubernetes/role/db-backup \ 27 | bound_service_account_names=pgdump \ 28 | bound_service_account_namespaces=acceptance \ 29 | policies=db-backup \ 30 | ttl=1h 31 | 32 | vault secrets enable database 33 | 34 | vault write database/config/postgresql \ 35 | plugin_name=postgresql-database-plugin \ 36 | allowed_roles="db-backup" \ 37 | connection_url="postgresql://{{username}}:{{password}}@postgres:5432/mydb?sslmode=disable" \ 38 | username="vault" \ 39 | password="vault" 40 | 41 | vault write database/roles/db-backup \ 42 | db_name=postgresql \ 43 | creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \ 44 | GRANT CONNECT ON DATABASE mydb TO \"{{name}}\"; \ 45 | GRANT USAGE ON SCHEMA app TO \"{{name}}\"; \ 46 | GRANT SELECT ON ALL TABLES IN SCHEMA app TO \"{{name}}\";" \ 47 | revocation_statements="ALTER ROLE \"{{name}}\" NOLOGIN;"\ 48 | default_ttl="1h" \ 49 | max_ttl="24h" 50 | -------------------------------------------------------------------------------- /test/unit/server-network-policy.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "server/network-policy: disabled by default" { 6 | cd `chart_dir` 7 | local actual=$( (helm template \ 8 | --show-only templates/server-network-policy.yaml \ 9 | . || echo "---") | tee /dev/stderr | 10 | yq 'length > 0' | tee /dev/stderr) 11 | [ "${actual}" = "false" ] 12 | } 13 | 14 | @test "server/network-policy: enabled by server.networkPolicy.enabled" { 15 | cd `chart_dir` 16 | local actual=$( (helm template \ 17 | --set 'server.networkPolicy.enabled=true' \ 18 | --show-only templates/server-network-policy.yaml \ 19 | . || echo "---") | tee /dev/stderr | 20 | yq 'length > 0' | tee /dev/stderr) 21 | [ "${actual}" = "true" ] 22 | } 23 | 24 | @test "server/network-policy: ingress changed by server.networkPolicy.ingress" { 25 | cd `chart_dir` 26 | local actual=$(helm template \ 27 | --set 'server.networkPolicy.enabled=true' \ 28 | --set 'server.networkPolicy.ingress[0].from[0].podSelector.matchLabels.foo=bar' \ 29 | --show-only templates/server-network-policy.yaml \ 30 | . | tee /dev/stderr | 31 | yq -r '.spec.ingress[0].from[0].podSelector.matchLabels.foo' | tee /dev/stderr) 32 | [ "${actual}" = "bar" ] 33 | } 34 | 35 | @test "server/network-policy: egress enabled by server.networkPolicy.egress" { 36 | cd `chart_dir` 37 | local actual=$(helm template \ 38 | --set 'server.networkPolicy.enabled=true' \ 39 | --set 'server.networkPolicy.egress[0].to[0].ipBlock.cidr=10.0.0.0/24' \ 40 | --set 'server.networkPolicy.egress[0].ports[0].protocol=TCP' \ 41 | --set 'server.networkPolicy.egress[0].ports[0].port=443' \ 42 | --show-only templates/server-network-policy.yaml \ 43 | . | tee /dev/stderr | 44 | yq -r '.spec.egress[0].to[0].ipBlock.cidr' | tee /dev/stderr) 45 | [ "${actual}" = "10.0.0.0/24" ] 46 | } 47 | -------------------------------------------------------------------------------- /test/acceptance/setup_suite.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | setup_suite() { 4 | local INJECTOR_AGENT_VERSION SERVER_VAULT_VERSION CSI_AGENT_VERSION CHART_VALUES 5 | if [ -n "${VAULT_VERSION}" ]; then 6 | INJECTOR_AGENT_VERSION=${VAULT_VERSION} 7 | SERVER_VAULT_VERSION=${VAULT_VERSION} 8 | CSI_AGENT_VERSION=${VAULT_VERSION} 9 | else 10 | # If VAULT_VERSION is not set, use the defaults from values.yaml 11 | INJECTOR_AGENT_VERSION=$(yq -r '.injector.agentImage.tag' values.yaml) 12 | SERVER_VAULT_VERSION=$(yq -r '.server.image.tag' values.yaml) 13 | CSI_AGENT_VERSION=$(yq -r '.csi.agent.image.tag' values.yaml) 14 | fi 15 | 16 | local VAULT_REPOSITORY 17 | VAULT_REPOSITORY=${VAULT_REPOSITORY:-hashicorp/vault} 18 | 19 | PRE_CHART_CMDS="" 20 | if [ "${ENT_TESTS}" = "true" ]; then 21 | SERVER_VAULT_VERSION="${SERVER_VAULT_VERSION}-ent" 22 | INJECTOR_AGENT_VERSION="${INJECTOR_AGENT_VERSION}-ent" 23 | CSI_AGENT_VERSION="${CSI_AGENT_VERSION}-ent" 24 | VAULT_REPOSITORY="hashicorp/vault-enterprise" 25 | VAULT_LICENSE_CI=${VAULT_LICENSE_CI:?"VAULT_LICENSE_CI must be set"} 26 | CHART_VALUES+=(--set server.enterpriseLicense.secretName=vault-license) 27 | PRE_CHART_CMDS+="kubectl create secret generic vault-license --from-literal=license=${VAULT_LICENSE_CI?}" 28 | fi 29 | 30 | CHART_VALUES+=(--set injector.agentImage.tag="${INJECTOR_AGENT_VERSION}") 31 | CHART_VALUES+=(--set injector.agentImage.repository="${VAULT_REPOSITORY}") 32 | CHART_VALUES+=(--set server.image.tag="${SERVER_VAULT_VERSION}") 33 | CHART_VALUES+=(--set server.image.repository="${VAULT_REPOSITORY}") 34 | CHART_VALUES+=(--set csi.agent.image.tag="${CSI_AGENT_VERSION}") 35 | CHART_VALUES+=(--set csi.agent.image.repository="${VAULT_REPOSITORY}") 36 | 37 | SET_CHART_VALUES=${CHART_VALUES[*]} 38 | export SET_CHART_VALUES PRE_CHART_CMDS 39 | } 40 | -------------------------------------------------------------------------------- /templates/injector-mutating-webhook.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{- template "vault.injectorEnabled" . -}} 7 | {{- if .injectorEnabled -}} 8 | {{- if .Capabilities.APIVersions.Has "admissionregistration.k8s.io/v1" }} 9 | apiVersion: admissionregistration.k8s.io/v1 10 | {{- else }} 11 | apiVersion: admissionregistration.k8s.io/v1beta1 12 | {{- end }} 13 | kind: MutatingWebhookConfiguration 14 | metadata: 15 | name: {{ template "vault.fullname" . }}-agent-injector-cfg 16 | labels: 17 | app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector 18 | app.kubernetes.io/instance: {{ .Release.Name }} 19 | app.kubernetes.io/managed-by: {{ .Release.Service }} 20 | {{- template "injector.webhookAnnotations" . }} 21 | webhooks: 22 | - name: vault.hashicorp.com 23 | failurePolicy: {{ ((.Values.injector.webhook)).failurePolicy | default .Values.injector.failurePolicy }} 24 | matchPolicy: {{ ((.Values.injector.webhook)).matchPolicy | default "Exact" }} 25 | sideEffects: None 26 | timeoutSeconds: {{ ((.Values.injector.webhook)).timeoutSeconds | default "30" }} 27 | admissionReviewVersions: ["v1", "v1beta1"] 28 | clientConfig: 29 | service: 30 | name: {{ template "vault.fullname" . }}-agent-injector-svc 31 | namespace: {{ include "vault.namespace" . }} 32 | path: "/mutate" 33 | caBundle: {{ .Values.injector.certs.caBundle | quote }} 34 | rules: 35 | - operations: ["CREATE"] 36 | apiGroups: [""] 37 | apiVersions: ["v1"] 38 | resources: ["pods"] 39 | scope: "Namespaced" 40 | {{- if or (.Values.injector.namespaceSelector) (((.Values.injector.webhook)).namespaceSelector) }} 41 | namespaceSelector: 42 | {{ toYaml (((.Values.injector.webhook)).namespaceSelector | default .Values.injector.namespaceSelector) | indent 6}} 43 | {{ end }} 44 | {{- template "injector.objectSelector" . -}} 45 | {{ end }} 46 | -------------------------------------------------------------------------------- /test/unit/injector-clusterrole.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "injector/ClusterRole: enabled by default" { 6 | cd `chart_dir` 7 | local actual=$(helm template \ 8 | --show-only templates/injector-clusterrole.yaml \ 9 | . | tee /dev/stderr | 10 | yq 'length > 0' | tee /dev/stderr) 11 | [ "${actual}" = "true" ] 12 | } 13 | 14 | @test "injector/ClusterRole: disable with global.enabled" { 15 | cd `chart_dir` 16 | local actual=$( (helm template \ 17 | --show-only templates/injector-clusterrole.yaml \ 18 | --set 'global.enabled=false' \ 19 | . || echo "---") | tee /dev/stderr | 20 | yq 'length > 0' | tee /dev/stderr) 21 | [ "${actual}" = "false" ] 22 | } 23 | 24 | @test "injector/ClusterRole: no nodes permissions when replicas=1" { 25 | cd `chart_dir` 26 | local rules=$(helm template \ 27 | --show-only templates/injector-clusterrole.yaml \ 28 | --set 'injector.replicas=1' \ 29 | . | tee /dev/stderr | 30 | yq '.rules' | tee /dev/stderr) 31 | rules_length=$(echo "${rules}" | yq 'length') 32 | [ "${rules_length}" = "1" ] 33 | resources_length=$(echo "${rules}" | yq '.[0].resources | length') 34 | [ "${resources_length}" = "1" ] 35 | resource=$(echo "${rules}" | yq -r '.[0].resources[0]') 36 | [ "${resource}" = "mutatingwebhookconfigurations" ] 37 | } 38 | 39 | @test "injector/ClusterRole: nodes permissions when replicas=2" { 40 | cd `chart_dir` 41 | local rules=$(helm template \ 42 | --show-only templates/injector-clusterrole.yaml \ 43 | --set 'injector.replicas=2' \ 44 | . | tee /dev/stderr | 45 | yq '.rules' | tee /dev/stderr) 46 | rules_length=$(echo "${rules}" | yq 'length') 47 | [ "${rules_length}" = "2" ] 48 | resources_length=$(echo "${rules}" | yq '.[1].resources | length') 49 | [ "${resources_length}" = "1" ] 50 | resource=$(echo "${rules}" | yq -r '.[1].resources[0]') 51 | [ "${resource}" = "nodes" ] 52 | } 53 | -------------------------------------------------------------------------------- /test/unit/csi-role.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "csi/Role: disabled by default" { 6 | cd `chart_dir` 7 | local actual=$( (helm template \ 8 | --show-only templates/csi-role.yaml \ 9 | . || echo "---") | tee /dev/stderr | 10 | yq 'length > 0' | tee /dev/stderr) 11 | [ "${actual}" = "false" ] 12 | } 13 | 14 | @test "csi/Role: names" { 15 | cd `chart_dir` 16 | local actual=$(helm template \ 17 | --show-only templates/csi-role.yaml \ 18 | --set "csi.enabled=true" \ 19 | . | tee /dev/stderr | 20 | yq -r '.metadata.name' | tee /dev/stderr) 21 | [ "${actual}" = "release-name-vault-csi-provider-role" ] 22 | local actual=$(helm template \ 23 | --show-only templates/csi-role.yaml \ 24 | --set "csi.enabled=true" \ 25 | . | tee /dev/stderr | 26 | yq -r '.rules[0].resourceNames[0]' | tee /dev/stderr) 27 | [ "${actual}" = "vault-csi-provider-hmac-key" ] 28 | } 29 | 30 | @test "csi/Role: namespace" { 31 | cd `chart_dir` 32 | local actual=$(helm template \ 33 | --show-only templates/csi-role.yaml \ 34 | --set "csi.enabled=true" \ 35 | --namespace foo \ 36 | . | tee /dev/stderr | 37 | yq -r '.metadata.namespace' | tee /dev/stderr) 38 | [ "${actual}" = "foo" ] 39 | local actual=$(helm template \ 40 | --show-only templates/csi-role.yaml \ 41 | --set "csi.enabled=true" \ 42 | --set 'global.namespace=bar' \ 43 | --namespace foo \ 44 | . | tee /dev/stderr | 45 | yq -r '.metadata.namespace' | tee /dev/stderr) 46 | [ "${actual}" = "bar" ] 47 | } 48 | 49 | @test "csi/Role: HMAC secret name configurable" { 50 | cd `chart_dir` 51 | local actual=$(helm template \ 52 | --show-only templates/csi-role.yaml \ 53 | --set "csi.enabled=true" \ 54 | --set 'csi.hmacSecretName=foo' \ 55 | . | tee /dev/stderr | 56 | yq -r '.rules[0].resourceNames[0]' | tee /dev/stderr) 57 | [ "${actual}" = "foo" ] 58 | } -------------------------------------------------------------------------------- /test/unit/injector-psp-role.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "injector/PodSecurityPolicy-Role: PodSecurityPolicy-Role not enabled by default" { 6 | cd `chart_dir` 7 | local actual=$( (helm template \ 8 | --show-only templates/injector-psp-role.yaml \ 9 | . || echo "---" ) | tee /dev/stderr | 10 | yq 'length > 0' | tee /dev/stderr) 11 | [ "${actual}" = "false" ] 12 | } 13 | 14 | @test "injector/PodSecurityPolicy-Role: enable with injector.enabled and global.psp.enable" { 15 | cd `chart_dir` 16 | local actual=$(helm template \ 17 | --show-only templates/injector-psp-role.yaml \ 18 | --set 'injector.enabled=true' \ 19 | --set 'global.psp.enable=true' \ 20 | . | tee /dev/stderr | 21 | yq 'length > 0' | tee /dev/stderr) 22 | [ "${actual}" = "true" ] 23 | } 24 | 25 | @test "injector/PodSecurityPolicy-Role: ignore global.enabled" { 26 | cd `chart_dir` 27 | local actual=$( (helm template \ 28 | --show-only templates/injector-psp-role.yaml \ 29 | --set 'global.enabled=false' \ 30 | --set 'injector.enabled=true' \ 31 | --set 'global.psp.enable=true' \ 32 | . || echo "---") | tee /dev/stderr | 33 | yq 'length > 0' | tee /dev/stderr) 34 | [ "${actual}" = "true" ] 35 | } 36 | 37 | @test "injector/PodSecurityPolicy-Role: namespace" { 38 | cd `chart_dir` 39 | local actual=$(helm template \ 40 | --show-only templates/injector-psp-role.yaml \ 41 | --set 'injector.enabled=true' \ 42 | --set 'global.psp.enable=true' \ 43 | --namespace foo \ 44 | . | tee /dev/stderr | 45 | yq -r '.metadata.namespace' | tee /dev/stderr) 46 | [ "${actual}" = "foo" ] 47 | local actual=$(helm template \ 48 | --show-only templates/injector-psp-role.yaml \ 49 | --set 'injector.enabled=true' \ 50 | --set 'global.psp.enable=true' \ 51 | --set 'global.namespace=bar' \ 52 | --namespace foo \ 53 | . | tee /dev/stderr | 54 | yq -r '.metadata.namespace' | tee /dev/stderr) 55 | [ "${actual}" = "bar" ] 56 | } -------------------------------------------------------------------------------- /templates/tests/server-test.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{ template "vault.mode" . }} 7 | {{- if ne .mode "external" }} 8 | {{- if .serverEnabled -}} 9 | apiVersion: v1 10 | kind: Pod 11 | metadata: 12 | name: {{ template "vault.fullname" . }}-server-test 13 | namespace: {{ include "vault.namespace" . }} 14 | annotations: 15 | "helm.sh/hook": test 16 | {{- with .Values.server.extraLabels }} 17 | labels: 18 | {{- toYaml . | nindent 4 }} 19 | {{- end }} 20 | spec: 21 | {{- include "imagePullSecrets" . | nindent 2 }} 22 | containers: 23 | - name: {{ .Release.Name }}-server-test 24 | image: {{ .Values.server.image.repository }}:{{ .Values.server.image.tag | default "latest" }} 25 | imagePullPolicy: {{ .Values.server.image.pullPolicy }} 26 | env: 27 | - name: VAULT_ADDR 28 | value: {{ include "vault.scheme" . }}://{{ template "vault.fullname" . }}.{{ include "vault.namespace" . }}.svc:{{ .Values.server.service.port }} 29 | {{- include "vault.extraEnvironmentVars" .Values.server | nindent 8 }} 30 | command: 31 | - /bin/sh 32 | - -c 33 | - | 34 | echo "Checking for sealed info in 'vault status' output" 35 | ATTEMPTS=10 36 | n=0 37 | until [ "$n" -ge $ATTEMPTS ] 38 | do 39 | echo "Attempt" $n... 40 | vault status -format yaml | grep -E '^sealed: (true|false)' && break 41 | n=$((n+1)) 42 | sleep 5 43 | done 44 | if [ $n -ge $ATTEMPTS ]; then 45 | echo "timed out looking for sealed info in 'vault status' output" 46 | exit 1 47 | fi 48 | 49 | exit 0 50 | {{- if .Values.server.volumeMounts }} 51 | volumeMounts: 52 | {{- toYaml .Values.server.volumeMounts | nindent 8}} 53 | {{- end }} 54 | {{- if .Values.server.volumes }} 55 | volumes: 56 | {{- toYaml .Values.server.volumes | nindent 4}} 57 | {{- end }} 58 | restartPolicy: Never 59 | {{- end }} 60 | {{- end }} 61 | -------------------------------------------------------------------------------- /test/unit/injector-psp-rolebinding.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "injector/PodSecurityPolicy-RoleBinding: PodSecurityPolicy-RoleBinding not enabled by default" { 6 | cd `chart_dir` 7 | local actual=$( (helm template \ 8 | --show-only templates/injector-psp-rolebinding.yaml \ 9 | . || echo "---" ) | tee /dev/stderr | 10 | yq 'length > 0' | tee /dev/stderr) 11 | [ "${actual}" = "false" ] 12 | } 13 | 14 | @test "injector/PodSecurityPolicy-RoleBinding: enable with injector.enabled and global.psp.enable" { 15 | cd `chart_dir` 16 | local actual=$(helm template \ 17 | --show-only templates/injector-psp-rolebinding.yaml \ 18 | --set 'injector.enabled=true' \ 19 | --set 'global.psp.enable=true' \ 20 | . | tee /dev/stderr | 21 | yq 'length > 0' | tee /dev/stderr) 22 | [ "${actual}" = "true" ] 23 | } 24 | 25 | @test "injector/PodSecurityPolicy-RoleBinding: ignore global.enabled" { 26 | cd `chart_dir` 27 | local actual=$( (helm template \ 28 | --show-only templates/injector-psp-rolebinding.yaml \ 29 | --set 'global.enabled=false' \ 30 | --set 'injector.enabled=true' \ 31 | --set 'global.psp.enable=true' \ 32 | . || echo "---") | tee /dev/stderr | 33 | yq 'length > 0' | tee /dev/stderr) 34 | [ "${actual}" = "true" ] 35 | } 36 | 37 | @test "injector/PodSecurityPolicy-RoleBinding: namespace" { 38 | cd `chart_dir` 39 | local actual=$(helm template \ 40 | --show-only templates/injector-psp-rolebinding.yaml \ 41 | --set 'injector.enabled=true' \ 42 | --set 'global.psp.enable=true' \ 43 | --namespace foo \ 44 | . | tee /dev/stderr | 45 | yq -r '.metadata.namespace' | tee /dev/stderr) 46 | [ "${actual}" = "foo" ] 47 | local actual=$(helm template \ 48 | --show-only templates/injector-psp-rolebinding.yaml \ 49 | --set 'injector.enabled=true' \ 50 | --set 'global.psp.enable=true' \ 51 | --set 'global.namespace=bar' \ 52 | --namespace foo \ 53 | . | tee /dev/stderr | 54 | yq -r '.metadata.namespace' | tee /dev/stderr) 55 | [ "${actual}" = "bar" ] 56 | } -------------------------------------------------------------------------------- /test/docker/Test.dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | # This Dockerfile installs all the dependencies necessary to run the unit and 5 | # acceptance tests. This image also contains gcloud so you can run tests 6 | # against a GKE cluster easily. 7 | # 8 | # This image has no automatic entrypoint. It is expected that you'll run 9 | # a script to configure kubectl, potentially install Helm, and run the tests 10 | # manually. This image only has the dependencies pre-installed. 11 | 12 | FROM docker.mirror.hashicorp.services/alpine:latest 13 | WORKDIR /root 14 | 15 | ENV BATS_VERSION "1.3.0" 16 | ENV TERRAFORM_VERSION "0.12.10" 17 | 18 | # base packages 19 | RUN apk update && apk add --no-cache --virtual .build-deps \ 20 | ca-certificates \ 21 | curl \ 22 | tar \ 23 | bash \ 24 | openssl \ 25 | py-pip \ 26 | git \ 27 | make \ 28 | jq 29 | 30 | # yq 31 | RUN python3 -m venv venv && \ 32 | . venv/bin/activate && \ 33 | pip install yq && \ 34 | ln -s $PWD/venv/bin/yq /usr/local/bin/yq && \ 35 | deactivate 36 | 37 | # gcloud 38 | RUN curl -OL https://dl.google.com/dl/cloudsdk/channels/rapid/install_google_cloud_sdk.bash && \ 39 | bash install_google_cloud_sdk.bash --disable-prompts --install-dir='/root/' && \ 40 | ln -s /root/google-cloud-sdk/bin/gcloud /usr/local/bin/gcloud 41 | 42 | # terraform 43 | RUN curl -sSL https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip -o /tmp/tf.zip \ 44 | && unzip /tmp/tf.zip \ 45 | && ln -s /root/terraform /usr/local/bin/terraform 46 | 47 | # kubectl 48 | RUN curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && \ 49 | chmod +x ./kubectl && \ 50 | mv ./kubectl /usr/local/bin/kubectl 51 | 52 | # helm 53 | RUN curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash 54 | 55 | # bats 56 | RUN curl -sSL https://github.com/bats-core/bats-core/archive/v${BATS_VERSION}.tar.gz -o /tmp/bats.tgz \ 57 | && tar -zxf /tmp/bats.tgz -C /tmp \ 58 | && /bin/bash /tmp/bats-core-$BATS_VERSION/install.sh /usr/local 59 | -------------------------------------------------------------------------------- /test/unit/server-discovery-role.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "server/DiscoveryRole: enabled by default with ha" { 6 | cd `chart_dir` 7 | local actual=$( (helm template \ 8 | --show-only templates/server-discovery-role.yaml \ 9 | . || echo "---") | tee /dev/stderr | 10 | yq 'length > 0' | tee /dev/stderr) 11 | [ "${actual}" = "false" ] 12 | 13 | local actual=$( (helm template \ 14 | --show-only templates/server-discovery-role.yaml \ 15 | --set 'server.ha.enabled=true' \ 16 | . || echo "---") | tee /dev/stderr | 17 | yq 'length > 0' | tee /dev/stderr) 18 | [ "${actual}" = "true" ] 19 | } 20 | 21 | @test "server/DiscoveryRole: can disable with server.enabled false" { 22 | cd `chart_dir` 23 | local actual=$( (helm template \ 24 | --show-only templates/server-discovery-role.yaml \ 25 | --set 'server.enabled=false' \ 26 | --set 'server.ha.enabled=true' \ 27 | . || echo "---") | tee /dev/stderr | 28 | yq 'length > 0' | tee /dev/stderr) 29 | [ "${actual}" = "false" ] 30 | } 31 | 32 | @test "server/DiscoveryRole: can disable with server.serviceAccount.serviceDiscovery.enabled false" { 33 | cd `chart_dir` 34 | local actual=$( (helm template \ 35 | --show-only templates/server-discovery-role.yaml \ 36 | --set 'server.ha.enabled=true' \ 37 | --set 'server.serviceAccount.serviceDiscovery.enabled=false' \ 38 | . || echo "---") | tee /dev/stderr | 39 | yq 'length > 0' | tee /dev/stderr) 40 | [ "${actual}" = "false" ] 41 | } 42 | 43 | @test "server/DiscoveryRole: namespace" { 44 | cd `chart_dir` 45 | local actual=$(helm template \ 46 | --show-only templates/server-discovery-role.yaml \ 47 | --set 'server.ha.enabled=true' \ 48 | --namespace foo \ 49 | . | tee /dev/stderr | 50 | yq -r '.metadata.namespace' | tee /dev/stderr) 51 | [ "${actual}" = "foo" ] 52 | local actual=$(helm template \ 53 | --show-only templates/server-discovery-role.yaml \ 54 | --set 'server.ha.enabled=true' \ 55 | --set 'global.namespace=bar' \ 56 | --namespace foo \ 57 | . | tee /dev/stderr | 58 | yq -r '.metadata.namespace' | tee /dev/stderr) 59 | [ "${actual}" = "bar" ] 60 | } -------------------------------------------------------------------------------- /test/acceptance/injector.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "injector: testing deployment" { 6 | cd `chart_dir` 7 | 8 | kubectl delete namespace acceptance --ignore-not-found=true 9 | kubectl create namespace acceptance 10 | kubectl config set-context --current --namespace=acceptance 11 | 12 | kubectl create -f ./test/acceptance/injector-test/pg-deployment.yaml 13 | sleep 5 14 | wait_for_ready $(kubectl get pod -l app=postgres -o jsonpath="{.items[0].metadata.name}") 15 | 16 | kubectl create secret generic test \ 17 | --from-file ./test/acceptance/injector-test/pgdump-policy.hcl \ 18 | --from-file ./test/acceptance/injector-test/bootstrap.sh 19 | 20 | kubectl label secret test app=vault-agent-demo 21 | 22 | eval "${PRE_CHART_CMDS}" 23 | helm install "$(name_prefix)" . \ 24 | --set="server.extraVolumes[0].type=secret" \ 25 | --set="server.extraVolumes[0].name=test" \ 26 | ${SET_CHART_VALUES} 27 | check_vault_versions "$(name_prefix)" 28 | wait_for_running $(name_prefix)-0 29 | 30 | wait_for_ready $(kubectl get pod -l component=webhook -o jsonpath="{.items[0].metadata.name}") 31 | 32 | kubectl exec -ti "$(name_prefix)-0" -- /bin/sh -c "cp /vault/userconfig/test/bootstrap.sh /tmp/bootstrap.sh && chmod +x /tmp/bootstrap.sh && /tmp/bootstrap.sh" 33 | sleep 5 34 | 35 | # Sealed, not initialized 36 | local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | 37 | jq -r '.sealed' ) 38 | [ "${sealed_status}" == "false" ] 39 | 40 | local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | 41 | jq -r '.initialized') 42 | [ "${init_status}" == "true" ] 43 | 44 | 45 | kubectl create -f ./test/acceptance/injector-test/job.yaml 46 | wait_for_complete_job "pgdump" 47 | } 48 | 49 | # Clean up 50 | teardown() { 51 | if [[ ${CLEANUP:-true} == "true" ]] 52 | then 53 | echo "helm/pvc teardown" 54 | helm delete vault 55 | kubectl delete --all pvc 56 | kubectl delete secret test 57 | kubectl delete job pgdump 58 | kubectl delete deployment postgres 59 | kubectl delete namespace acceptance 60 | kubectl config unset contexts."$(kubectl config current-context)".namespace 61 | fi 62 | } 63 | -------------------------------------------------------------------------------- /test/unit/server-discovery-rolebinding.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "server/DiscoveryRoleBinding: enabled by default with ha" { 6 | cd `chart_dir` 7 | local actual=$( (helm template \ 8 | --show-only templates/server-discovery-rolebinding.yaml \ 9 | . || echo "---") | tee /dev/stderr | 10 | yq 'length > 0' | tee /dev/stderr) 11 | [ "${actual}" = "false" ] 12 | 13 | local actual=$( (helm template \ 14 | --show-only templates/server-discovery-rolebinding.yaml \ 15 | --set 'server.ha.enabled=true' \ 16 | . || echo "---") | tee /dev/stderr | 17 | yq 'length > 0' | tee /dev/stderr) 18 | [ "${actual}" = "true" ] 19 | } 20 | 21 | @test "server/DiscoveryRoleBinding: can disable with server.enabled false" { 22 | cd `chart_dir` 23 | local actual=$( (helm template \ 24 | --show-only templates/server-discovery-rolebinding.yaml \ 25 | --set 'server.enabled=false' \ 26 | --set 'server.ha.enabled=true' \ 27 | . || echo "---") | tee /dev/stderr | 28 | yq 'length > 0' | tee /dev/stderr) 29 | [ "${actual}" = "false" ] 30 | } 31 | 32 | @test "server/DiscoveryRoleBinding: can disable with server.serviceAccount.serviceDiscovery.enabled false" { 33 | cd `chart_dir` 34 | local actual=$( (helm template \ 35 | --show-only templates/server-discovery-rolebinding.yaml \ 36 | --set 'server.ha.enabled=true' \ 37 | --set 'server.serviceAccount.serviceDiscovery.enabled=false' \ 38 | . || echo "---") | tee /dev/stderr | 39 | yq 'length > 0' | tee /dev/stderr) 40 | [ "${actual}" = "false" ] 41 | } 42 | 43 | @test "server/DiscoveryRoleBinding: namespace" { 44 | cd `chart_dir` 45 | local actual=$(helm template \ 46 | --show-only templates/server-discovery-rolebinding.yaml \ 47 | --set 'server.ha.enabled=true' \ 48 | --namespace foo \ 49 | . | tee /dev/stderr | 50 | yq -r '.metadata.namespace' | tee /dev/stderr) 51 | [ "${actual}" = "foo" ] 52 | local actual=$(helm template \ 53 | --show-only templates/server-discovery-rolebinding.yaml \ 54 | --set 'server.ha.enabled=true' \ 55 | --set 'global.namespace=bar' \ 56 | --namespace foo \ 57 | . | tee /dev/stderr | 58 | yq -r '.metadata.namespace' | tee /dev/stderr) 59 | [ "${actual}" = "bar" ] 60 | } -------------------------------------------------------------------------------- /test/unit/csi-clusterrolebinding.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "csi/ClusterRoleBinding: disabled by default" { 6 | cd `chart_dir` 7 | local actual=$( (helm template \ 8 | --show-only templates/csi-clusterrolebinding.yaml \ 9 | . || echo "---")| tee /dev/stderr | 10 | yq 'length > 0' | tee /dev/stderr) 11 | [ "${actual}" = "false" ] 12 | } 13 | 14 | @test "csi/ClusterRoleBinding: enabled with csi.enabled" { 15 | cd `chart_dir` 16 | local actual=$(helm template \ 17 | --show-only templates/csi-clusterrolebinding.yaml \ 18 | --set 'csi.enabled=true' \ 19 | . | tee /dev/stderr | 20 | yq 'length > 0' | tee /dev/stderr) 21 | [ "${actual}" = "true" ] 22 | } 23 | 24 | # ClusterRoleBinding cluster role ref name 25 | @test "csi/ClusterRoleBinding: cluster role ref name" { 26 | cd `chart_dir` 27 | local actual=$(helm template \ 28 | --show-only templates/csi-clusterrolebinding.yaml \ 29 | --set "csi.enabled=true" \ 30 | . | tee /dev/stderr | 31 | yq -r '.roleRef.name' | tee /dev/stderr) 32 | [ "${actual}" = "release-name-vault-csi-provider-clusterrole" ] 33 | } 34 | 35 | # ClusterRoleBinding service account name 36 | @test "csi/ClusterRoleBinding: service account name" { 37 | cd `chart_dir` 38 | local actual=$(helm template \ 39 | --show-only templates/csi-clusterrolebinding.yaml \ 40 | --set "csi.enabled=true" \ 41 | . | tee /dev/stderr | 42 | yq -r '.subjects[0].name' | tee /dev/stderr) 43 | [ "${actual}" = "release-name-vault-csi-provider" ] 44 | } 45 | 46 | # ClusterRoleBinding service account namespace 47 | @test "csi/ClusterRoleBinding: service account namespace" { 48 | cd `chart_dir` 49 | local actual=$(helm template \ 50 | --show-only templates/csi-clusterrolebinding.yaml \ 51 | --set "csi.enabled=true" \ 52 | --namespace foo \ 53 | . | tee /dev/stderr | 54 | yq -r '.subjects[0].namespace' | tee /dev/stderr) 55 | [ "${actual}" = "foo" ] 56 | local actual=$(helm template \ 57 | --show-only templates/csi-clusterrolebinding.yaml \ 58 | --set "csi.enabled=true" \ 59 | --set 'global.namespace=bar' \ 60 | --namespace foo \ 61 | . | tee /dev/stderr | 62 | yq -r '.subjects[0].namespace' | tee /dev/stderr) 63 | [ "${actual}" = "bar" ] 64 | } -------------------------------------------------------------------------------- /test/unit/csi-agent-configmap.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "csi/Agent-ConfigMap: disabled by default" { 6 | cd `chart_dir` 7 | local actual=$( (helm template \ 8 | --show-only templates/csi-agent-configmap.yaml \ 9 | . || echo "---") | tee /dev/stderr | 10 | yq 'length > 0' | tee /dev/stderr) 11 | [ "${actual}" = "false" ] 12 | } 13 | 14 | @test "csi/Agent-ConfigMap: name" { 15 | cd `chart_dir` 16 | local actual=$(helm template \ 17 | --show-only templates/csi-agent-configmap.yaml \ 18 | --set "csi.enabled=true" \ 19 | . | tee /dev/stderr | 20 | yq -r '.metadata.name' | tee /dev/stderr) 21 | [ "${actual}" = "release-name-vault-csi-provider-agent-config" ] 22 | } 23 | 24 | @test "csi/Agent-ConfigMap: namespace" { 25 | cd `chart_dir` 26 | local actual=$(helm template \ 27 | --show-only templates/csi-agent-configmap.yaml \ 28 | --set "csi.enabled=true" \ 29 | --namespace foo \ 30 | . | tee /dev/stderr | 31 | yq -r '.metadata.namespace' | tee /dev/stderr) 32 | [ "${actual}" = "foo" ] 33 | local actual=$(helm template \ 34 | --show-only templates/csi-agent-configmap.yaml \ 35 | --set "csi.enabled=true" \ 36 | --set 'global.namespace=bar' \ 37 | --namespace foo \ 38 | . | tee /dev/stderr | 39 | yq -r '.metadata.namespace' | tee /dev/stderr) 40 | [ "${actual}" = "bar" ] 41 | } 42 | 43 | @test "csi/Agent-ConfigMap: Vault addr not affected by injector setting" { 44 | cd `chart_dir` 45 | local actual=$(helm template \ 46 | --show-only templates/csi-agent-configmap.yaml \ 47 | --set "csi.enabled=true" \ 48 | --release-name not-external-test \ 49 | --set 'injector.externalVaultAddr=http://vault-outside' \ 50 | . | tee /dev/stderr | 51 | yq -r '.data["config.hcl"]' | tee /dev/stderr) 52 | echo "${actual}" | grep "http://not-external-test-vault.default.svc:8200" 53 | } 54 | 55 | @test "csi/Agent-ConfigMap: Vault addr correctly set for externalVaultAddr" { 56 | cd `chart_dir` 57 | local actual=$(helm template \ 58 | --show-only templates/csi-agent-configmap.yaml \ 59 | --set "csi.enabled=true" \ 60 | --set 'global.externalVaultAddr=http://vault-outside' \ 61 | . | tee /dev/stderr | 62 | yq -r '.data["config.hcl"]' | tee /dev/stderr) 63 | echo "${actual}" | grep "http://vault-outside" 64 | } -------------------------------------------------------------------------------- /templates/server-service.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{ template "vault.mode" . }} 7 | {{- if ne .mode "external" }} 8 | {{- template "vault.serverServiceEnabled" . -}} 9 | {{- if .serverServiceEnabled -}} 10 | # Service for Vault cluster 11 | apiVersion: v1 12 | kind: Service 13 | metadata: 14 | name: {{ template "vault.fullname" . }} 15 | namespace: {{ include "vault.namespace" . }} 16 | labels: 17 | helm.sh/chart: {{ include "vault.chart" . }} 18 | app.kubernetes.io/name: {{ include "vault.name" . }} 19 | app.kubernetes.io/instance: {{ .Release.Name }} 20 | app.kubernetes.io/managed-by: {{ .Release.Service }} 21 | annotations: 22 | {{ template "vault.service.annotations" .}} 23 | spec: 24 | {{- if .Values.server.service.type}} 25 | type: {{ .Values.server.service.type }} 26 | {{- end}} 27 | {{- if (semverCompare ">= 1.23-0" .Capabilities.KubeVersion.Version) }} 28 | {{- if .Values.server.service.ipFamilyPolicy }} 29 | ipFamilyPolicy: {{ .Values.server.service.ipFamilyPolicy }} 30 | {{- end }} 31 | {{- if .Values.server.service.ipFamilies }} 32 | ipFamilies: {{ .Values.server.service.ipFamilies | toYaml | nindent 2 }} 33 | {{- end }} 34 | {{- end }} 35 | {{- if .Values.server.service.clusterIP }} 36 | clusterIP: {{ .Values.server.service.clusterIP }} 37 | {{- end }} 38 | {{- include "service.externalTrafficPolicy" .Values.server.service }} 39 | # We want the servers to become available even if they're not ready 40 | # since this DNS is also used for join operations. 41 | publishNotReadyAddresses: {{ .Values.server.service.publishNotReadyAddresses }} 42 | ports: 43 | - name: {{ include "vault.scheme" . }} 44 | port: {{ .Values.server.service.port }} 45 | targetPort: {{ .Values.server.service.targetPort }} 46 | {{- if and (.Values.server.service.nodePort) (eq (.Values.server.service.type | toString) "NodePort") }} 47 | nodePort: {{ .Values.server.service.nodePort }} 48 | {{- end }} 49 | - name: https-internal 50 | port: 8201 51 | targetPort: 8201 52 | selector: 53 | app.kubernetes.io/name: {{ include "vault.name" . }} 54 | {{- if eq (.Values.server.service.instanceSelector.enabled | toString) "true" }} 55 | app.kubernetes.io/instance: {{ .Release.Name }} 56 | {{- end }} 57 | component: server 58 | {{- end }} 59 | {{- end }} 60 | -------------------------------------------------------------------------------- /test/acceptance/injector-test/bootstrap-cross-namespace.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (c) HashiCorp, Inc. 3 | # SPDX-License-Identifier: MPL-2.0 4 | 5 | OUTPUT=/tmp/output.txt 6 | 7 | vault operator init -n 1 -t 1 >> ${OUTPUT?} 8 | 9 | unseal=$(cat ${OUTPUT?} | grep "Unseal Key 1:" | sed -e "s/Unseal Key 1: //g") 10 | root=$(cat ${OUTPUT?} | grep "Initial Root Token:" | sed -e "s/Initial Root Token: //g") 11 | 12 | vault operator unseal ${unseal?} 13 | 14 | vault login -no-print ${root?} 15 | 16 | vault write sys/config/group-policy-application \ 17 | group_policy_application_mode="any" 18 | 19 | # Create new namespaces - they are peer 20 | vault namespace create us-west-org 21 | vault namespace create us-east-org 22 | 23 | #-------------------------- 24 | # us-west-org namespace 25 | #-------------------------- 26 | VAULT_NAMESPACE=us-west-org vault auth enable kubernetes 27 | VAULT_NAMESPACE=us-west-org vault write auth/kubernetes/config kubernetes_host=https://kubernetes.default:443 28 | VAULT_NAMESPACE=us-west-org vault write auth/kubernetes/role/cross-namespace-demo bound_service_account_names="mega-app" bound_service_account_namespaces="acceptance" alias_name_source="serviceaccount_name" 29 | 30 | # Create an entity 31 | VAULT_NAMESPACE=us-west-org vault auth list | grep -E '^kubernetes' | awk '{print $3}' > /tmp/accessor.txt 32 | VAULT_NAMESPACE=us-west-org vault write identity/entity name="entity-for-mega-app" | grep -E '^id' | awk '{print $2}' > /tmp/entity_id.txt 33 | VAULT_NAMESPACE=us-west-org vault write identity/entity-alias name="acceptance/mega-app" canonical_id="$(cat /tmp/entity_id.txt)" mount_accessor="$(cat /tmp/accessor.txt)" 34 | 35 | #-------------------------- 36 | # us-east-org namespace 37 | #-------------------------- 38 | VAULT_NAMESPACE=us-east-org vault secrets enable -path="kv-marketing" kv-v2 39 | VAULT_NAMESPACE=us-east-org vault kv put kv-marketing/campaign start_date="March 1, 2023" end_date="March 31, 2023" prise="Certification voucher" quantity="100" 40 | 41 | # Create a policy to allow read access to kv-marketing 42 | VAULT_NAMESPACE=us-east-org vault policy write marketing-read-only -< 0' | tee /dev/stderr) 11 | [ "${actual}" = "false" ] 12 | } 13 | 14 | @test "injector/DisruptionBudget: namespace" { 15 | cd `chart_dir` 16 | local actual=$(helm template \ 17 | --show-only templates/injector-disruptionbudget.yaml \ 18 | --set 'injector.podDisruptionBudget.minAvailable=2' \ 19 | --namespace foo \ 20 | . | tee /dev/stderr | 21 | yq -r '.metadata.namespace' | tee /dev/stderr) 22 | [ "${actual}" = "foo" ] 23 | local actual=$(helm template \ 24 | --show-only templates/injector-disruptionbudget.yaml \ 25 | --set 'injector.podDisruptionBudget.minAvailable=2' \ 26 | --set 'global.namespace=bar' \ 27 | --namespace foo \ 28 | . | tee /dev/stderr | 29 | yq -r '.metadata.namespace' | tee /dev/stderr) 30 | [ "${actual}" = "bar" ] 31 | } 32 | 33 | @test "injector/DisruptionBudget: configure with injector.podDisruptionBudget minAvailable" { 34 | cd `chart_dir` 35 | local actual=$(helm template \ 36 | --show-only templates/injector-disruptionbudget.yaml \ 37 | --set 'injector.podDisruptionBudget.minAvailable=2' \ 38 | . | tee /dev/stderr | 39 | yq '.spec.minAvailable == 2' | tee /dev/stderr) 40 | [ "${actual}" = "true" ] 41 | } 42 | 43 | @test "injector/DisruptionBudget: configure with injector.podDisruptionBudget maxUnavailable" { 44 | cd `chart_dir` 45 | local actual=$(helm template \ 46 | --show-only templates/injector-disruptionbudget.yaml \ 47 | --set 'injector.podDisruptionBudget.maxUnavailable=3' \ 48 | . | tee /dev/stderr | 49 | yq '.spec.maxUnavailable == 3' | tee /dev/stderr) 50 | [ "${actual}" = "true" ] 51 | } 52 | 53 | @test "injector/DisruptionBudget: apiVersion is set correctly >= version 1.21 of kube" { 54 | cd `chart_dir` 55 | local actual=$(helm template \ 56 | --show-only templates/injector-disruptionbudget.yaml \ 57 | --set 'injector.podDisruptionBudget.minAvailable=2' \ 58 | --kube-version 1.22.5 \ 59 | . | tee /dev/stderr | 60 | yq '.apiVersion == "policy/v1"' | tee /dev/stderr) 61 | [ "${actual}" = "true" ] 62 | } 63 | -------------------------------------------------------------------------------- /test/acceptance/server-dev.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "server/dev: testing deployment" { 6 | cd "$(chart_dir)" 7 | kubectl delete namespace acceptance --ignore-not-found=true 8 | kubectl create namespace acceptance 9 | kubectl config set-context --current --namespace=acceptance 10 | 11 | echo "Using chart vars: ${SET_CHART_VALUES}" 12 | 13 | eval "${PRE_CHART_CMDS}" 14 | helm install "$(name_prefix)" . --set='server.dev.enabled=true' ${SET_CHART_VALUES} 15 | check_vault_versions "$(name_prefix)" 16 | wait_for_running "$(name_prefix)-0" 17 | 18 | # Replicas 19 | local replicas=$(kubectl get statefulset "$(name_prefix)" --output json | 20 | jq -r '.spec.replicas') 21 | [ "${replicas}" == "1" ] 22 | 23 | # Volume Mounts 24 | local volumeCount=$(kubectl get statefulset "$(name_prefix)" --output json | 25 | jq -r '.spec.template.spec.containers[0].volumeMounts | length >= 1') 26 | [ "${volumeCount}" == "true" ] 27 | 28 | # Service 29 | local service=$(kubectl get service "$(name_prefix)" --output json | 30 | jq -r '.spec.clusterIP') 31 | [ "${service}" != "None" ] 32 | 33 | local service=$(kubectl get service "$(name_prefix)" --output json | 34 | jq -r '.spec.type') 35 | [ "${service}" == "ClusterIP" ] 36 | 37 | local ports=$(kubectl get service "$(name_prefix)" --output json | 38 | jq -r '.spec.ports | length') 39 | [ "${ports}" == "2" ] 40 | 41 | local ports=$(kubectl get service "$(name_prefix)" --output json | 42 | jq -r '.spec.ports[0].port') 43 | [ "${ports}" == "8200" ] 44 | 45 | local ports=$(kubectl get service "$(name_prefix)" --output json | 46 | jq -r '.spec.ports[1].port') 47 | [ "${ports}" == "8201" ] 48 | 49 | # Sealed, not initialized 50 | local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | 51 | jq -r '.sealed' ) 52 | [ "${sealed_status}" == "false" ] 53 | 54 | local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | 55 | jq -r '.initialized') 56 | [ "${init_status}" == "true" ] 57 | } 58 | 59 | # Clean up 60 | teardown() { 61 | if [[ ${CLEANUP:-true} == "true" ]] 62 | then 63 | echo "helm/pvc teardown" 64 | helm delete vault 65 | kubectl delete --all pvc 66 | kubectl delete namespace acceptance --ignore-not-found=true 67 | kubectl config unset contexts."$(kubectl config current-context)".namespace 68 | fi 69 | } 70 | -------------------------------------------------------------------------------- /templates/prometheus-servicemonitor.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{ template "vault.mode" . }} 7 | {{ if or (.Values.global.serverTelemetry.prometheusOperator) (.Values.serverTelemetry.serviceMonitor.enabled) }} 8 | --- 9 | apiVersion: monitoring.coreos.com/v1 10 | kind: ServiceMonitor 11 | metadata: 12 | name: {{ template "vault.fullname" . }} 13 | labels: 14 | helm.sh/chart: {{ include "vault.chart" . }} 15 | app.kubernetes.io/name: {{ include "vault.name" . }} 16 | app.kubernetes.io/instance: {{ .Release.Name }} 17 | app.kubernetes.io/managed-by: {{ .Release.Service }} 18 | {{- /* update the selectors docs in values.yaml whenever the defaults below change. */ -}} 19 | {{- $selectors := .Values.serverTelemetry.serviceMonitor.selectors }} 20 | {{- if $selectors }} 21 | {{- toYaml $selectors | nindent 4 }} 22 | {{- else }} 23 | release: prometheus 24 | {{- end }} 25 | spec: 26 | selector: 27 | matchLabels: 28 | app.kubernetes.io/name: {{ template "vault.name" . }} 29 | app.kubernetes.io/instance: {{ .Release.Name }} 30 | {{- if .Values.serverTelemetry.serviceMonitor.matchLabels }} 31 | {{- toYaml .Values.serverTelemetry.serviceMonitor.matchLabels | nindent 6 }} 32 | {{- else }} 33 | {{- if eq .mode "ha" }} 34 | vault-active: "true" 35 | {{- else }} 36 | vault-internal: "true" 37 | {{- end }} 38 | {{- end }} 39 | endpoints: 40 | - port: {{ include "vault.scheme" . }} 41 | interval: {{ .Values.serverTelemetry.serviceMonitor.interval }} 42 | scrapeTimeout: {{ .Values.serverTelemetry.serviceMonitor.scrapeTimeout }} 43 | scheme: {{ include "vault.scheme" . | lower }} 44 | path: /v1/sys/metrics 45 | params: 46 | format: 47 | - prometheus 48 | {{- with .Values.serverTelemetry.serviceMonitor.tlsConfig }} 49 | tlsConfig: 50 | {{- toYaml . | nindent 6 }} 51 | {{- else }} 52 | tlsConfig: 53 | insecureSkipVerify: true 54 | {{- end }} 55 | {{- with .Values.serverTelemetry.serviceMonitor.authorization }} 56 | authorization: 57 | {{- toYaml . | nindent 6 }} 58 | {{- end }} 59 | {{- with .Values.serverTelemetry.serviceMonitor.metricRelabelings }} 60 | metricRelabelings: 61 | {{- toYaml . | nindent 6 }} 62 | {{- end }} 63 | namespaceSelector: 64 | matchNames: 65 | - {{ include "vault.namespace" . }} 66 | {{ end }} 67 | -------------------------------------------------------------------------------- /templates/server-ha-standby-service.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{ template "vault.mode" . }} 7 | {{- if ne .mode "external" }} 8 | {{- template "vault.serverServiceEnabled" . -}} 9 | {{- if .serverServiceEnabled -}} 10 | {{- if eq .mode "ha" }} 11 | {{- if eq (.Values.server.service.standby.enabled | toString) "true" }} 12 | # Service for standby Vault pod 13 | apiVersion: v1 14 | kind: Service 15 | metadata: 16 | name: {{ template "vault.fullname" . }}-standby 17 | namespace: {{ include "vault.namespace" . }} 18 | labels: 19 | helm.sh/chart: {{ include "vault.chart" . }} 20 | app.kubernetes.io/name: {{ include "vault.name" . }} 21 | app.kubernetes.io/instance: {{ .Release.Name }} 22 | app.kubernetes.io/managed-by: {{ .Release.Service }} 23 | annotations: 24 | {{- template "vault.service.annotations" . }} 25 | {{- template "vault.service.standby.annotations" . }} 26 | spec: 27 | {{- if .Values.server.service.type}} 28 | type: {{ .Values.server.service.type }} 29 | {{- end}} 30 | {{- if (semverCompare ">= 1.23-0" .Capabilities.KubeVersion.Version) }} 31 | {{- if .Values.server.service.ipFamilyPolicy }} 32 | ipFamilyPolicy: {{ .Values.server.service.ipFamilyPolicy }} 33 | {{- end }} 34 | {{- if .Values.server.service.ipFamilies }} 35 | ipFamilies: {{ .Values.server.service.ipFamilies | toYaml | nindent 2 }} 36 | {{- end }} 37 | {{- end }} 38 | {{- if .Values.server.service.clusterIP }} 39 | clusterIP: {{ .Values.server.service.clusterIP }} 40 | {{- end }} 41 | {{- include "service.externalTrafficPolicy" .Values.server.service }} 42 | publishNotReadyAddresses: {{ .Values.server.service.publishNotReadyAddresses }} 43 | ports: 44 | - name: {{ include "vault.scheme" . }} 45 | port: {{ .Values.server.service.port }} 46 | targetPort: {{ .Values.server.service.targetPort }} 47 | {{- if and (.Values.server.service.standbyNodePort) (eq (.Values.server.service.type | toString) "NodePort") }} 48 | nodePort: {{ .Values.server.service.standbyNodePort }} 49 | {{- end }} 50 | - name: https-internal 51 | port: 8201 52 | targetPort: 8201 53 | selector: 54 | app.kubernetes.io/name: {{ include "vault.name" . }} 55 | {{- if eq (.Values.server.service.instanceSelector.enabled | toString) "true" }} 56 | app.kubernetes.io/instance: {{ .Release.Name }} 57 | {{- end }} 58 | component: server 59 | vault-active: "false" 60 | {{- end }} 61 | {{- end }} 62 | {{- end }} 63 | {{- end }} 64 | -------------------------------------------------------------------------------- /templates/server-ha-active-service.yaml: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) HashiCorp, Inc. 3 | SPDX-License-Identifier: MPL-2.0 4 | */}} 5 | 6 | {{ template "vault.mode" . }} 7 | {{- if ne .mode "external" }} 8 | {{- template "vault.serverServiceEnabled" . -}} 9 | {{- if .serverServiceEnabled -}} 10 | {{- if eq .mode "ha" }} 11 | {{- if eq (.Values.server.service.active.enabled | toString) "true" }} 12 | # Service for active Vault pod 13 | apiVersion: v1 14 | kind: Service 15 | metadata: 16 | name: {{ template "vault.fullname" . }}-active 17 | namespace: {{ include "vault.namespace" . }} 18 | labels: 19 | helm.sh/chart: {{ include "vault.chart" . }} 20 | app.kubernetes.io/name: {{ include "vault.name" . }} 21 | app.kubernetes.io/instance: {{ .Release.Name }} 22 | app.kubernetes.io/managed-by: {{ .Release.Service }} 23 | vault-active: "true" 24 | annotations: 25 | {{- template "vault.service.annotations" . }} 26 | {{- template "vault.service.active.annotations" . }} 27 | spec: 28 | {{- if .Values.server.service.type}} 29 | type: {{ .Values.server.service.type }} 30 | {{- end}} 31 | {{- if (semverCompare ">= 1.23-0" .Capabilities.KubeVersion.Version) }} 32 | {{- if .Values.server.service.ipFamilyPolicy }} 33 | ipFamilyPolicy: {{ .Values.server.service.ipFamilyPolicy }} 34 | {{- end }} 35 | {{- if .Values.server.service.ipFamilies }} 36 | ipFamilies: {{ .Values.server.service.ipFamilies | toYaml | nindent 2 }} 37 | {{- end }} 38 | {{- end }} 39 | {{- if .Values.server.service.clusterIP }} 40 | clusterIP: {{ .Values.server.service.clusterIP }} 41 | {{- end }} 42 | {{- include "service.externalTrafficPolicy" .Values.server.service }} 43 | publishNotReadyAddresses: {{ .Values.server.service.publishNotReadyAddresses }} 44 | ports: 45 | - name: {{ include "vault.scheme" . }} 46 | port: {{ .Values.server.service.port }} 47 | targetPort: {{ .Values.server.service.targetPort }} 48 | {{- if and (.Values.server.service.activeNodePort) (eq (.Values.server.service.type | toString) "NodePort") }} 49 | nodePort: {{ .Values.server.service.activeNodePort }} 50 | {{- end }} 51 | - name: https-internal 52 | port: 8201 53 | targetPort: 8201 54 | selector: 55 | app.kubernetes.io/name: {{ include "vault.name" . }} 56 | {{- if eq (.Values.server.service.instanceSelector.enabled | toString) "true" }} 57 | app.kubernetes.io/instance: {{ .Release.Name }} 58 | {{- end }} 59 | component: server 60 | vault-active: "true" 61 | {{- end }} 62 | {{- end }} 63 | {{- end }} 64 | {{- end }} 65 | -------------------------------------------------------------------------------- /test/unit/injector-psp.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "injector/PodSecurityPolicy: PodSecurityPolicy not enabled by default" { 6 | cd `chart_dir` 7 | local actual=$( (helm template \ 8 | --show-only templates/injector-psp.yaml \ 9 | . || echo "---") | tee /dev/stderr | 10 | yq 'length > 0' | tee /dev/stderr) 11 | [ "${actual}" = "false" ] 12 | } 13 | 14 | @test "injector/PodSecurityPolicy: enable with injector.enabled and global.psp.enable" { 15 | cd `chart_dir` 16 | local actual=$(helm template \ 17 | --show-only templates/injector-psp.yaml \ 18 | --set 'injector.enabled=true' \ 19 | --set 'global.psp.enable=true' \ 20 | . | tee /dev/stderr | 21 | yq 'length > 0' | tee /dev/stderr) 22 | [ "${actual}" = "true" ] 23 | } 24 | 25 | @test "injector/PodSecurityPolicy: ignore global.enabled" { 26 | cd `chart_dir` 27 | local actual=$( (helm template \ 28 | --show-only templates/injector-psp.yaml \ 29 | --set 'global.enabled=false' \ 30 | --set 'injector.enabled=true' \ 31 | --set 'global.psp.enable=true' \ 32 | . || echo "---") | tee /dev/stderr | 33 | yq 'length > 0' | tee /dev/stderr) 34 | [ "${actual}" = "true" ] 35 | } 36 | 37 | @test "injector/PodSecurityPolicy: annotations are templated correctly by default" { 38 | cd `chart_dir` 39 | local actual=$(helm template \ 40 | --show-only templates/injector-psp.yaml \ 41 | --set 'injector.enabled=true' \ 42 | --set 'global.psp.enable=true' \ 43 | . | tee /dev/stderr | 44 | yq '.metadata.annotations | length == 4' | tee /dev/stderr) 45 | [ "${actual}" = "true" ] 46 | } 47 | 48 | @test "injector/PodSecurityPolicy: annotations are added - string" { 49 | cd `chart_dir` 50 | local actual=$(helm template \ 51 | --show-only templates/injector-psp.yaml \ 52 | --set 'injector.enabled=true' \ 53 | --set 'global.psp.enable=true' \ 54 | --set 'global.psp.annotations=vault-is: amazing' \ 55 | . | tee /dev/stderr | 56 | yq -r '.metadata.annotations["vault-is"]' | tee /dev/stderr) 57 | [ "${actual}" = "amazing" ] 58 | } 59 | 60 | @test "injector/PodSecurityPolicy: annotations are added - object" { 61 | cd `chart_dir` 62 | local actual=$(helm template \ 63 | --show-only templates/injector-psp.yaml \ 64 | --set 'injector.enabled=true' \ 65 | --set 'global.psp.enable=true' \ 66 | --set 'global.psp.annotations.vault-is=amazing' \ 67 | . | tee /dev/stderr | 68 | yq -r '.metadata.annotations["vault-is"]' | tee /dev/stderr) 69 | [ "${actual}" = "amazing" ] 70 | } 71 | -------------------------------------------------------------------------------- /test/chart/verifier.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | setup_file() { 6 | cd `chart_dir` 7 | export VERIFY_OUTPUT="/$BATS_RUN_TMPDIR/verify.json" 8 | export CHART_VOLUME=vault-helm-chart-src 9 | local IMAGE="quay.io/redhat-certification/chart-verifier:1.13.12" 10 | # chart-verifier requires an openshift version if a cluster isn't available 11 | local OPENSHIFT_VERSION="4.19" 12 | local DISABLED_TESTS="chart-testing" 13 | 14 | local run_cmd="chart-verifier" 15 | local chart_src="." 16 | 17 | if [ ! -e $USE_DOCKER ]; then 18 | chart_src="/chart" 19 | # Create a dummy container which will hold a volume with chart source 20 | docker create -v $chart_src --name $CHART_VOLUME alpine:3 /bin/true 21 | # Copy the chart source into this volume 22 | docker cp . $CHART_VOLUME:$chart_src 23 | # Make sure we have the latest version of chart-verifier 24 | docker pull $IMAGE 25 | # Start chart-verifier using this volume 26 | run_cmd="docker run --rm --volumes-from $CHART_VOLUME -w $chart_src $IMAGE" 27 | fi 28 | 29 | $run_cmd verify $chart_src \ 30 | --output json \ 31 | --openshift-version $OPENSHIFT_VERSION \ 32 | --disable $DISABLED_TESTS \ 33 | --chart-values values.openshift.yaml | tee $VERIFY_OUTPUT 34 | } 35 | 36 | teardown_file() { 37 | if [ ! -e $USE_DOCKER ]; then 38 | docker rm $CHART_VOLUME 39 | fi 40 | } 41 | 42 | @test "has-kubeversion" { 43 | check_result v1.1/has-kubeversion 44 | } 45 | 46 | @test "is-helm-v3" { 47 | check_result v1.0/is-helm-v3 48 | } 49 | 50 | @test "not-contains-crds" { 51 | check_result v1.0/not-contains-crds 52 | } 53 | 54 | @test "helm-lint" { 55 | check_result v1.0/helm-lint 56 | } 57 | 58 | @test "not-contain-csi-objects" { 59 | check_result v1.0/not-contain-csi-objects 60 | } 61 | 62 | @test "has-readme" { 63 | check_result v1.0/has-readme 64 | } 65 | 66 | @test "contains-values" { 67 | check_result v1.0/contains-values 68 | } 69 | 70 | @test "contains-values-schema" { 71 | check_result v1.0/contains-values-schema 72 | } 73 | 74 | @test "contains-test" { 75 | check_result v1.0/contains-test 76 | } 77 | 78 | @test "images-are-certified" { 79 | check_result v1.1/images-are-certified 80 | } 81 | 82 | @test "required-annotations-present" { 83 | check_result v1.0/required-annotations-present 84 | } 85 | 86 | @test "chart-testing" { 87 | skip "Skipping since this test requires a kubernetes/openshift cluster" 88 | check_result v1.0/chart-testing 89 | } 90 | 91 | @test "signature-is-valid" { 92 | skip "Chart is not signed : Signature verification not required" 93 | check_result v1.0/signature-is-valid 94 | } 95 | -------------------------------------------------------------------------------- /.github/workflows/acceptance.yaml: -------------------------------------------------------------------------------- 1 | name: Acceptance Tests 2 | on: [push, workflow_dispatch] 3 | jobs: 4 | versions: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - run: echo "setting versions" 8 | outputs: 9 | K8S_1: "1.33.4" 10 | K8S_2: "1.32.8" 11 | K8S_3: "1.31.12" 12 | K8S_4: "1.30.13" 13 | K8S_5: "1.29.14" 14 | VAULT_N: "1.21.0" 15 | VAULT_N_1: "1.20.5" 16 | VAULT_N_2: "1.19.11" 17 | VAULT_LTS_1: "1.16.27" 18 | 19 | kind-ce-vault: 20 | name: ce vault:${{ matrix.vault-version }} k8s:${{ matrix.k8s-version }} 21 | runs-on: ubuntu-latest 22 | needs: versions 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | vault-version: 27 | - ${{ needs.versions.outputs.VAULT_N }} 28 | k8s-version: 29 | - ${{ needs.versions.outputs.K8S_1 }} 30 | - ${{ needs.versions.outputs.K8S_2 }} 31 | - ${{ needs.versions.outputs.K8S_3 }} 32 | - ${{ needs.versions.outputs.K8S_4 }} 33 | - ${{ needs.versions.outputs.K8S_5 }} 34 | steps: 35 | - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 36 | - uses: ./.github/actions/acceptance-test 37 | name: vault:${{ matrix.vault-version }} kind:${{ matrix.k8s-version }} 38 | with: 39 | k8s-version: ${{ matrix.k8s-version }} 40 | vault-version: ${{ matrix.vault-version }} 41 | vault-license: ${{ secrets.VAULT_LICENSE_CI }} 42 | vault-enterprise: false 43 | 44 | kind-ent-vault: 45 | name: ent vault:${{ matrix.vault-version }} k8s:${{ matrix.k8s-version }} 46 | runs-on: ubuntu-latest 47 | needs: versions 48 | strategy: 49 | fail-fast: false 50 | matrix: 51 | vault-version: 52 | - ${{ needs.versions.outputs.VAULT_N }} 53 | - ${{ needs.versions.outputs.VAULT_N_1 }} 54 | - ${{ needs.versions.outputs.VAULT_N_2 }} 55 | - ${{ needs.versions.outputs.VAULT_LTS_1 }} 56 | k8s-version: 57 | - ${{ needs.versions.outputs.K8S_1 }} 58 | - ${{ needs.versions.outputs.K8S_2 }} 59 | - ${{ needs.versions.outputs.K8S_3 }} 60 | - ${{ needs.versions.outputs.K8S_4 }} 61 | - ${{ needs.versions.outputs.K8S_5 }} 62 | steps: 63 | - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 64 | - uses: ./.github/actions/acceptance-test 65 | name: vault:${{ matrix.vault-version }} kind:${{ matrix.k8s-version }} 66 | with: 67 | k8s-version: ${{ matrix.k8s-version }} 68 | vault-version: ${{ matrix.vault-version }} 69 | vault-license: ${{ secrets.VAULT_LICENSE_CI }} 70 | vault-enterprise: true 71 | 72 | permissions: 73 | contents: read 74 | -------------------------------------------------------------------------------- /test/terraform/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | provider "google" { 5 | project = "${var.project}" 6 | } 7 | 8 | resource "random_id" "suffix" { 9 | byte_length = 4 10 | } 11 | 12 | data "google_container_engine_versions" "main" { 13 | location = "${var.zone}" 14 | version_prefix = "1.19." 15 | } 16 | 17 | data "google_service_account" "gcpapi" { 18 | account_id = "${var.gcp_service_account}" 19 | } 20 | 21 | resource "google_container_cluster" "cluster" { 22 | name = "vault-helm-dev-${random_id.suffix.dec}" 23 | project = "${var.project}" 24 | enable_legacy_abac = true 25 | initial_node_count = 3 26 | location = "${var.zone}" 27 | min_master_version = "${data.google_container_engine_versions.main.latest_master_version}" 28 | node_version = "${data.google_container_engine_versions.main.latest_node_version}" 29 | 30 | node_config { 31 | #service account for nodes to use 32 | oauth_scopes = [ 33 | "https://www.googleapis.com/auth/cloud-platform", 34 | "https://www.googleapis.com/auth/compute", 35 | "https://www.googleapis.com/auth/devstorage.read_write", 36 | "https://www.googleapis.com/auth/logging.write", 37 | "https://www.googleapis.com/auth/monitoring", 38 | "https://www.googleapis.com/auth/service.management.readonly", 39 | "https://www.googleapis.com/auth/servicecontrol", 40 | "https://www.googleapis.com/auth/trace.append", 41 | ] 42 | 43 | service_account = "${data.google_service_account.gcpapi.email}" 44 | } 45 | } 46 | 47 | resource "null_resource" "kubectl" { 48 | count = "${var.init_cli ? 1 : 0 }" 49 | 50 | triggers = { 51 | cluster = "${google_container_cluster.cluster.id}" 52 | } 53 | 54 | # On creation, we want to setup the kubectl credentials. The easiest way 55 | # to do this is to shell out to gcloud. 56 | provisioner "local-exec" { 57 | command = "gcloud container clusters get-credentials --zone=${var.zone} ${google_container_cluster.cluster.name}" 58 | } 59 | 60 | # On destroy we want to try to clean up the kubectl credentials. This 61 | # might fail if the credentials are already cleaned up or something so we 62 | # want this to continue on failure. Generally, this works just fine since 63 | # it only operates on local data. 64 | provisioner "local-exec" { 65 | when = "destroy" 66 | on_failure = "continue" 67 | command = "kubectl config get-clusters | grep ${google_container_cluster.cluster.name} | xargs -n1 kubectl config delete-cluster" 68 | } 69 | 70 | provisioner "local-exec" { 71 | when = "destroy" 72 | on_failure = "continue" 73 | command = "kubectl config get-contexts | grep ${google_container_cluster.cluster.name} | xargs -n1 kubectl config delete-context" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # Vault Helm Tests 2 | 3 | ## Running Vault Helm Acceptance tests 4 | 5 | The Makefile at the top level of this repo contains a few target that should help with running acceptance tests in your own GKE instance or in a kind cluster. 6 | 7 | Note that for the Vault Enterprise tests to pass, a `VAULT_LICENSE_CI` environment variable needs to be set to the contents of a valid Vault Enterprise license. 8 | 9 | ### Running in a GKE cluster 10 | 11 | * Set the `GOOGLE_CREDENTIALS` and `CLOUDSDK_CORE_PROJECT` variables at the top of the file. `GOOGLE_CREDENTIALS` should contain the local path to your Google Cloud Platform account credentials in JSON format. `CLOUDSDK_CORE_PROJECT` should be set to the ID of your GCP project. 12 | * Run `make test-image` to create the docker image (with dependencies installed) that will be re-used in the below steps. 13 | * Run `make test-provision` to provision the GKE cluster using terraform. 14 | * Run `make test-acceptance` to run the acceptance tests in this already provisioned cluster. 15 | * You can choose to only run certain tests by setting the ACCEPTANCE_TESTS variable and re-running the above target. 16 | * Run `make test-destroy` when you have finished testing and want to tear-down and remove the cluster. 17 | 18 | ### Running in a kind cluster 19 | 20 | * Run `make test-acceptance LOCAL_ACCEPTANCE_TESTS=true` 21 | * You can choose to only run certain tests by setting the `ACCEPTANCE_TESTS` variable and re-running the above target. 22 | * Run `make delete-kind` when you have finished testing and want to tear-down and remove the cluster. 23 | * You can set an alternate kind cluster name by specifying the `KIND_CLUSTER_NAME` variable for any of the above targets. 24 | * You can set an alternate K8S version by specifying the `KIND_K8S_VERSION` variable for any of the above targets. 25 | 26 | See [kind-quick-start](https://kind.sigs.k8s.io/docs/user/quick-start/) if you don't have kind installed on your system. 27 | 28 | ## Running chart verification tests 29 | 30 | If [chart-verifier](https://github.com/redhat-certification/chart-verifier) is built and available in your PATH, run: 31 | 32 | bats test/chart/verifier.bats 33 | 34 | Or if you'd rather use the latest chart-verifier docker container, set 35 | USE_DOCKER: 36 | 37 | USE_DOCKER=true bats test/chart/verifier.bats 38 | 39 | ## Generating the values json schema 40 | 41 | There is a make target for generating values.schema.json: 42 | 43 | make values-schema 44 | 45 | It relies on the helm [schema-gen plugin][schema-gen]. Note that some manual 46 | editing will be required, since several properties accept multiple data types. 47 | 48 | [schema-gen]: https://github.com/KnechtionsCoding/helm-schema-gen 49 | 50 | ## Helm test 51 | 52 | Vault Helm also contains a simple helm test under 53 | [templates/tests/](../templates/tests/) that may be run against a helm release: 54 | 55 | helm test 56 | -------------------------------------------------------------------------------- /test/unit/injector-service.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "injector/Service: service enabled by default" { 6 | cd `chart_dir` 7 | local actual=$(helm template \ 8 | --show-only templates/injector-service.yaml \ 9 | . | tee /dev/stderr | 10 | yq 'length > 0' | tee /dev/stderr) 11 | [ "${actual}" = "true" ] 12 | 13 | local actual=$(helm template \ 14 | --show-only templates/injector-service.yaml \ 15 | --set 'injector.enabled=true' \ 16 | . | tee /dev/stderr | 17 | yq 'length > 0' | tee /dev/stderr) 18 | [ "${actual}" = "true" ] 19 | } 20 | 21 | @test "injector/Service: namespace" { 22 | cd `chart_dir` 23 | local actual=$(helm template \ 24 | --show-only templates/injector-service.yaml \ 25 | --namespace foo \ 26 | . | tee /dev/stderr | 27 | yq -r '.metadata.namespace' | tee /dev/stderr) 28 | [ "${actual}" = "foo" ] 29 | local actual=$(helm template \ 30 | --show-only templates/injector-service.yaml \ 31 | --set 'global.namespace=bar' \ 32 | --namespace foo \ 33 | . | tee /dev/stderr | 34 | yq -r '.metadata.namespace' | tee /dev/stderr) 35 | [ "${actual}" = "bar" ] 36 | } 37 | 38 | @test "injector/Service: service with default port" { 39 | cd `chart_dir` 40 | local actual=$(helm template \ 41 | --show-only templates/injector-service.yaml \ 42 | . | tee /dev/stderr | 43 | yq -r '.spec.ports[0].targetPort' | tee /dev/stderr) 44 | [ "${actual}" = "8080" ] 45 | } 46 | 47 | @test "injector/Service: service with custom port" { 48 | cd `chart_dir` 49 | local actual=$(helm template \ 50 | --show-only templates/injector-service.yaml \ 51 | --set 'injector.port=8443' \ 52 | . | tee /dev/stderr | 53 | yq -r '.spec.ports[0].targetPort' | tee /dev/stderr) 54 | [ "${actual}" = "8443" ] 55 | } 56 | 57 | @test "injector/Service: disable with global.enabled false" { 58 | cd `chart_dir` 59 | local actual=$( (helm template \ 60 | --show-only templates/injector-service.yaml \ 61 | --set 'global.enabled=false' \ 62 | . || echo "---") | tee /dev/stderr | 63 | yq 'length > 0' | tee /dev/stderr) 64 | [ "${actual}" = "false" ] 65 | 66 | local actual=$( (helm template \ 67 | --show-only templates/injector-service.yaml \ 68 | --set 'global.enabled=false' \ 69 | --set 'injector.enabled=true' \ 70 | . || echo "---") | tee /dev/stderr | 71 | yq 'length > 0' | tee /dev/stderr) 72 | [ "${actual}" = "true" ] 73 | } 74 | 75 | @test "injector/Service: generic annotations" { 76 | cd `chart_dir` 77 | local actual=$(helm template \ 78 | --show-only templates/injector-service.yaml \ 79 | --set 'injector.service.annotations=vaultIsAwesome: true' \ 80 | . | tee /dev/stderr | 81 | yq -r '.metadata.annotations["vaultIsAwesome"]' | tee /dev/stderr) 82 | [ "${actual}" = "true" ] 83 | } 84 | -------------------------------------------------------------------------------- /test/acceptance/server-telemetry.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "server/telemetry: prometheusOperator" { 6 | cd `chart_dir` 7 | helm --namespace acceptance uninstall $(name_prefix) || : 8 | helm --namespace acceptance uninstall prometheus || : 9 | kubectl delete namespace acceptance --ignore-not-found=true 10 | kubectl create namespace acceptance 11 | kubectl config set-context --current --namespace=acceptance 12 | eval "${PRE_CHART_CMDS}" 13 | 14 | # Install prometheus-operator and friends. 15 | helm repo add prometheus-community https://prometheus-community.github.io/helm-charts 16 | helm repo update 17 | helm upgrade --install \ 18 | --wait \ 19 | --version 58.3.1 \ 20 | prometheus prometheus-community/kube-prometheus-stack 21 | 22 | # Install Vault with telemetry config now that the prometheus CRDs are applied. 23 | helm upgrade "$(name_prefix)" . \ 24 | --install \ 25 | --wait \ 26 | --values ./test/acceptance/server-test/vault-server.yaml \ 27 | --values ./test/acceptance/server-test/vault-telemetry.yaml \ 28 | ${SET_CHART_VALUES} 29 | 30 | wait_for_ready "$(name_prefix)-0" 31 | 32 | echo 'path "sys/metrics" {capabilities = ["read"]}' | kubectl exec -i "$(name_prefix)-0" -- vault policy write metrics - 33 | 34 | # Store Vault's dev TLS CA and a token in a secret for prometheus to use. 35 | kubectl create secret generic vault-metrics-client \ 36 | --from-literal="ca.crt=$(kubectl exec "$(name_prefix)-0" -- cat /var/run/tls/vault-ca.pem)" \ 37 | --from-literal="token=$(kubectl exec "$(name_prefix)-0" -- vault token create -policy=metrics -field=token)" 38 | 39 | # unfortunately it can take up to 2 minutes for the vault prometheus job to appear 40 | # TODO: investigate how reduce this. 41 | local job_labels 42 | local tries=0 43 | until [ $tries -ge 240 ] 44 | do 45 | job_labels=$( (kubectl exec -n acceptance svc/prometheus-kube-prometheus-prometheus \ 46 | -c prometheus \ 47 | -- wget -q -O - http://127.0.0.1:9090/api/v1/label/job/values) | tee /dev/stderr ) 48 | 49 | # Ensure the expected job label was picked up by Prometheus 50 | [ "$(echo "${job_labels}" | jq 'any(.data[]; . == "vault-internal")')" = "true" ] && break 51 | 52 | ((++tries)) 53 | sleep .5 54 | done 55 | 56 | 57 | # Ensure the expected job is "up" 58 | local job_up=$( ( kubectl exec -n acceptance svc/prometheus-kube-prometheus-prometheus \ 59 | -c prometheus \ 60 | -- wget -q -O - 'http://127.0.0.1:9090/api/v1/query?query=up{job="vault-internal"}' ) | \ 61 | tee /dev/stderr ) 62 | [ "$(echo "${job_up}" | jq '.data.result[0].value[1]')" = \"1\" ] 63 | } 64 | 65 | # Clean up 66 | teardown() { 67 | if [[ ${CLEANUP:-true} == "true" ]] 68 | then 69 | echo "helm/pvc teardown" 70 | helm uninstall $(name_prefix) 71 | helm uninstall prometheus 72 | kubectl delete --all pvc 73 | kubectl delete namespace acceptance --ignore-not-found=true 74 | kubectl config unset contexts."$(kubectl config current-context)".namespace 75 | fi 76 | } 77 | -------------------------------------------------------------------------------- /test/unit/server-serviceaccount-secret.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "server/ServiceAccountSecret: verify service account name match" { 6 | cd `chart_dir` 7 | 8 | local actual=$( (helm template \ 9 | --show-only templates/server-serviceaccount-secret.yaml \ 10 | --set 'server.dev.enabled=true' \ 11 | --set 'server.serviceAccount.create=false' \ 12 | . || echo "---") | tee /dev/stderr | 13 | yq 'length > 0' | tee /dev/stderr) 14 | [ "${actual}" = "false" ] 15 | 16 | local actual=$(helm template \ 17 | --show-only templates/server-serviceaccount-secret.yaml \ 18 | --set 'server.dev.enabled=true' \ 19 | --set 'server.serviceAccount.name=user-defined-ksa' \ 20 | --set 'server.serviceAccount.createSecret=true' \ 21 | . | tee /dev/stderr | 22 | yq -r '.metadata.name' | tee /dev/stderr) 23 | [ "${actual}" = "user-defined-ksa-token" ] 24 | 25 | local actual=$(helm template \ 26 | --show-only templates/server-serviceaccount-secret.yaml \ 27 | --set 'server.dev.enabled=true' \ 28 | --set 'server.serviceAccount.createSecret=true' \ 29 | . | tee /dev/stderr | 30 | yq -r '.metadata.name' | tee /dev/stderr) 31 | [ "${actual}" = "release-name-vault-token" ] 32 | 33 | } 34 | 35 | @test "server/ServiceAccountSecret: annotation mapping to service account" { 36 | cd `chart_dir` 37 | 38 | local actual=$(helm template \ 39 | --show-only templates/server-serviceaccount-secret.yaml \ 40 | --set 'server.dev.enabled=true' \ 41 | --set 'server.serviceAccount.name=user-defined-ksa' \ 42 | --set 'server.serviceAccount.createSecret=true' \ 43 | . | tee /dev/stderr | 44 | yq -r '.metadata.annotations["kubernetes.io/service-account.name"]' | tee /dev/stderr) 45 | [ "${actual}" = "user-defined-ksa" ] 46 | 47 | local actual=$(helm template \ 48 | --show-only templates/server-serviceaccount-secret.yaml \ 49 | --set 'server.dev.enabled=true' \ 50 | --set 'server.serviceAccount.createSecret=true' \ 51 | . | tee /dev/stderr | 52 | yq -r '.metadata.annotations["kubernetes.io/service-account.name"]' | tee /dev/stderr) 53 | [ "${actual}" = "release-name-vault" ] 54 | 55 | } 56 | 57 | @test "server/ServiceAccountSecret: namespace" { 58 | cd `chart_dir` 59 | local actual=$(helm template \ 60 | --show-only templates/server-serviceaccount-secret.yaml \ 61 | --set 'server.serviceAccount.create=true' \ 62 | --set 'server.serviceAccount.createSecret=true' \ 63 | --namespace foo \ 64 | . | tee /dev/stderr | 65 | yq -r '.metadata.namespace' | tee /dev/stderr) 66 | [ "${actual}" = "foo" ] 67 | local actual=$(helm template \ 68 | --show-only templates/server-serviceaccount-secret.yaml \ 69 | --set 'server.serviceAccount.create=true' \ 70 | --set 'server.serviceAccount.createSecret=true' \ 71 | --set 'global.namespace=bar' \ 72 | --namespace foo \ 73 | . | tee /dev/stderr | 74 | yq -r '.metadata.namespace' | tee /dev/stderr) 75 | [ "${actual}" = "bar" ] 76 | } 77 | 78 | -------------------------------------------------------------------------------- /test/acceptance/injector-cross-namespace.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | setup() { 6 | kubectl delete namespace acceptance --ignore-not-found=true 7 | kubectl create namespace acceptance 8 | kubectl config set-context --current --namespace=acceptance 9 | eval "${PRE_CHART_CMDS}" 10 | } 11 | 12 | teardown() { 13 | if [[ ${CLEANUP:-true} == "true" ]] 14 | then 15 | echo "helm/pvc teardown" 16 | helm delete vault 17 | kubectl delete --all pvc 18 | kubectl delete secret test 19 | kubectl delete namespace acceptance 20 | kubectl config unset contexts."$(kubectl config current-context)".namespace 21 | fi 22 | } 23 | 24 | @test "injector/enterprise: testing cross namespace access" { 25 | if [ ! "$ENT_TESTS" = "true" ]; then 26 | skip "Enterprise tests are not enabled" 27 | fi 28 | 29 | cd `chart_dir` 30 | 31 | kubectl create secret generic test \ 32 | --from-file ./test/acceptance/injector-test/bootstrap-cross-namespace.sh 33 | 34 | kubectl label secret test app=vault-agent-demo 35 | helm install "$(name_prefix)" . \ 36 | --set="server.extraVolumes[0].type=secret" \ 37 | --set="server.extraVolumes[0].name=test" \ 38 | --set='server.ha.enabled=true' \ 39 | --set='server.ha.raft.enabled=true' \ 40 | --set='server.ha.replicas=1' \ 41 | ${SET_CHART_VALUES} 42 | check_vault_versions "$(name_prefix)" 43 | wait_for_running "$(name_prefix)-0" 44 | 45 | wait_for_ready $(kubectl get pod -l component=webhook -o jsonpath="{.items[0].metadata.name}") 46 | 47 | kubectl exec -ti "$(name_prefix)-0" -- /bin/sh -c "cp /vault/userconfig/test/bootstrap-cross-namespace.sh /tmp/bootstrap.sh && chmod +x /tmp/bootstrap.sh && /tmp/bootstrap.sh" 48 | sleep 5 49 | 50 | # Sealed, not initialized 51 | local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | 52 | jq -r '.sealed' ) 53 | [ "${sealed_status}" == "false" ] 54 | 55 | local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | 56 | jq -r '.initialized') 57 | [ "${init_status}" == "true" ] 58 | 59 | kubectl create serviceaccount mega-app 60 | kubectl run nginx \ 61 | --image=nginx \ 62 | --annotations="vault.hashicorp.com/agent-inject=true" \ 63 | --annotations="vault.hashicorp.com/role=cross-namespace-demo" \ 64 | --annotations="vault.hashicorp.com/auth-path=us-west-org/auth/kubernetes" \ 65 | --annotations="vault.hashicorp.com/agent-inject-secret-marketing=us-east-org/kv-marketing/campaign" \ 66 | --annotations="vault.hashicorp.com/agent-inject-default-template=json" \ 67 | --overrides='{ "apiVersion": "v1", "spec": { "serviceAccountName": "mega-app" } }' 68 | kubectl wait --for=condition=Ready --timeout=5m pod nginx 69 | 70 | local secret_output=$(kubectl exec nginx -c nginx -- cat /vault/secrets/marketing) 71 | [ "$(jq -r '.data.start_date' <<< ${secret_output})" == "March 1, 2023" ] 72 | [ "$(jq -r '.data.end_date' <<< ${secret_output})" == "March 31, 2023" ] 73 | [ "$(jq -r '.data.prise' <<< ${secret_output})" == "Certification voucher" ] 74 | [ "$(jq -r '.data.quantity' <<< ${secret_output})" == "100" ] 75 | } 76 | -------------------------------------------------------------------------------- /test/unit/prometheus-prometheusrules.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "prometheus/PrometheusRules-server: assertDisabled by default" { 6 | cd `chart_dir` 7 | local actual=$( (helm template \ 8 | --show-only templates/prometheus-prometheusrules.yaml \ 9 | --set 'serverTelemetry.prometheusRules.rules[0].foo=bar' \ 10 | . || echo "---") | tee /dev/stderr | 11 | yq 'length > 0' | tee /dev/stderr) 12 | [ "${actual}" = "false" ] 13 | } 14 | 15 | @test "prometheus/PrometheusRules-server: assertDisabled with rules-defined=false" { 16 | cd `chart_dir` 17 | local actual=$( (helm template \ 18 | --show-only templates/prometheus-prometheusrules.yaml \ 19 | --set 'serverTelemetry.prometheusRules.enabled=true' \ 20 | . || echo "---") | tee /dev/stderr | yq 'length > 0' | tee /dev/stderr) 21 | [ "${actual}" = "false" ] 22 | } 23 | 24 | @test "prometheus/PrometheusRules-server: assertEnabled with rules-defined=true" { 25 | cd `chart_dir` 26 | local output=$( (helm template \ 27 | --show-only templates/prometheus-prometheusrules.yaml \ 28 | --set 'serverTelemetry.prometheusRules.enabled=true' \ 29 | --set 'serverTelemetry.prometheusRules.rules[0].foo=bar' \ 30 | --set 'serverTelemetry.prometheusRules.rules[1].baz=qux' \ 31 | .) | tee /dev/stderr ) 32 | 33 | [ "$(echo "$output" | yq -r '.spec.groups | length')" = "1" ] 34 | [ "$(echo "$output" | yq -r '.spec.groups[0] | length')" = "2" ] 35 | [ "$(echo "$output" | yq -r '.spec.groups[0].name')" = "release-name-vault" ] 36 | [ "$(echo "$output" | yq -r '.spec.groups[0].rules | length')" = "2" ] 37 | [ "$(echo "$output" | yq -r '.spec.groups[0].rules[0].foo')" = "bar" ] 38 | [ "$(echo "$output" | yq -r '.spec.groups[0].rules[1].baz')" = "qux" ] 39 | } 40 | 41 | @test "prometheus/PrometheusRules-server: assertSelectors default" { 42 | cd `chart_dir` 43 | local output=$( (helm template \ 44 | --show-only templates/prometheus-prometheusrules.yaml \ 45 | --set 'serverTelemetry.prometheusRules.enabled=true' \ 46 | --set 'serverTelemetry.prometheusRules.rules[0].foo=bar' \ 47 | . ) | tee /dev/stderr) 48 | 49 | [ "$(echo "$output" | yq -r '.metadata.labels | length')" = "5" ] 50 | [ "$(echo "$output" | yq -r '.metadata.labels.release')" = "prometheus" ] 51 | } 52 | 53 | @test "prometheus/PrometheusRules-server: assertSelectors overrides" { 54 | cd `chart_dir` 55 | local output=$( (helm template \ 56 | --show-only templates/prometheus-prometheusrules.yaml \ 57 | --set 'serverTelemetry.prometheusRules.enabled=true' \ 58 | --set 'serverTelemetry.prometheusRules.rules[0].foo=bar' \ 59 | --set 'serverTelemetry.prometheusRules.selectors.baz=qux' \ 60 | --set 'serverTelemetry.prometheusRules.selectors.bar=foo' \ 61 | . ) | tee /dev/stderr) 62 | 63 | [ "$(echo "$output" | yq -r '.metadata.labels | length')" = "6" ] 64 | [ "$(echo "$output" | yq -r '.metadata.labels | has("app")')" = "false" ] 65 | [ "$(echo "$output" | yq -r '.metadata.labels | has("kube-prometheus-stack")')" = "false" ] 66 | [ "$(echo "$output" | yq -r '.metadata.labels.baz')" = "qux" ] 67 | [ "$(echo "$output" | yq -r '.metadata.labels.bar')" = "foo" ] 68 | } 69 | -------------------------------------------------------------------------------- /test/unit/csi-serviceaccount.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "csi/ServiceAccount: disabled by default" { 6 | cd `chart_dir` 7 | local actual=$( (helm template \ 8 | --show-only templates/csi-serviceaccount.yaml \ 9 | . || echo "---") | tee /dev/stderr | 10 | yq 'length > 0' | tee /dev/stderr) 11 | [ "${actual}" = "false" ] 12 | } 13 | 14 | @test "csi/ServiceAccount: enable with csi.enabled" { 15 | cd `chart_dir` 16 | local actual=$(helm template \ 17 | --show-only templates/csi-serviceaccount.yaml \ 18 | --set 'csi.enabled=true' \ 19 | . | tee /dev/stderr | 20 | yq 'length > 0' | tee /dev/stderr) 21 | [ "${actual}" = "true" ] 22 | } 23 | 24 | # serviceAccountName reference name 25 | @test "csi/daemonset: serviceAccountName name" { 26 | cd `chart_dir` 27 | local actual=$(helm template \ 28 | --show-only templates/csi-serviceaccount.yaml \ 29 | --set "csi.enabled=true" \ 30 | . | tee /dev/stderr | 31 | yq -r '.metadata.name' | tee /dev/stderr) 32 | [ "${actual}" = "release-name-vault-csi-provider" ] 33 | } 34 | 35 | # serviceAccountNamespace namespace 36 | @test "csi/daemonset: serviceAccountNamespace namespace" { 37 | cd `chart_dir` 38 | local actual=$(helm template \ 39 | --show-only templates/csi-serviceaccount.yaml \ 40 | --set "csi.enabled=true" \ 41 | --namespace foo \ 42 | . | tee /dev/stderr | 43 | yq -r '.metadata.namespace' | tee /dev/stderr) 44 | [ "${actual}" = "foo" ] 45 | local actual=$(helm template \ 46 | --show-only templates/csi-serviceaccount.yaml \ 47 | --set "csi.enabled=true" \ 48 | --set 'global.namespace=bar' \ 49 | --namespace foo \ 50 | . | tee /dev/stderr | 51 | yq -r '.metadata.namespace' | tee /dev/stderr) 52 | [ "${actual}" = "bar" ] 53 | } 54 | 55 | @test "csi/serviceAccount: specify annotations" { 56 | cd `chart_dir` 57 | local actual=$(helm template \ 58 | --show-only templates/server-serviceaccount.yaml \ 59 | --set 'csi.enabled=true' \ 60 | . | tee /dev/stderr | 61 | yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) 62 | [ "${actual}" = "null" ] 63 | 64 | local actual=$(helm template \ 65 | --show-only templates/server-serviceaccount.yaml \ 66 | --set 'csi.enabled=true' \ 67 | --set 'csi.serviceAccount.annotations=foo: bar' \ 68 | . | tee /dev/stderr | 69 | yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) 70 | [ "${actual}" = "null" ] 71 | 72 | local actual=$(helm template \ 73 | --show-only templates/server-serviceaccount.yaml \ 74 | --set 'csi.enabled=true' \ 75 | --set 'server.serviceAccount.annotations.foo=bar' \ 76 | . | tee /dev/stderr | 77 | yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) 78 | [ "${actual}" = "bar" ] 79 | } 80 | 81 | # serviceAccount extraLabels 82 | 83 | @test "csi/serviceAccount: specify csi.serviceAccount.extraLabels" { 84 | cd `chart_dir` 85 | local actual=$(helm template \ 86 | --show-only templates/csi-serviceaccount.yaml \ 87 | --set 'csi.enabled=true' \ 88 | --set 'csi.serviceAccount.extraLabels.foo=bar' \ 89 | . | tee /dev/stderr | 90 | yq -r '.metadata.labels.foo' | tee /dev/stderr) 91 | [ "${actual}" = "bar" ] 92 | } 93 | 94 | 95 | -------------------------------------------------------------------------------- /.github/actions/acceptance-test/action.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | name: Acceptance test 5 | description: Run the acceptance tests against a single version of k8s and Vault 6 | inputs: 7 | k8s-version: 8 | description: 'Kubernetes version to use for the kind cluster' 9 | required: true 10 | vault-version: 11 | description: 'Vault version to use for the tests' 12 | required: true 13 | vault-enterprise: 14 | description: 'Test against Vault Enterprise' 15 | required: true 16 | kind-cluster-name: 17 | description: 'Name of the kind cluster to create and test against' 18 | default: 'vault-helm' 19 | bats-version: 20 | description: 'Version of bats to run tests with' 21 | default: '1.12.0' 22 | vault-license: 23 | description: 'Vault license to use for enterprise tests' 24 | required: true 25 | runs: 26 | using: "composite" 27 | steps: 28 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 29 | - name: Setup test tools 30 | uses: ./.github/actions/setup-test-tools 31 | - name: Create K8s Kind Cluster 32 | uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0 33 | with: 34 | cluster_name: ${{ inputs.kind-cluster-name }} 35 | config: test/kind/config.yaml 36 | node_image: kindest/node:v${{ inputs.k8s-version }} 37 | version: "v0.29.0" 38 | 39 | - name: Create kind export log root 40 | id: create_kind_export_log_root 41 | shell: bash 42 | run: | 43 | vault_flavor=community 44 | if [ ${{ inputs.vault-enterprise }} == 'true' ]; then 45 | vault_flavor=ent 46 | fi 47 | log_artifact_name="kind-${{ inputs.kind-cluster-name }}-$(git rev-parse --short ${{ github.sha }})-${{ inputs.k8s-version }}-${{ inputs.vault-version }}-${vault_flavor}-helm-logs" 48 | log_root="/tmp/${log_artifact_name}" 49 | mkdir -p "${log_root}" 50 | echo "log_root=${log_root}" >> $GITHUB_OUTPUT 51 | echo "log_artifact_name=${log_artifact_name}" >> $GITHUB_OUTPUT 52 | 53 | - name: bats tests 54 | shell: bash 55 | env: 56 | VAULT_LICENSE_CI: ${{ inputs.vault-license }} 57 | VAULT_VERSION: ${{ inputs.vault-version }} 58 | ENT_TESTS: ${{ inputs.vault-enterprise }} 59 | run: bats --tap --timing ./test/acceptance 60 | 61 | - name: export kind cluster logs 62 | if: always() 63 | shell: bash 64 | run: | 65 | kind export logs --name ${{ inputs.kind-cluster-name }} ${{ steps.create_kind_export_log_root.outputs.log_root }} 66 | 67 | - name: Store kind cluster logs 68 | if: success() 69 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 70 | with: 71 | name: ${{ steps.create_kind_export_log_root.outputs.log_artifact_name }} 72 | path: ${{ steps.create_kind_export_log_root.outputs.log_root }} 73 | 74 | - name: Store kind cluster logs failure 75 | if: failure() 76 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 77 | with: 78 | name: ${{ steps.create_kind_export_log_root.outputs.log_artifact_name }}-failed 79 | path: ${{ steps.create_kind_export_log_root.outputs.log_root }} 80 | -------------------------------------------------------------------------------- /test/unit/server-clusterrolebinding.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "server/ClusterRoleBinding: enabled by default" { 6 | cd `chart_dir` 7 | local actual=$( (helm template \ 8 | --show-only templates/server-clusterrolebinding.yaml \ 9 | --set 'server.dev.enabled=true' \ 10 | . || echo "---") | tee /dev/stderr | 11 | yq 'length > 0' | tee /dev/stderr) 12 | [ "${actual}" = "true" ] 13 | 14 | local actual=$( (helm template \ 15 | --show-only templates/server-clusterrolebinding.yaml \ 16 | --set 'server.ha.enabled=true' \ 17 | . || echo "---") | tee /dev/stderr | 18 | yq 'length > 0' | tee /dev/stderr) 19 | [ "${actual}" = "true" ] 20 | 21 | local actual=$( (helm template \ 22 | --show-only templates/server-clusterrolebinding.yaml \ 23 | . || echo "---") | tee /dev/stderr | 24 | yq 'length > 0' | tee /dev/stderr) 25 | [ "${actual}" = "true" ] 26 | } 27 | 28 | @test "server/ClusterRoleBinding: disable with global.enabled" { 29 | cd `chart_dir` 30 | local actual=$( (helm template \ 31 | --show-only templates/server-clusterrolebinding.yaml \ 32 | --set 'global.enabled=false' \ 33 | . || echo "---") | tee /dev/stderr | 34 | yq 'length > 0' | tee /dev/stderr) 35 | [ "${actual}" = "false" ] 36 | } 37 | 38 | @test "server/ClusterRoleBinding: can disable with server.authDelegator" { 39 | cd `chart_dir` 40 | local actual=$( (helm template \ 41 | --show-only templates/server-clusterrolebinding.yaml \ 42 | --set 'server.authDelegator.enabled=false' \ 43 | . || echo "---") | tee /dev/stderr | 44 | yq 'length > 0' | tee /dev/stderr) 45 | [ "${actual}" = "false" ] 46 | 47 | local actual=$( (helm template \ 48 | --show-only templates/server-clusterrolebinding.yaml \ 49 | --set 'server.authDelegator.enabled=false' \ 50 | --set 'server.ha.enabled=true' \ 51 | . || echo "---") | tee /dev/stderr | 52 | yq 'length > 0' | tee /dev/stderr) 53 | [ "${actual}" = "false" ] 54 | 55 | local actual=$( (helm template \ 56 | --show-only templates/server-clusterrolebinding.yaml \ 57 | --set 'server.authDelegator.enabled=false' \ 58 | --set 'server.dev.enabled=true' \ 59 | . || echo "---") | tee /dev/stderr | 60 | yq 'length > 0' | tee /dev/stderr) 61 | [ "${actual}" = "false" ] 62 | } 63 | 64 | @test "server/ClusterRoleBinding: also deploy with injector.externalVaultAddr" { 65 | cd `chart_dir` 66 | local actual=$( (helm template \ 67 | --show-only templates/server-clusterrolebinding.yaml \ 68 | --set 'server.enabled=false' \ 69 | --set 'injector.externalVaultAddr=http://vault-outside' \ 70 | . || echo "---") | tee /dev/stderr | 71 | yq 'length > 0' | tee /dev/stderr) 72 | [ "${actual}" = "true" ] 73 | } 74 | 75 | @test "server/ClusterRoleBinding: service account namespace" { 76 | cd `chart_dir` 77 | local actual=$(helm template \ 78 | --show-only templates/server-clusterrolebinding.yaml \ 79 | --namespace foo \ 80 | . | tee /dev/stderr | 81 | yq -r '.subjects[0].namespace' | tee /dev/stderr) 82 | [ "${actual}" = "foo" ] 83 | local actual=$(helm template \ 84 | --show-only templates/server-clusterrolebinding.yaml \ 85 | --set 'global.namespace=bar' \ 86 | --namespace foo \ 87 | . | tee /dev/stderr | 88 | yq -r '.subjects[0].namespace' | tee /dev/stderr) 89 | [ "${actual}" = "bar" ] 90 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TEST_IMAGE?=vault-helm-test 2 | GOOGLE_CREDENTIALS?=vault-helm-test.json 3 | CLOUDSDK_CORE_PROJECT?=vault-helm-dev-246514 4 | # set to run a single test - e.g acceptance/server-ha-enterprise-dr.bats 5 | ACCEPTANCE_TESTS?=acceptance 6 | 7 | # filter bats unit tests to run. 8 | UNIT_TESTS_FILTER?='.*' 9 | 10 | # set to 'true' to run acceptance tests locally in a kind cluster 11 | LOCAL_ACCEPTANCE_TESTS?=false 12 | 13 | # kind cluster name 14 | KIND_CLUSTER_NAME?=vault-helm 15 | 16 | # kind k8s version 17 | KIND_K8S_VERSION?=v1.32.3 18 | 19 | # Generate json schema for chart values. See test/README.md for more details. 20 | values-schema: 21 | helm schema-gen values.yaml > values.schema.json 22 | 23 | test-image: 24 | @docker build --rm -t $(TEST_IMAGE) -f $(CURDIR)/test/docker/Test.dockerfile $(CURDIR) 25 | 26 | test-unit: 27 | @docker run --rm -it -v ${PWD}:/helm-test $(TEST_IMAGE) bats -f $(UNIT_TESTS_FILTER) /helm-test/test/unit 28 | 29 | test-bats: test-unit test-acceptance 30 | 31 | test: test-image test-bats 32 | 33 | # run acceptance tests on GKE 34 | # set google project/credential vars above 35 | test-acceptance: 36 | ifeq ($(LOCAL_ACCEPTANCE_TESTS),true) 37 | make setup-kind acceptance 38 | else 39 | @docker run -it -v ${PWD}:/helm-test \ 40 | -e GOOGLE_CREDENTIALS=${GOOGLE_CREDENTIALS} \ 41 | -e CLOUDSDK_CORE_PROJECT=${CLOUDSDK_CORE_PROJECT} \ 42 | -e KUBECONFIG=/helm-test/.kube/config \ 43 | -e VAULT_LICENSE_CI=${VAULT_LICENSE_CI} \ 44 | -w /helm-test \ 45 | $(TEST_IMAGE) \ 46 | make acceptance 47 | endif 48 | 49 | # destroy GKE cluster using terraform 50 | test-destroy: 51 | @docker run -it -v ${PWD}:/helm-test \ 52 | -e GOOGLE_CREDENTIALS=${GOOGLE_CREDENTIALS} \ 53 | -e CLOUDSDK_CORE_PROJECT=${CLOUDSDK_CORE_PROJECT} \ 54 | -w /helm-test \ 55 | $(TEST_IMAGE) \ 56 | make destroy-cluster 57 | 58 | # provision GKE cluster using terraform 59 | test-provision: 60 | @docker run -it -v ${PWD}:/helm-test \ 61 | -e GOOGLE_CREDENTIALS=${GOOGLE_CREDENTIALS} \ 62 | -e CLOUDSDK_CORE_PROJECT=${CLOUDSDK_CORE_PROJECT} \ 63 | -e KUBECONFIG=/helm-test/.kube/config \ 64 | -w /helm-test \ 65 | $(TEST_IMAGE) \ 66 | make provision-cluster 67 | 68 | # this target is for running the acceptance tests 69 | # it is run in the docker container above when the test-acceptance target is invoked 70 | acceptance: 71 | ifneq ($(LOCAL_ACCEPTANCE_TESTS),true) 72 | gcloud auth activate-service-account --key-file=${GOOGLE_CREDENTIALS} 73 | endif 74 | bats --tap --timing test/${ACCEPTANCE_TESTS} 75 | 76 | # this target is for provisioning the GKE cluster 77 | # it is run in the docker container above when the test-provision target is invoked 78 | provision-cluster: 79 | gcloud auth activate-service-account --key-file=${GOOGLE_CREDENTIALS} 80 | terraform init test/terraform 81 | terraform apply -var project=${CLOUDSDK_CORE_PROJECT} -var init_cli=true -auto-approve test/terraform 82 | 83 | # this target is for removing the GKE cluster 84 | # it is run in the docker container above when the test-destroy target is invoked 85 | destroy-cluster: 86 | terraform destroy -auto-approve 87 | 88 | # create a kind cluster for running the acceptance tests locally 89 | setup-kind: 90 | kind get clusters | grep -q "^${KIND_CLUSTER_NAME}$$" || \ 91 | kind create cluster \ 92 | --image kindest/node:${KIND_K8S_VERSION} \ 93 | --name ${KIND_CLUSTER_NAME} \ 94 | --config $(CURDIR)/test/kind/config.yaml 95 | kubectl config use-context kind-${KIND_CLUSTER_NAME} 96 | 97 | # delete the kind cluster 98 | delete-kind: 99 | kind delete cluster --name ${KIND_CLUSTER_NAME} || : 100 | 101 | .PHONY: values-schema test-image test-unit test-bats test test-acceptance test-destroy test-provision acceptance provision-cluster destroy-cluster 102 | -------------------------------------------------------------------------------- /test/unit/server-headless-service.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "server/headless-Service: publishNotReadyAddresses cannot be changed" { 6 | cd `chart_dir` 7 | local actual=$(helm template \ 8 | --show-only templates/server-headless-service.yaml \ 9 | . | tee /dev/stderr | 10 | yq -r '.spec.publishNotReadyAddresses' | tee /dev/stderr) 11 | [ "${actual}" = "true" ] 12 | 13 | local actual=$(helm template \ 14 | --show-only templates/server-headless-service.yaml \ 15 | --set 'server.service.publishNotReadyAddresses=false' \ 16 | . | tee /dev/stderr | 17 | yq -r '.spec.publishNotReadyAddresses' | tee /dev/stderr) 18 | [ "${actual}" = "true" ] 19 | } 20 | 21 | @test "server/headless-Service: instance selector cannot be disabled" { 22 | cd `chart_dir` 23 | local actual=$(helm template \ 24 | --show-only templates/server-headless-service.yaml \ 25 | --set 'server.ha.enabled=true' \ 26 | . | tee /dev/stderr | 27 | yq -r '.spec.selector["app.kubernetes.io/instance"]' | tee /dev/stderr) 28 | [ "${actual}" = "release-name" ] 29 | 30 | local actual=$(helm template \ 31 | --show-only templates/server-headless-service.yaml \ 32 | --set 'server.ha.enabled=true' \ 33 | --set 'server.service.instanceSelector.enabled=false' \ 34 | . | tee /dev/stderr | 35 | yq -r '.spec.selector["app.kubernetes.io/instance"]' | tee /dev/stderr) 36 | [ "${actual}" = "release-name" ] 37 | } 38 | 39 | @test "server/headless-Service: namespace" { 40 | cd `chart_dir` 41 | local actual=$(helm template \ 42 | --show-only templates/server-headless-service.yaml \ 43 | --set 'server.ha.enabled=true' \ 44 | --namespace foo \ 45 | . | tee /dev/stderr | 46 | yq -r '.metadata.namespace' | tee /dev/stderr) 47 | [ "${actual}" = "foo" ] 48 | local actual=$(helm template \ 49 | --show-only templates/server-headless-service.yaml \ 50 | --set 'server.ha.enabled=true' \ 51 | --set 'global.namespace=bar' \ 52 | --namespace foo \ 53 | . | tee /dev/stderr | 54 | yq -r '.metadata.namespace' | tee /dev/stderr) 55 | [ "${actual}" = "bar" ] 56 | } 57 | 58 | @test "server/headless-Service: Assert ipFamilyPolicy set" { 59 | cd `chart_dir` 60 | local actual=$(helm template \ 61 | --show-only templates/server-headless-service.yaml \ 62 | --set 'server.service.ipFamilyPolicy=PreferDualStack' \ 63 | . | tee /dev/stderr | 64 | yq -r '.spec.ipFamilyPolicy' | tee /dev/stderr) 65 | [ "${actual}" = "PreferDualStack" ] 66 | } 67 | 68 | @test "server/headless-Service: Assert ipFamilies set" { 69 | cd `chart_dir` 70 | local actual=$(helm template \ 71 | --show-only templates/server-headless-service.yaml \ 72 | --set 'server.service.ipFamilies={IPv4,IPv6}' \ 73 | . | tee /dev/stderr | 74 | yq '.spec.ipFamilies' -c | tee /dev/stderr) 75 | [ "${actual}" = '["IPv4","IPv6"]' ] 76 | } 77 | 78 | @test "server/headless-Service: Assert ipFamilyPolicy is not set if version below 1.23" { 79 | cd `chart_dir` 80 | local actual=$(helm template \ 81 | --show-only templates/server-headless-service.yaml \ 82 | --kube-version 1.22.0 \ 83 | --set 'server.service.ipFamilyPolicy=PreferDualStack' \ 84 | . | tee /dev/stderr | 85 | yq -r '.spec.ipFamilyPolicy' | tee /dev/stderr) 86 | [ "${actual}" = "null" ] 87 | } 88 | 89 | @test "server/headless-Service: Assert ipFamilies is not set if version below 1.23" { 90 | cd `chart_dir` 91 | local actual=$(helm template \ 92 | --show-only templates/server-headless-service.yaml \ 93 | --kube-version 1.22.0 \ 94 | --set 'server.service.ipFamilies={IPv4,IPv6}' \ 95 | . | tee /dev/stderr | 96 | yq -r '.spec.ipFamilies' | tee /dev/stderr) 97 | [ "${actual}" = "null" ] 98 | } -------------------------------------------------------------------------------- /test/acceptance/csi.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "csi: testing deployment" { 6 | cd `chart_dir` 7 | 8 | kubectl delete namespace acceptance --ignore-not-found=true 9 | kubectl create namespace acceptance 10 | kubectl config set-context --current --namespace=acceptance 11 | eval "${PRE_CHART_CMDS}" 12 | 13 | # Install Secrets Store CSI driver 14 | # Configure it to pass in a JWT for the provider to use, and rotate secrets rapidly 15 | # so we can see Agent's cache working. 16 | CSI_DRIVER_VERSION=1.5.3 17 | helm install secrets-store-csi-driver secrets-store-csi-driver \ 18 | --repo https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts \ 19 | --version=$CSI_DRIVER_VERSION \ 20 | --wait --timeout=5m \ 21 | --namespace=acceptance \ 22 | --set linux.image.pullPolicy="IfNotPresent" \ 23 | --set tokenRequests[0].audience="vault" \ 24 | --set enableSecretRotation=true \ 25 | --set rotationPollInterval=5s 26 | # Install Vault and Vault provider 27 | helm install vault . \ 28 | --wait --timeout=5m \ 29 | --namespace=acceptance \ 30 | --set="server.dev.enabled=true" \ 31 | --set="csi.enabled=true" \ 32 | --set="csi.logLevel=debug" \ 33 | --set="csi.agent.logLevel=debug" \ 34 | --set="injector.enabled=false" \ 35 | ${SET_CHART_VALUES} 36 | check_vault_versions vault 37 | 38 | kubectl --namespace=acceptance wait --for=condition=Ready --timeout=5m pod -l app.kubernetes.io/name=vault 39 | kubectl --namespace=acceptance wait --for=condition=Ready --timeout=5m pod -l app.kubernetes.io/name=vault-csi-provider 40 | 41 | # Set up k8s auth and a kv secret. 42 | cat ./test/acceptance/csi-test/vault-policy.hcl | kubectl --namespace=acceptance exec -i vault-0 -- vault policy write kv-policy - 43 | kubectl --namespace=acceptance exec vault-0 -- vault auth enable kubernetes 44 | kubectl --namespace=acceptance exec vault-0 -- sh -c 'vault write auth/kubernetes/config \ 45 | kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443"' 46 | kubectl --namespace=acceptance exec vault-0 -- vault write auth/kubernetes/role/kv-role \ 47 | bound_service_account_names=nginx \ 48 | bound_service_account_namespaces=acceptance \ 49 | policies=kv-policy \ 50 | ttl=20m 51 | kubectl --namespace=acceptance exec vault-0 -- vault kv put secret/kv1 bar1=hello1 52 | 53 | kubectl --namespace=acceptance apply -f ./test/acceptance/csi-test/vault-kv-secretproviderclass.yaml 54 | kubectl --namespace=acceptance apply -f ./test/acceptance/csi-test/nginx.yaml 55 | kubectl --namespace=acceptance wait --for=condition=Ready --timeout=5m pod nginx 56 | 57 | result=$(kubectl --namespace=acceptance exec nginx -- cat /mnt/secrets-store/bar) 58 | [[ "$result" == "hello1" ]] 59 | 60 | for i in $(seq 10); do 61 | sleep 2 62 | if [ "$(kubectl --namespace=acceptance logs --tail=-1 -l "app.kubernetes.io/name=vault-csi-provider" -c vault-agent | grep "secret renewed: path=/v1/auth/kubernetes/login")" ]; then 63 | echo "Agent returned a cached login response" 64 | return 65 | fi 66 | 67 | echo "Waiting to confirm the Agent is renewing CSI's auth token..." 68 | done 69 | 70 | # Print the logs and fail the test 71 | echo "Failed to find a log for the Agent renewing CSI's auth token" 72 | kubectl --namespace=acceptance logs --tail=-1 -l "app.kubernetes.io/name=vault-csi-provider" -c vault-agent 73 | kubectl --namespace=acceptance logs --tail=-1 -l "app.kubernetes.io/name=vault-csi-provider" -c vault-csi-provider 74 | exit 1 75 | } 76 | 77 | # Clean up 78 | teardown() { 79 | if [[ ${CLEANUP:-true} == "true" ]] 80 | then 81 | echo "helm/pvc teardown" 82 | helm --namespace=acceptance delete vault 83 | helm --namespace=acceptance delete secrets-store-csi-driver 84 | kubectl delete --all pvc 85 | kubectl delete namespace acceptance 86 | kubectl config unset contexts."$(kubectl config current-context)".namespace 87 | fi 88 | } 89 | -------------------------------------------------------------------------------- /test/acceptance/server.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "server/standalone: testing deployment" { 6 | cd `chart_dir` 7 | 8 | kubectl delete namespace acceptance --ignore-not-found=true 9 | kubectl create namespace acceptance 10 | kubectl config set-context --current --namespace=acceptance 11 | 12 | eval "${PRE_CHART_CMDS}" 13 | helm install "$(name_prefix)" . ${SET_CHART_VALUES} 14 | check_vault_versions "$(name_prefix)" 15 | wait_for_running "$(name_prefix)-0" 16 | 17 | # Sealed, not initialized 18 | wait_for_sealed_vault "$(name_prefix)-0" 19 | 20 | local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | 21 | jq -r '.initialized') 22 | [ "${init_status}" == "false" ] 23 | 24 | # Replicas 25 | local replicas=$(kubectl get statefulset "$(name_prefix)" --output json | 26 | jq -r '.spec.replicas') 27 | [ "${replicas}" == "1" ] 28 | 29 | # Affinity 30 | local affinity=$(kubectl get statefulset "$(name_prefix)" --output json | 31 | jq -r '.spec.template.spec.affinity') 32 | [ "${affinity}" != "null" ] 33 | 34 | # Volume Mounts 35 | local volumeCount=$(kubectl get statefulset "$(name_prefix)" --output json | 36 | jq -r '.spec.template.spec.containers[0].volumeMounts | length >= 3') 37 | [ "${volumeCount}" == "true" ] 38 | 39 | local mountName=$(kubectl get statefulset "$(name_prefix)" --output json | 40 | jq -r '.spec.template.spec.containers[0].volumeMounts[0].name') 41 | [ "${mountName}" == "data" ] 42 | 43 | local mountPath=$(kubectl get statefulset "$(name_prefix)" --output json | 44 | jq -r '.spec.template.spec.containers[0].volumeMounts[0].mountPath') 45 | [ "${mountPath}" == "/vault/data" ] 46 | 47 | # Volumes 48 | local volumeCount=$(kubectl get statefulset "$(name_prefix)" --output json | 49 | jq -r '.spec.template.spec.volumes | length >= 2') 50 | [ "${volumeCount}" == "true" ] 51 | 52 | local volume=$(kubectl get statefulset "$(name_prefix)" --output json | 53 | jq -r '.spec.template.spec.volumes[0].configMap.name') 54 | [ "${volume}" == "$(name_prefix)-config" ] 55 | 56 | # Service 57 | local service=$(kubectl get service "$(name_prefix)" --output json | 58 | jq -r '.spec.clusterIP') 59 | [ "${service}" != "None" ] 60 | 61 | local service=$(kubectl get service "$(name_prefix)" --output json | 62 | jq -r '.spec.type') 63 | [ "${service}" == "ClusterIP" ] 64 | 65 | local ports=$(kubectl get service "$(name_prefix)" --output json | 66 | jq -r '.spec.ports | length') 67 | [ "${ports}" == "2" ] 68 | 69 | local ports=$(kubectl get service "$(name_prefix)" --output json | 70 | jq -r '.spec.ports[0].port') 71 | [ "${ports}" == "8200" ] 72 | 73 | local ports=$(kubectl get service "$(name_prefix)" --output json | 74 | jq -r '.spec.ports[1].port') 75 | [ "${ports}" == "8201" ] 76 | 77 | # Vault Init 78 | local token=$(kubectl exec -ti "$(name_prefix)-0" -- \ 79 | vault operator init -format=json -n 1 -t 1 | \ 80 | jq -r '.unseal_keys_b64[0]') 81 | [ "${token}" != "" ] 82 | 83 | # Vault Unseal 84 | local pods=($(kubectl get pods --selector='app.kubernetes.io/name=vault' -o json | jq -r '.items[].metadata.name')) 85 | for pod in "${pods[@]}" 86 | do 87 | kubectl exec -ti ${pod} -- vault operator unseal ${token} 88 | done 89 | 90 | wait_for_ready "$(name_prefix)-0" 91 | 92 | # Unsealed, initialized 93 | local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | 94 | jq -r '.sealed' ) 95 | [ "${sealed_status}" == "false" ] 96 | 97 | local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | 98 | jq -r '.initialized') 99 | [ "${init_status}" == "true" ] 100 | } 101 | 102 | # Clean up 103 | teardown() { 104 | if [[ ${CLEANUP:-true} == "true" ]] 105 | then 106 | echo "helm/pvc teardown" 107 | helm delete vault 108 | kubectl delete --all pvc 109 | kubectl delete namespace acceptance --ignore-not-found=true 110 | kubectl config unset contexts."$(kubectl config current-context)".namespace 111 | fi 112 | } 113 | -------------------------------------------------------------------------------- /test/acceptance/server-ha.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "server/ha: testing deployment" { 6 | cd `chart_dir` 7 | 8 | helm install "$(name_prefix)" . \ 9 | --set='server.ha.enabled=true' \ 10 | ${SET_CHART_VALUES} 11 | check_vault_versions "$(name_prefix)" 12 | wait_for_running $(name_prefix)-0 13 | 14 | # Sealed, not initialized 15 | wait_for_sealed_vault $(name_prefix)-0 16 | 17 | local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | 18 | jq -r '.initialized') 19 | [ "${init_status}" == "false" ] 20 | 21 | # Replicas 22 | local replicas=$(kubectl get statefulset "$(name_prefix)" --output json | 23 | jq -r '.spec.replicas') 24 | [ "${replicas}" == "3" ] 25 | 26 | # Volume Mounts 27 | local volumeCount=$(kubectl get statefulset "$(name_prefix)" --output json | 28 | jq -r '.spec.template.spec.containers[0].volumeMounts | length >= 2') 29 | [ "${volumeCount}" == "true" ] 30 | 31 | # Volumes 32 | local volumeCount=$(kubectl get statefulset "$(name_prefix)" --output json | 33 | jq -r '.spec.template.spec.volumes | length >= 2') 34 | [ "${volumeCount}" == "true" ] 35 | 36 | local volume=$(kubectl get statefulset "$(name_prefix)" --output json | 37 | jq -r '.spec.template.spec.volumes[0].configMap.name') 38 | [ "${volume}" == "$(name_prefix)-config" ] 39 | 40 | # Service 41 | local service=$(kubectl get service "$(name_prefix)" --output json | 42 | jq -r '.spec.clusterIP') 43 | [ "${service}" != "None" ] 44 | 45 | local service=$(kubectl get service "$(name_prefix)" --output json | 46 | jq -r '.spec.type') 47 | [ "${service}" == "ClusterIP" ] 48 | 49 | local ports=$(kubectl get service "$(name_prefix)" --output json | 50 | jq -r '.spec.ports | length') 51 | [ "${ports}" == "2" ] 52 | 53 | local ports=$(kubectl get service "$(name_prefix)" --output json | 54 | jq -r '.spec.ports[0].port') 55 | [ "${ports}" == "8200" ] 56 | 57 | local ports=$(kubectl get service "$(name_prefix)" --output json | 58 | jq -r '.spec.ports[1].port') 59 | [ "${ports}" == "8201" ] 60 | 61 | # Vault Init 62 | local token=$(kubectl exec -ti "$(name_prefix)-0" -- \ 63 | vault operator init -format=json -n 1 -t 1 | \ 64 | jq -r '.unseal_keys_b64[0]') 65 | [ "${token}" != "" ] 66 | 67 | # Vault Unseal 68 | local pods=($(kubectl get pods --selector='app.kubernetes.io/name=vault' -o json | jq -r '.items[].metadata.name')) 69 | for pod in "${pods[@]}" 70 | do 71 | kubectl exec -ti ${pod} -- vault operator unseal ${token} 72 | done 73 | 74 | wait_for_ready "$(name_prefix)-0" 75 | 76 | # Sealed, not initialized 77 | local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | 78 | jq -r '.sealed' ) 79 | [ "${sealed_status}" == "false" ] 80 | 81 | local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | 82 | jq -r '.initialized') 83 | [ "${init_status}" == "true" ] 84 | } 85 | 86 | # setup a consul env 87 | setup() { 88 | kubectl delete namespace acceptance --ignore-not-found=true 89 | kubectl create namespace acceptance 90 | kubectl config set-context --current --namespace=acceptance 91 | 92 | helm repo add hashicorp https://helm.releases.hashicorp.com 93 | helm repo update 94 | 95 | CONSUL_HELM_VERSION=v0.48.0 96 | 97 | K8S_MAJOR=$(kubectl version --output=json | jq -r .serverVersion.major) 98 | K8S_MINOR=$(kubectl version --output=json | jq -r .serverVersion.minor) 99 | if [ \( $K8S_MAJOR -eq 1 \) -a \( $K8S_MINOR -le 20 \) ]; then 100 | CONSUL_HELM_VERSION=v0.32.1 101 | fi 102 | helm install consul hashicorp/consul \ 103 | --version $CONSUL_HELM_VERSION \ 104 | --set 'ui.enabled=false' 105 | 106 | wait_for_running_consul 107 | eval "${PRE_CHART_CMDS}" 108 | } 109 | 110 | #cleanup 111 | teardown() { 112 | if [[ ${CLEANUP:-true} == "true" ]] 113 | then 114 | # If the test failed, print some debug output 115 | if [[ "$BATS_ERROR_STATUS" -ne 0 ]]; then 116 | kubectl logs -l app=consul 117 | kubectl logs -l app.kubernetes.io/name=vault 118 | fi 119 | helm delete vault 120 | helm delete consul 121 | kubectl delete --all pvc 122 | kubectl delete namespace acceptance --ignore-not-found=true 123 | kubectl config unset contexts."$(kubectl config current-context)".namespace 124 | fi 125 | } 126 | -------------------------------------------------------------------------------- /test/acceptance/server-ha-raft.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "server/ha-raft: testing deployment" { 6 | cd `chart_dir` 7 | 8 | helm install "$(name_prefix)" . \ 9 | --set='server.ha.enabled=true' \ 10 | --set='server.ha.raft.enabled=true' \ 11 | ${SET_CHART_VALUES} 12 | check_vault_versions "$(name_prefix)" 13 | wait_for_running $(name_prefix)-0 14 | 15 | # Sealed, not initialized 16 | wait_for_sealed_vault $(name_prefix)-0 17 | 18 | local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | 19 | jq -r '.initialized') 20 | [ "${init_status}" == "false" ] 21 | 22 | # Replicas 23 | local replicas=$(kubectl get statefulset "$(name_prefix)" --output json | 24 | jq -r '.spec.replicas') 25 | [ "${replicas}" == "3" ] 26 | 27 | # Volume Mounts 28 | local volumeCount=$(kubectl get statefulset "$(name_prefix)" --output json | 29 | jq -r '.spec.template.spec.containers[0].volumeMounts | length >= 3') 30 | [ "${volumeCount}" == "true" ] 31 | 32 | # Volumes 33 | local volumeCount=$(kubectl get statefulset "$(name_prefix)" --output json | 34 | jq -r '.spec.template.spec.volumes | length >= 2') 35 | [ "${volumeCount}" == "true" ] 36 | 37 | local volume=$(kubectl get statefulset "$(name_prefix)" --output json | 38 | jq -r '.spec.template.spec.volumes[0].configMap.name') 39 | [ "${volume}" == "$(name_prefix)-config" ] 40 | 41 | # Service 42 | local service=$(kubectl get service "$(name_prefix)" --output json | 43 | jq -r '.spec.clusterIP') 44 | [ "${service}" != "None" ] 45 | 46 | local service=$(kubectl get service "$(name_prefix)" --output json | 47 | jq -r '.spec.type') 48 | [ "${service}" == "ClusterIP" ] 49 | 50 | local ports=$(kubectl get service "$(name_prefix)" --output json | 51 | jq -r '.spec.ports | length') 52 | [ "${ports}" == "2" ] 53 | 54 | local ports=$(kubectl get service "$(name_prefix)" --output json | 55 | jq -r '.spec.ports[0].port') 56 | [ "${ports}" == "8200" ] 57 | 58 | local ports=$(kubectl get service "$(name_prefix)" --output json | 59 | jq -r '.spec.ports[1].port') 60 | [ "${ports}" == "8201" ] 61 | 62 | # Vault Init 63 | local init=$(kubectl exec -ti "$(name_prefix)-0" -- \ 64 | vault operator init -format=json -n 1 -t 1) 65 | 66 | local token=$(echo ${init} | jq -r '.unseal_keys_b64[0]') 67 | [ "${token}" != "" ] 68 | 69 | local root=$(echo ${init} | jq -r '.root_token') 70 | [ "${root}" != "" ] 71 | 72 | kubectl exec -ti vault-0 -- vault operator unseal ${token} 73 | wait_for_ready "$(name_prefix)-0" 74 | 75 | sleep 5 76 | 77 | # Vault Unseal 78 | local pods=($(kubectl get pods --selector='app.kubernetes.io/name=vault' -o json | jq -r '.items[].metadata.name')) 79 | for pod in "${pods[@]}" 80 | do 81 | if [[ ${pod?} != "$(name_prefix)-0" ]] 82 | then 83 | kubectl exec -ti ${pod} -- vault operator raft join http://$(name_prefix)-0.$(name_prefix)-internal:8200 84 | kubectl exec -ti ${pod} -- vault operator unseal ${token} 85 | wait_for_ready "${pod}" 86 | fi 87 | done 88 | 89 | # Sealed, not initialized 90 | local sealed_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | 91 | jq -r '.sealed' ) 92 | [ "${sealed_status}" == "false" ] 93 | 94 | local init_status=$(kubectl exec "$(name_prefix)-0" -- vault status -format=json | 95 | jq -r '.initialized') 96 | [ "${init_status}" == "true" ] 97 | 98 | kubectl exec "$(name_prefix)-0" -- vault login ${root} 99 | 100 | local raft_status=$(kubectl exec "$(name_prefix)-0" -- vault operator raft list-peers -format=json | 101 | jq -r '.data.config.servers | length') 102 | [ "${raft_status}" == "3" ] 103 | } 104 | 105 | setup() { 106 | kubectl delete namespace acceptance --ignore-not-found=true 107 | kubectl create namespace acceptance 108 | kubectl config set-context --current --namespace=acceptance 109 | eval "${PRE_CHART_CMDS}" 110 | } 111 | 112 | #cleanup 113 | teardown() { 114 | if [[ ${CLEANUP:-true} == "true" ]] 115 | then 116 | # If the test failed, print some debug output 117 | if [[ "$BATS_ERROR_STATUS" -ne 0 ]]; then 118 | kubectl logs -l app.kubernetes.io/name=vault 119 | fi 120 | helm delete vault 121 | kubectl delete --all pvc 122 | kubectl delete namespace acceptance --ignore-not-found=true 123 | kubectl config unset contexts."$(kubectl config current-context)".namespace 124 | fi 125 | } 126 | -------------------------------------------------------------------------------- /test/unit/server-psp-role.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "server/PSP-Role: PSP-Role not enabled by default" { 6 | cd `chart_dir` 7 | local actual=$( (helm template \ 8 | --show-only templates/server-psp-role.yaml \ 9 | --set 'server.dev.enabled=true' \ 10 | . || echo "---") | tee /dev/stderr | 11 | yq 'length > 0' | tee /dev/stderr) 12 | [ "${actual}" = "false" ] 13 | 14 | local actual=$( (helm template \ 15 | --show-only templates/server-psp-role.yaml \ 16 | --set 'server.ha.enabled=true' \ 17 | . || echo "---") | tee /dev/stderr | 18 | yq 'length > 0' | tee /dev/stderr) 19 | [ "${actual}" = "false" ] 20 | 21 | local actual=$( (helm template \ 22 | --show-only templates/server-psp-role.yaml \ 23 | --set 'server.standalone.enabled=true' \ 24 | . || echo "---") | tee /dev/stderr | 25 | yq 'length > 0' | tee /dev/stderr) 26 | [ "${actual}" = "false" ] 27 | } 28 | 29 | @test "server/PSP-Role: PSP-Role can be enabled" { 30 | cd `chart_dir` 31 | local actual=$(helm template \ 32 | --show-only templates/server-psp-role.yaml \ 33 | --set 'server.dev.enabled=true' \ 34 | --set 'global.psp.enable=true' \ 35 | . | tee /dev/stderr | 36 | yq 'length > 0' | tee /dev/stderr) 37 | [ "${actual}" = "true" ] 38 | 39 | local actual=$(helm template \ 40 | --show-only templates/server-psp-role.yaml \ 41 | --set 'server.ha.enabled=true' \ 42 | --set 'global.psp.enable=true' \ 43 | . | tee /dev/stderr | 44 | yq 'length > 0' | tee /dev/stderr) 45 | [ "${actual}" = "true" ] 46 | 47 | local actual=$(helm template \ 48 | --show-only templates/server-psp-role.yaml \ 49 | --set 'server.standalone.enabled=true' \ 50 | --set 'global.psp.enable=true' \ 51 | . | tee /dev/stderr | 52 | yq 'length > 0' | tee /dev/stderr) 53 | [ "${actual}" = "true" ] 54 | } 55 | 56 | @test "server/PSP-Role: disable with global.enabled false" { 57 | cd `chart_dir` 58 | local actual=$( (helm template \ 59 | --show-only templates/server-psp-role.yaml \ 60 | --set 'server.dev.enabled=true' \ 61 | --set 'global.enabled=false' \ 62 | --set 'global.psp.enable=true' \ 63 | . || echo "---") | tee /dev/stderr | 64 | yq 'length > 0' | tee /dev/stderr) 65 | [ "${actual}" = "false" ] 66 | 67 | local actual=$( (helm template \ 68 | --show-only templates/server-psp-role.yaml \ 69 | --set 'server.ha.enabled=true' \ 70 | --set 'global.enabled=false' \ 71 | --set 'global.psp.enable=true' \ 72 | . || echo "---") | tee /dev/stderr | 73 | yq 'length > 0' | tee /dev/stderr) 74 | [ "${actual}" = "false" ] 75 | 76 | local actual=$( (helm template \ 77 | --show-only templates/server-psp-role.yaml \ 78 | --set 'server.standalone.enabled=true' \ 79 | --set 'global.enabled=false' \ 80 | --set 'global.psp.enable=true' \ 81 | . || echo "---") | tee /dev/stderr | 82 | yq 'length > 0' | tee /dev/stderr) 83 | [ "${actual}" = "false" ] 84 | } 85 | 86 | @test "server/PSP-Role: disable with global.psp.enable false" { 87 | cd `chart_dir` 88 | local actual=$( (helm template \ 89 | --show-only templates/server-psp-role.yaml \ 90 | --set 'server.dev.enabled=true' \ 91 | --set 'global.psp.enable=false' \ 92 | . || echo "---") | tee /dev/stderr | 93 | yq 'length > 0' | tee /dev/stderr) 94 | [ "${actual}" = "false" ] 95 | 96 | local actual=$( (helm template \ 97 | --show-only templates/server-psp-role.yaml \ 98 | --set 'server.ha.enabled=true' \ 99 | --set 'global.psp.enable=false' \ 100 | . || echo "---") | tee /dev/stderr | 101 | yq 'length > 0' | tee /dev/stderr) 102 | [ "${actual}" = "false" ] 103 | 104 | local actual=$( (helm template \ 105 | --show-only templates/server-psp-role.yaml \ 106 | --set 'server.standalone.enabled=true' \ 107 | --set 'global.psp.enable=false' \ 108 | . || echo "---") | tee /dev/stderr | 109 | yq 'length > 0' | tee /dev/stderr) 110 | [ "${actual}" = "false" ] 111 | } 112 | 113 | @test "server/PSP-Role: namespace" { 114 | cd `chart_dir` 115 | local actual=$(helm template \ 116 | --show-only templates/server-psp-role.yaml \ 117 | --set 'global.psp.enable=true' \ 118 | --namespace foo \ 119 | . | tee /dev/stderr | 120 | yq -r '.metadata.namespace' | tee /dev/stderr) 121 | [ "${actual}" = "foo" ] 122 | local actual=$(helm template \ 123 | --show-only templates/server-psp-role.yaml \ 124 | --set 'global.psp.enable=true' \ 125 | --set 'global.namespace=bar' \ 126 | --namespace foo \ 127 | . | tee /dev/stderr | 128 | yq -r '.metadata.namespace' | tee /dev/stderr) 129 | [ "${actual}" = "bar" ] 130 | } -------------------------------------------------------------------------------- /test/unit/server-ha-disruptionbudget.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "server/DisruptionBudget: enabled by default" { 6 | cd `chart_dir` 7 | local actual=$(helm template \ 8 | --show-only templates/server-disruptionbudget.yaml \ 9 | --set 'server.ha.enabled=true' \ 10 | . | tee /dev/stderr | 11 | yq 'length > 0' | tee /dev/stderr) 12 | [ "${actual}" = "true" ] 13 | } 14 | 15 | @test "server/DisruptionBudget: disable with server.enabled" { 16 | cd `chart_dir` 17 | local actual=$( (helm template \ 18 | --show-only templates/server-disruptionbudget.yaml \ 19 | --set 'global.enabled=false' \ 20 | --set 'server.ha.enabled=false' \ 21 | . || echo "---") | tee /dev/stderr | 22 | yq 'length > 0' | tee /dev/stderr) 23 | [ "${actual}" = "false" ] 24 | } 25 | 26 | @test "server/DisruptionBudget: disable with server.disruptionBudget.enabled" { 27 | cd `chart_dir` 28 | local actual=$( (helm template \ 29 | --show-only templates/server-disruptionbudget.yaml \ 30 | --set 'server.ha.disruptionBudget.enabled=false' \ 31 | . || echo "---") | tee /dev/stderr | 32 | yq 'length > 0' | tee /dev/stderr) 33 | [ "${actual}" = "false" ] 34 | } 35 | 36 | @test "server/DisruptionBudget: disable with global.enabled" { 37 | cd `chart_dir` 38 | local actual=$( (helm template \ 39 | --show-only templates/server-disruptionbudget.yaml \ 40 | --set 'global.enabled=false' \ 41 | . || echo "---") | tee /dev/stderr | 42 | yq 'length > 0' | tee /dev/stderr) 43 | [ "${actual}" = "false" ] 44 | } 45 | 46 | @test "server/DisruptionBudget: disable with injector.exernalVaultAddr" { 47 | cd `chart_dir` 48 | local actual=$( (helm template \ 49 | --show-only templates/server-disruptionbudget.yaml \ 50 | --set 'injector.externalVaultAddr=http://vault-outside' \ 51 | . || echo "---") | tee /dev/stderr | 52 | yq 'length > 0' | tee /dev/stderr) 53 | [ "${actual}" = "false" ] 54 | } 55 | 56 | @test "server/DisruptionBudget: namespace" { 57 | cd `chart_dir` 58 | local actual=$(helm template \ 59 | --show-only templates/server-disruptionbudget.yaml \ 60 | --set 'server.ha.enabled=true' \ 61 | --namespace foo \ 62 | . | tee /dev/stderr | 63 | yq -r '.metadata.namespace' | tee /dev/stderr) 64 | [ "${actual}" = "foo" ] 65 | local actual=$(helm template \ 66 | --show-only templates/server-disruptionbudget.yaml \ 67 | --set 'server.ha.enabled=true' \ 68 | --set 'global.namespace=bar' \ 69 | --namespace foo \ 70 | . | tee /dev/stderr | 71 | yq -r '.metadata.namespace' | tee /dev/stderr) 72 | [ "${actual}" = "bar" ] 73 | } 74 | 75 | @test "server/DisruptionBudget: correct maxUnavailable with n=1" { 76 | cd `chart_dir` 77 | local actual=$(helm template \ 78 | --show-only templates/server-disruptionbudget.yaml \ 79 | --set 'server.ha.enabled=true' \ 80 | --set 'server.ha.replicas=1' \ 81 | . | tee /dev/stderr | 82 | yq '.spec.maxUnavailable' | tee /dev/stderr) 83 | [ "${actual}" = "0" ] 84 | } 85 | 86 | @test "server/DisruptionBudget: correct maxUnavailable with n=3" { 87 | cd `chart_dir` 88 | local actual=$(helm template \ 89 | --show-only templates/server-disruptionbudget.yaml \ 90 | --set 'server.ha.enabled=true' \ 91 | --set 'server.ha.replicas=3' \ 92 | . | tee /dev/stderr | 93 | yq '.spec.maxUnavailable' | tee /dev/stderr) 94 | [ "${actual}" = "1" ] 95 | } 96 | 97 | @test "server/DisruptionBudget: correct maxUnavailable with n=5" { 98 | cd `chart_dir` 99 | local actual=$(helm template \ 100 | --show-only templates/server-disruptionbudget.yaml \ 101 | --set 'server.ha.enabled=true' \ 102 | --set 'server.ha.replicas=5' \ 103 | . | tee /dev/stderr | 104 | yq '.spec.maxUnavailable' | tee /dev/stderr) 105 | [ "${actual}" = "2" ] 106 | } 107 | 108 | @test "server/DisruptionBudget: correct maxUnavailable with custom value" { 109 | cd `chart_dir` 110 | local actual=$(helm template \ 111 | --show-only templates/server-disruptionbudget.yaml \ 112 | --set 'server.ha.enabled=true' \ 113 | --set 'server.ha.replicas=3' \ 114 | --set 'server.ha.disruptionBudget.maxUnavailable=2' \ 115 | . | tee /dev/stderr | 116 | yq '.spec.maxUnavailable' | tee /dev/stderr) 117 | [ "${actual}" = "2" ] 118 | } 119 | 120 | @test "server/DisruptionBudget: apiVersion is set correctly >= version 1.21 of kube" { 121 | cd `chart_dir` 122 | local actual=$(helm template \ 123 | --show-only templates/server-disruptionbudget.yaml \ 124 | --set 'server.ha.enabled=true' \ 125 | --set 'server.ha.replicas=1' \ 126 | --kube-version 1.22.5 \ 127 | . | tee /dev/stderr | 128 | yq '.apiVersion == "policy/v1"' | tee /dev/stderr) 129 | [ "${actual}" = "true" ] 130 | } 131 | -------------------------------------------------------------------------------- /test/unit/server-psp-rolebinding.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "server/PSP-RoleBinding: PSP-RoleBinding not enabled by default" { 6 | cd `chart_dir` 7 | local actual=$( (helm template \ 8 | --show-only templates/server-psp-rolebinding.yaml \ 9 | --set 'server.dev.enabled=true' \ 10 | . || echo "---") | tee /dev/stderr | 11 | yq 'length > 0' | tee /dev/stderr) 12 | [ "${actual}" = "false" ] 13 | 14 | local actual=$( (helm template \ 15 | --show-only templates/server-psp-rolebinding.yaml \ 16 | --set 'server.ha.enabled=true' \ 17 | . || echo "---") | tee /dev/stderr | 18 | yq 'length > 0' | tee /dev/stderr) 19 | [ "${actual}" = "false" ] 20 | 21 | local actual=$( (helm template \ 22 | --show-only templates/server-psp-rolebinding.yaml \ 23 | --set 'server.standalone.enabled=true' \ 24 | . || echo "---") | tee /dev/stderr | 25 | yq 'length > 0' | tee /dev/stderr) 26 | [ "${actual}" = "false" ] 27 | } 28 | 29 | @test "server/PSP-RoleBinding: PSP-RoleBinding can be enabled" { 30 | cd `chart_dir` 31 | local actual=$(helm template \ 32 | --show-only templates/server-psp-rolebinding.yaml \ 33 | --set 'server.dev.enabled=true' \ 34 | --set 'global.psp.enable=true' \ 35 | . | tee /dev/stderr | 36 | yq 'length > 0' | tee /dev/stderr) 37 | [ "${actual}" = "true" ] 38 | 39 | local actual=$(helm template \ 40 | --show-only templates/server-psp-rolebinding.yaml \ 41 | --set 'server.ha.enabled=true' \ 42 | --set 'global.psp.enable=true' \ 43 | . | tee /dev/stderr | 44 | yq 'length > 0' | tee /dev/stderr) 45 | [ "${actual}" = "true" ] 46 | 47 | local actual=$(helm template \ 48 | --show-only templates/server-psp-rolebinding.yaml \ 49 | --set 'server.standalone.enabled=true' \ 50 | --set 'global.psp.enable=true' \ 51 | . | tee /dev/stderr | 52 | yq 'length > 0' | tee /dev/stderr) 53 | [ "${actual}" = "true" ] 54 | } 55 | 56 | @test "server/PSP-RoleBinding: disable with global.enabled false" { 57 | cd `chart_dir` 58 | local actual=$( (helm template \ 59 | --show-only templates/server-psp-rolebinding.yaml \ 60 | --set 'server.dev.enabled=true' \ 61 | --set 'global.enabled=false' \ 62 | --set 'global.psp.enable=true' \ 63 | . || echo "---") | tee /dev/stderr | 64 | yq 'length > 0' | tee /dev/stderr) 65 | [ "${actual}" = "false" ] 66 | 67 | local actual=$( (helm template \ 68 | --show-only templates/server-psp-rolebinding.yaml \ 69 | --set 'server.ha.enabled=true' \ 70 | --set 'global.enabled=false' \ 71 | --set 'global.psp.enable=true' \ 72 | . || echo "---") | tee /dev/stderr | 73 | yq 'length > 0' | tee /dev/stderr) 74 | [ "${actual}" = "false" ] 75 | 76 | local actual=$( (helm template \ 77 | --show-only templates/server-psp-rolebinding.yaml \ 78 | --set 'server.standalone.enabled=true' \ 79 | --set 'global.enabled=false' \ 80 | --set 'global.psp.enable=true' \ 81 | . || echo "---") | tee /dev/stderr | 82 | yq 'length > 0' | tee /dev/stderr) 83 | [ "${actual}" = "false" ] 84 | } 85 | 86 | @test "server/PSP-RoleBinding: disable with global.psp.enable false" { 87 | cd `chart_dir` 88 | local actual=$( (helm template \ 89 | --show-only templates/server-psp-rolebinding.yaml \ 90 | --set 'server.dev.enabled=true' \ 91 | --set 'global.psp.enable=false' \ 92 | . || echo "---") | tee /dev/stderr | 93 | yq 'length > 0' | tee /dev/stderr) 94 | [ "${actual}" = "false" ] 95 | 96 | local actual=$( (helm template \ 97 | --show-only templates/server-psp-rolebinding.yaml \ 98 | --set 'server.ha.enabled=true' \ 99 | --set 'global.psp.enable=false' \ 100 | . || echo "---") | tee /dev/stderr | 101 | yq 'length > 0' | tee /dev/stderr) 102 | [ "${actual}" = "false" ] 103 | 104 | local actual=$( (helm template \ 105 | --show-only templates/server-psp-rolebinding.yaml \ 106 | --set 'server.standalone.enabled=true' \ 107 | --set 'global.psp.enable=false' \ 108 | . || echo "---") | tee /dev/stderr | 109 | yq 'length > 0' | tee /dev/stderr) 110 | [ "${actual}" = "false" ] 111 | } 112 | 113 | @test "server/PSP-RoleBinding: namespace" { 114 | cd `chart_dir` 115 | local actual=$(helm template \ 116 | --show-only templates/server-psp-rolebinding.yaml \ 117 | --set 'global.psp.enable=true' \ 118 | --namespace foo \ 119 | . | tee /dev/stderr | 120 | yq -r '.metadata.namespace' | tee /dev/stderr) 121 | [ "${actual}" = "foo" ] 122 | local actual=$(helm template \ 123 | --show-only templates/server-psp-rolebinding.yaml \ 124 | --set 'global.psp.enable=true' \ 125 | --set 'global.namespace=bar' \ 126 | --namespace foo \ 127 | . | tee /dev/stderr | 128 | yq -r '.metadata.namespace' | tee /dev/stderr) 129 | [ "${actual}" = "bar" ] 130 | } -------------------------------------------------------------------------------- /test/unit/server-serviceaccount.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "server/ServiceAccount: specify service account name" { 6 | cd `chart_dir` 7 | 8 | local actual=$( (helm template \ 9 | --show-only templates/server-serviceaccount.yaml \ 10 | --set 'server.dev.enabled=true' \ 11 | --set 'server.serviceAccount.create=false' \ 12 | . || echo "---") | tee /dev/stderr | 13 | yq 'length > 0' | tee /dev/stderr) 14 | [ "${actual}" = "false" ] 15 | 16 | local actual=$(helm template \ 17 | --show-only templates/server-serviceaccount.yaml \ 18 | --set 'server.dev.enabled=true' \ 19 | --set 'server.serviceAccount.name=user-defined-ksa' \ 20 | . | tee /dev/stderr | 21 | yq -r '.metadata.name' | tee /dev/stderr) 22 | [ "${actual}" = "user-defined-ksa" ] 23 | 24 | local actual=$(helm template \ 25 | --show-only templates/server-serviceaccount.yaml \ 26 | --set 'server.dev.enabled=true' \ 27 | . | tee /dev/stderr | 28 | yq -r '.metadata.name' | tee /dev/stderr) 29 | [ "${actual}" = "release-name-vault" ] 30 | 31 | } 32 | 33 | @test "server/ServiceAccount: namespace" { 34 | cd `chart_dir` 35 | local actual=$(helm template \ 36 | --show-only templates/server-serviceaccount.yaml \ 37 | --set 'server.serviceAccount.create=true' \ 38 | --namespace foo \ 39 | . | tee /dev/stderr | 40 | yq -r '.metadata.namespace' | tee /dev/stderr) 41 | [ "${actual}" = "foo" ] 42 | local actual=$(helm template \ 43 | --show-only templates/server-serviceaccount.yaml \ 44 | --set 'server.serviceAccount.create=true' \ 45 | --set 'global.namespace=bar' \ 46 | --namespace foo \ 47 | . | tee /dev/stderr | 48 | yq -r '.metadata.namespace' | tee /dev/stderr) 49 | [ "${actual}" = "bar" ] 50 | } 51 | 52 | @test "server/ServiceAccount: specify annotations" { 53 | cd `chart_dir` 54 | local actual=$(helm template \ 55 | --show-only templates/server-serviceaccount.yaml \ 56 | --set 'server.dev.enabled=true' \ 57 | --set 'server.serviceAccount.annotations=foo: bar' \ 58 | . | tee /dev/stderr | 59 | yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) 60 | [ "${actual}" = "null" ] 61 | 62 | local actual=$(helm template \ 63 | --show-only templates/server-serviceaccount.yaml \ 64 | --set 'server.ha.enabled=true' \ 65 | --set 'server.serviceAccount.annotations=foo: bar' \ 66 | . | tee /dev/stderr | 67 | yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) 68 | [ "${actual}" = "bar" ] 69 | 70 | local actual=$(helm template \ 71 | --show-only templates/server-serviceaccount.yaml \ 72 | --set 'server.ha.enabled=true' \ 73 | --set 'server.serviceAccount.annotations.foo=bar' \ 74 | . | tee /dev/stderr | 75 | yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) 76 | [ "${actual}" = "bar" ] 77 | 78 | local actual=$(helm template \ 79 | --show-only templates/server-serviceaccount.yaml \ 80 | --set 'server.ha.enabled=true' \ 81 | . | tee /dev/stderr | 82 | yq -r '.metadata.annotations["foo"]' | tee /dev/stderr) 83 | [ "${actual}" = "null" ] 84 | } 85 | 86 | @test "server/ServiceAccount: disable with global.enabled false" { 87 | cd `chart_dir` 88 | local actual=$( (helm template \ 89 | --show-only templates/server-service.yaml \ 90 | --set 'server.dev.enabled=true' \ 91 | --set 'global.enabled=false' \ 92 | . || echo "---") | tee /dev/stderr | 93 | yq 'length > 0' | tee /dev/stderr) 94 | [ "${actual}" = "false" ] 95 | 96 | local actual=$( (helm template \ 97 | --show-only templates/server-service.yaml \ 98 | --set 'server.ha.enabled=true' \ 99 | --set 'global.enabled=false' \ 100 | . || echo "---") | tee /dev/stderr | 101 | yq 'length > 0' | tee /dev/stderr) 102 | [ "${actual}" = "false" ] 103 | 104 | local actual=$( (helm template \ 105 | --show-only templates/server-service.yaml \ 106 | --set 'server.standalone.enabled=true' \ 107 | --set 'global.enabled=false' \ 108 | . || echo "---") | tee /dev/stderr | 109 | yq 'length > 0' | tee /dev/stderr) 110 | [ "${actual}" = "false" ] 111 | } 112 | 113 | @test "server/ServiceAccount: disable by injector.externalVaultAddr" { 114 | cd `chart_dir` 115 | local actual=$( (helm template \ 116 | --show-only templates/server-service.yaml \ 117 | --set 'server.dev.enabled=true' \ 118 | --set 'injector.externalVaultAddr=http://vault-outside' \ 119 | . || echo "---") | tee /dev/stderr | 120 | yq 'length > 0' | tee /dev/stderr) 121 | [ "${actual}" = "false" ] 122 | 123 | local actual=$( (helm template \ 124 | --show-only templates/server-service.yaml \ 125 | --set 'server.ha.enabled=true' \ 126 | --set 'injector.externalVaultAddr=http://vault-outside' \ 127 | . || echo "---") | tee /dev/stderr | 128 | yq 'length > 0' | tee /dev/stderr) 129 | [ "${actual}" = "false" ] 130 | 131 | local actual=$( (helm template \ 132 | --show-only templates/server-service.yaml \ 133 | --set 'server.standalone.enabled=true' \ 134 | --set 'injector.externalVaultAddr=http://vault-outside' \ 135 | . || echo "---") | tee /dev/stderr | 136 | yq 'length > 0' | tee /dev/stderr) 137 | [ "${actual}" = "false" ] 138 | } 139 | 140 | @test "server/serviceAccount: specify server.serviceAccount.extraLabels" { 141 | cd `chart_dir` 142 | local actual=$(helm template \ 143 | --show-only templates/server-serviceaccount.yaml \ 144 | --set 'server.serviceAccount.extraLabels.foo=bar' \ 145 | . | tee /dev/stderr | 146 | yq -r '.metadata.labels.foo' | tee /dev/stderr) 147 | [ "${actual}" = "bar" ] 148 | } -------------------------------------------------------------------------------- /test/acceptance/server-ha-enterprise-perf.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "server/ha-enterprise-raft: testing performance replica deployment" { 6 | if [ ! "$ENT_TESTS" = "true" ]; then 7 | skip "Enterprise tests are not enabled" 8 | fi 9 | 10 | cd `chart_dir` 11 | 12 | helm install "$(name_prefix)-east" . \ 13 | --set='injector.enabled=false' \ 14 | --set='server.ha.enabled=true' \ 15 | --set='server.ha.raft.enabled=true' \ 16 | ${SET_CHART_VALUES} 17 | check_vault_versions "$(name_prefix)-east" 18 | wait_for_running "$(name_prefix)-east-0" 19 | 20 | # Sealed, not initialized 21 | wait_for_sealed_vault $(name_prefix)-east-0 22 | 23 | local init_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | 24 | jq -r '.initialized') 25 | [ "${init_status}" == "false" ] 26 | 27 | # Vault Init 28 | local init=$(kubectl exec -ti "$(name_prefix)-east-0" -- \ 29 | vault operator init -format=json -n 1 -t 1) 30 | 31 | local primary_token=$(echo ${init} | jq -r '.unseal_keys_b64[0]') 32 | [ "${primary_token}" != "" ] 33 | 34 | local primary_root=$(echo ${init} | jq -r '.root_token') 35 | [ "${primary_root}" != "" ] 36 | 37 | kubectl exec -ti "$(name_prefix)-east-0" -- vault operator unseal ${primary_token} 38 | wait_for_ready "$(name_prefix)-east-0" 39 | 40 | sleep 30 41 | 42 | # Vault Unseal 43 | local pods=($(kubectl get pods --selector='app.kubernetes.io/name=vault' -o json | jq -r '.items[].metadata.name')) 44 | for pod in "${pods[@]}" 45 | do 46 | if [[ ${pod?} != "$(name_prefix)-east-0" ]] 47 | then 48 | kubectl exec -ti ${pod} -- vault operator raft join http://$(name_prefix)-east-0.$(name_prefix)-east-internal:8200 49 | kubectl exec -ti ${pod} -- vault operator unseal ${primary_token} 50 | wait_for_ready "${pod}" 51 | fi 52 | done 53 | 54 | # Unsealed, initialized 55 | local sealed_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | 56 | jq -r '.sealed' ) 57 | [ "${sealed_status}" == "false" ] 58 | 59 | local init_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | 60 | jq -r '.initialized') 61 | [ "${init_status}" == "true" ] 62 | 63 | kubectl exec "$(name_prefix)-east-0" -- vault login ${primary_root} 64 | 65 | local raft_status=$(kubectl exec "$(name_prefix)-east-0" -- vault operator raft list-peers -format=json | 66 | jq -r '.data.config.servers | length') 67 | [ "${raft_status}" == "3" ] 68 | 69 | kubectl exec -ti $(name_prefix)-east-0 -- vault write -f sys/replication/performance/primary/enable primary_cluster_addr=https://$(name_prefix)-east-active:8201 70 | 71 | local secondary=$(kubectl exec -ti "$(name_prefix)-east-0" -- vault write sys/replication/performance/primary/secondary-token id=secondary -format=json) 72 | [ "${secondary}" != "" ] 73 | 74 | local secondary_replica_token=$(echo ${secondary} | jq -r '.wrap_info.token') 75 | [ "${secondary_replica_token}" != "" ] 76 | 77 | # Install vault-west 78 | helm install "$(name_prefix)-west" . \ 79 | --set='injector.enabled=false' \ 80 | --set='server.ha.enabled=true' \ 81 | --set='server.ha.raft.enabled=true' \ 82 | ${SET_CHART_VALUES} 83 | check_vault_versions "$(name_prefix)-west" 84 | wait_for_running "$(name_prefix)-west-0" 85 | 86 | # Sealed, not initialized 87 | wait_for_sealed_vault $(name_prefix)-west-0 88 | 89 | local init_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | 90 | jq -r '.initialized') 91 | [ "${init_status}" == "false" ] 92 | 93 | # Vault Init 94 | local init=$(kubectl exec -ti "$(name_prefix)-west-0" -- \ 95 | vault operator init -format=json -n 1 -t 1) 96 | 97 | local secondary_token=$(echo ${init} | jq -r '.unseal_keys_b64[0]') 98 | [ "${secondary_token}" != "" ] 99 | 100 | local secondary_root=$(echo ${init} | jq -r '.root_token') 101 | [ "${secondary_root}" != "" ] 102 | 103 | kubectl exec -ti "$(name_prefix)-west-0" -- vault operator unseal ${secondary_token} 104 | wait_for_ready "$(name_prefix)-west-0" 105 | 106 | sleep 30 107 | 108 | # Vault Unseal 109 | local pods=($(kubectl get pods --selector='app.kubernetes.io/instance=vault-west' -o json | jq -r '.items[].metadata.name')) 110 | for pod in "${pods[@]}" 111 | do 112 | if [[ ${pod?} != "$(name_prefix)-west-0" ]] 113 | then 114 | kubectl exec -ti ${pod} -- vault operator raft join http://$(name_prefix)-west-0.$(name_prefix)-west-internal:8200 115 | kubectl exec -ti ${pod} -- vault operator unseal ${secondary_token} 116 | wait_for_ready "${pod}" 117 | fi 118 | done 119 | 120 | # Unsealed, initialized 121 | local sealed_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | 122 | jq -r '.sealed' ) 123 | [ "${sealed_status}" == "false" ] 124 | 125 | local init_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | 126 | jq -r '.initialized') 127 | [ "${init_status}" == "true" ] 128 | 129 | kubectl exec "$(name_prefix)-west-0" -- vault login ${secondary_root} 130 | 131 | local raft_status=$(kubectl exec "$(name_prefix)-west-0" -- vault operator raft list-peers -format=json | 132 | jq -r '.data.config.servers | length') 133 | [ "${raft_status}" == "3" ] 134 | 135 | kubectl exec -ti "$(name_prefix)-west-0" -- vault write sys/replication/performance/secondary/enable token=${secondary_replica_token} 136 | 137 | sleep 30 138 | 139 | local pods=($(kubectl get pods --selector='app.kubernetes.io/instance=vault-west' -o json | jq -r '.items[].metadata.name')) 140 | for pod in "${pods[@]}" 141 | do 142 | if [[ ${pod?} != "$(name_prefix)-west-0" ]] 143 | then 144 | kubectl exec -ti ${pod} -- vault operator unseal ${primary_token} 145 | wait_for_ready "${pod}" 146 | fi 147 | done 148 | } 149 | 150 | setup() { 151 | kubectl delete namespace acceptance --ignore-not-found=true 152 | kubectl create namespace acceptance 153 | kubectl config set-context --current --namespace=acceptance 154 | eval "${PRE_CHART_CMDS}" 155 | } 156 | 157 | #cleanup 158 | teardown() { 159 | if [[ ${CLEANUP:-true} == "true" ]] 160 | then 161 | helm delete vault-east 162 | helm delete vault-west 163 | kubectl delete --all pvc 164 | kubectl delete namespace acceptance --ignore-not-found=true 165 | kubectl config unset contexts."$(kubectl config current-context)".namespace 166 | fi 167 | } 168 | -------------------------------------------------------------------------------- /test/acceptance/server-ha-enterprise-dr.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load _helpers 4 | 5 | @test "server/ha-enterprise-raft: testing DR deployment" { 6 | if [ ! "$ENT_TESTS" = "true" ]; then 7 | skip "Enterprise tests are not enabled" 8 | fi 9 | 10 | cd `chart_dir` 11 | 12 | helm install "$(name_prefix)-east" . \ 13 | --set='injector.enabled=false' \ 14 | --set='server.ha.enabled=true' \ 15 | --set='server.ha.raft.enabled=true' \ 16 | ${SET_CHART_VALUES} 17 | check_vault_versions "$(name_prefix)-east" 18 | wait_for_running "$(name_prefix)-east-0" 19 | 20 | # Sealed, not initialized 21 | wait_for_sealed_vault $(name_prefix)-east-0 22 | 23 | local init_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | 24 | jq -r '.initialized') 25 | [ "${init_status}" == "false" ] 26 | 27 | # Vault Init 28 | local init=$(kubectl exec -ti "$(name_prefix)-east-0" -- \ 29 | vault operator init -format=json -n 1 -t 1) 30 | 31 | local primary_token=$(echo ${init} | jq -r '.unseal_keys_b64[0]') 32 | [ "${primary_token}" != "" ] 33 | 34 | local primary_root=$(echo ${init} | jq -r '.root_token') 35 | [ "${primary_root}" != "" ] 36 | 37 | kubectl exec -ti "$(name_prefix)-east-0" -- vault operator unseal ${primary_token} 38 | wait_for_ready "$(name_prefix)-east-0" 39 | 40 | sleep 10 41 | 42 | # Vault Unseal 43 | local pods=($(kubectl get pods --selector='app.kubernetes.io/name=vault' -o json | jq -r '.items[].metadata.name')) 44 | for pod in "${pods[@]}" 45 | do 46 | if [[ ${pod?} != "$(name_prefix)-east-0" ]] 47 | then 48 | kubectl exec -ti ${pod} -- vault operator raft join http://$(name_prefix)-east-0.$(name_prefix)-east-internal:8200 49 | kubectl exec -ti ${pod} -- vault operator unseal ${primary_token} 50 | wait_for_ready "${pod}" 51 | fi 52 | done 53 | 54 | # Unsealed, initialized 55 | local sealed_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | 56 | jq -r '.sealed' ) 57 | [ "${sealed_status}" == "false" ] 58 | 59 | local init_status=$(kubectl exec "$(name_prefix)-east-0" -- vault status -format=json | 60 | jq -r '.initialized') 61 | [ "${init_status}" == "true" ] 62 | 63 | kubectl exec "$(name_prefix)-east-0" -- vault login ${primary_root} 64 | 65 | local raft_status=$(kubectl exec "$(name_prefix)-east-0" -- vault operator raft list-peers -format=json | 66 | jq -r '.data.config.servers | length') 67 | [ "${raft_status}" == "3" ] 68 | 69 | kubectl exec -ti $(name_prefix)-east-0 -- vault write -f sys/replication/dr/primary/enable primary_cluster_addr=https://$(name_prefix)-east-active:8201 70 | 71 | local secondary=$(kubectl exec -ti "$(name_prefix)-east-0" -- vault write sys/replication/dr/primary/secondary-token id=secondary -format=json) 72 | [ "${secondary}" != "" ] 73 | 74 | local secondary_replica_token=$(echo ${secondary} | jq -r '.wrap_info.token') 75 | [ "${secondary_replica_token}" != "" ] 76 | 77 | # Install vault-west 78 | helm install "$(name_prefix)-west" . \ 79 | --set='injector.enabled=false' \ 80 | --set='server.ha.enabled=true' \ 81 | --set='server.ha.raft.enabled=true' \ 82 | ${SET_CHART_VALUES} 83 | check_vault_versions "$(name_prefix)-west" 84 | wait_for_running "$(name_prefix)-west-0" 85 | 86 | # Sealed, not initialized 87 | wait_for_sealed_vault $(name_prefix)-west-0 88 | 89 | local init_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | 90 | jq -r '.initialized') 91 | [ "${init_status}" == "false" ] 92 | 93 | # Vault Init 94 | local init=$(kubectl exec -ti "$(name_prefix)-west-0" -- \ 95 | vault operator init -format=json -n 1 -t 1) 96 | 97 | local secondary_token=$(echo ${init} | jq -r '.unseal_keys_b64[0]') 98 | [ "${secondary_token}" != "" ] 99 | 100 | local secondary_root=$(echo ${init} | jq -r '.root_token') 101 | [ "${secondary_root}" != "" ] 102 | 103 | kubectl exec -ti "$(name_prefix)-west-0" -- vault operator unseal ${secondary_token} 104 | wait_for_ready "$(name_prefix)-west-0" 105 | 106 | sleep 10 107 | 108 | # Vault Unseal 109 | local pods=($(kubectl get pods --selector='app.kubernetes.io/instance=vault-west' -o json | jq -r '.items[].metadata.name')) 110 | for pod in "${pods[@]}" 111 | do 112 | if [[ ${pod?} != "$(name_prefix)-west-0" ]] 113 | then 114 | kubectl exec -ti ${pod} -- vault operator raft join http://$(name_prefix)-west-0.$(name_prefix)-west-internal:8200 115 | kubectl exec -ti ${pod} -- vault operator unseal ${secondary_token} 116 | wait_for_ready "${pod}" 117 | fi 118 | done 119 | 120 | # Unsealed, initialized 121 | local sealed_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | 122 | jq -r '.sealed' ) 123 | [ "${sealed_status}" == "false" ] 124 | 125 | local init_status=$(kubectl exec "$(name_prefix)-west-0" -- vault status -format=json | 126 | jq -r '.initialized') 127 | [ "${init_status}" == "true" ] 128 | 129 | kubectl exec "$(name_prefix)-west-0" -- vault login ${secondary_root} 130 | 131 | local raft_status=$(kubectl exec "$(name_prefix)-west-0" -- vault operator raft list-peers -format=json | 132 | jq -r '.data.config.servers | length') 133 | [ "${raft_status}" == "3" ] 134 | 135 | kubectl exec -ti "$(name_prefix)-west-0" -- vault write sys/replication/dr/secondary/enable token=${secondary_replica_token} 136 | 137 | sleep 10 138 | 139 | local pods=($(kubectl get pods --selector='app.kubernetes.io/instance=vault-west' -o json | jq -r '.items[].metadata.name')) 140 | for pod in "${pods[@]}" 141 | do 142 | if [[ ${pod?} != "$(name_prefix)-west-0" ]] 143 | then 144 | kubectl delete pod "${pod?}" 145 | wait_for_running "${pod?}" 146 | kubectl exec -ti ${pod} -- vault operator unseal ${primary_token} 147 | wait_for_ready "${pod}" 148 | fi 149 | done 150 | } 151 | 152 | setup() { 153 | kubectl delete namespace acceptance --ignore-not-found=true 154 | kubectl create namespace acceptance 155 | kubectl config set-context --current --namespace=acceptance 156 | eval "${PRE_CHART_CMDS}" 157 | } 158 | 159 | #cleanup 160 | teardown() { 161 | if [[ ${CLEANUP:-true} == "true" ]] 162 | then 163 | helm delete vault-east 164 | helm delete vault-west 165 | kubectl delete --all pvc 166 | kubectl delete namespace acceptance --ignore-not-found=true 167 | kubectl config unset contexts."$(kubectl config current-context)".namespace 168 | fi 169 | } 170 | --------------------------------------------------------------------------------