From 7dab6ea5692c50c6f0aeb81d660e7decc072ac38 Mon Sep 17 00:00:00 2001 From: Samuel Attwood Date: Tue, 18 Oct 2022 04:13:05 -0400 Subject: [PATCH] CI Updated Charts Added: hashicorp/consul: - 0.49.0 hashicorp/vault: - 0.22.0 --- assets/hashicorp/consul-0.49.0.tgz | Bin 0 -> 108152 bytes assets/hashicorp/vault-0.22.0.tgz | Bin 0 -> 43180 bytes charts/hashicorp/consul/0.49.0/.helmignore | 4 + charts/hashicorp/consul/0.49.0/Chart.yaml | 35 + charts/hashicorp/consul/0.49.0/README.md | 68 + charts/hashicorp/consul/0.49.0/addons/gen.sh | 34 + .../0.49.0/addons/values/prometheus.yaml | 18 + .../hashicorp/consul/0.49.0/assets/icon.png | Bin 0 -> 7981 bytes .../consul/0.49.0/templates/NOTES.txt | 21 + .../consul/0.49.0/templates/_helpers.tpl | 314 ++ .../api-gateway-controller-clusterrole.yaml | 239 ++ ...gateway-controller-clusterrolebinding.yaml | 20 + .../api-gateway-controller-deployment.yaml | 238 ++ .../api-gateway-controller-service.yaml | 27 + ...api-gateway-controller-serviceaccount.yaml | 23 + .../templates/api-gateway-gatewayclass.yaml | 18 + .../api-gateway-gatewayclassconfig.yaml | 50 + .../templates/auth-method-clusterrole.yaml | 18 + .../auth-method-clusterrolebinding.yaml | 39 + .../0.49.0/templates/auth-method-secret.yaml | 16 + .../templates/auth-method-serviceaccount.yaml | 19 + .../templates/client-config-configmap.yaml | 37 + .../0.49.0/templates/client-daemonset.yaml | 608 ++++ .../templates/client-podsecuritypolicy.yaml | 76 + .../consul/0.49.0/templates/client-role.yaml | 43 + .../0.49.0/templates/client-rolebinding.yaml | 20 + .../client-securitycontextconstraints.yaml | 56 + .../templates/client-serviceaccount.yaml | 23 + .../client-snapshot-agent-deployment.yaml | 281 ++ ...ient-snapshot-agent-podsecuritypolicy.yaml | 42 + .../templates/client-snapshot-agent-role.yaml | 26 + .../client-snapshot-agent-rolebinding.yaml | 22 + .../client-snapshot-agent-serviceaccount.yaml | 25 + .../0.49.0/templates/cni-clusterrole.yaml | 38 + .../templates/cni-clusterrolebinding.yaml | 20 + .../0.49.0/templates/cni-daemonset.yaml | 84 + .../cni-networkattachmentdefinition.yaml | 25 + .../templates/cni-podsecuritypolicy.yaml | 31 + .../0.49.0/templates/cni-resourcequota.yaml | 22 + .../cni-securitycontextconstraints.yaml | 50 + .../0.49.0/templates/cni-serviceaccount.yaml | 19 + .../templates/connect-inject-clusterrole.yaml | 109 + .../connect-inject-clusterrolebinding.yaml | 20 + .../templates/connect-inject-deployment.yaml | 437 +++ .../connect-inject-leader-election-role.yaml | 41 + ...ct-inject-leader-election-rolebinding.yaml | 21 + ...t-inject-mutatingwebhookconfiguration.yaml | 85 + .../connect-inject-podsecuritypolicy.yaml | 40 + .../templates/connect-inject-service.yaml | 23 + .../connect-inject-serviceaccount.yaml | 23 + .../connect-injector-disruptionbudget.yaml | 26 + .../templates/controller-clusterrole.yaml | 79 + .../controller-clusterrolebinding.yaml | 20 + .../templates/controller-deployment.yaml | 277 ++ .../controller-leader-election-role.yaml | 41 + ...ontroller-leader-election-rolebinding.yaml | 20 + ...ntroller-mutatingwebhookconfiguration.yaml | 224 ++ .../controller-podsecuritypolicy.yaml | 40 + .../templates/controller-serviceaccount.yaml | 23 + .../templates/controller-webhook-service.yaml | 23 + .../templates/crd-exportedservices.yaml | 143 + .../0.49.0/templates/crd-ingressgateways.yaml | 336 ++ .../consul/0.49.0/templates/crd-meshes.yaml | 198 ++ .../templates/crd-peeringacceptors.yaml | 154 + .../0.49.0/templates/crd-peeringdialers.yaml | 154 + .../0.49.0/templates/crd-proxydefaults.yaml | 186 + .../0.49.0/templates/crd-servicedefaults.yaml | 422 +++ .../templates/crd-serviceintentions.yaml | 241 ++ .../templates/crd-serviceresolvers.yaml | 283 ++ .../0.49.0/templates/crd-servicerouters.yaml | 316 ++ .../templates/crd-servicesplitters.yaml | 194 ++ .../templates/crd-terminatinggateways.yaml | 145 + .../create-federation-secret-job.yaml | 161 + ...e-federation-secret-podsecuritypolicy.yaml | 42 + .../create-federation-secret-role.yaml | 49 + .../create-federation-secret-rolebinding.yaml | 23 + ...eate-federation-secret-serviceaccount.yaml | 22 + .../consul/0.49.0/templates/dns-service.yaml | 41 + .../templates/enterprise-license-job.yaml | 138 + .../enterprise-license-podsecuritypolicy.yaml | 39 + .../templates/enterprise-license-role.yaml | 37 + .../enterprise-license-rolebinding.yaml | 22 + .../enterprise-license-serviceaccount.yaml | 21 + .../templates/expose-servers-service.yaml | 63 + .../gossip-encryption-autogenerate-job.yaml | 62 + ...yption-autogenerate-podsecuritypolicy.yaml | 40 + .../gossip-encryption-autogenerate-role.yaml | 32 + ...p-encryption-autogenerate-rolebinding.yaml | 23 + ...ncryption-autogenerate-serviceaccount.yaml | 22 + .../ingress-gateways-deployment.yaml | 489 +++ .../ingress-gateways-podsecuritypolicy.yaml | 45 + .../templates/ingress-gateways-role.yaml | 46 + .../ingress-gateways-rolebinding.yaml | 25 + .../templates/ingress-gateways-service.yaml | 51 + .../ingress-gateways-serviceaccount.yaml | 35 + .../templates/mesh-gateway-clusterrole.yaml | 34 + .../mesh-gateway-clusterrolebinding.yaml | 20 + .../templates/mesh-gateway-deployment.yaml | 423 +++ .../mesh-gateway-podsecuritypolicy.yaml | 52 + .../templates/mesh-gateway-service.yaml | 33 + .../mesh-gateway-serviceaccount.yaml | 23 + .../0.49.0/templates/partition-init-job.yaml | 138 + .../partition-init-podsecuritypolicy.yaml | 40 + .../0.49.0/templates/partition-init-role.yaml | 41 + .../templates/partition-init-rolebinding.yaml | 24 + .../partition-init-serviceaccount.yaml | 23 + .../templates/partition-name-configmap.yaml | 19 + .../0.49.0/templates/partition-service.yaml | 45 + .../consul/0.49.0/templates/prometheus.yaml | 488 +++ .../server-acl-init-cleanup-job.yaml | 67 + ...er-acl-init-cleanup-podsecuritypolicy.yaml | 40 + .../server-acl-init-cleanup-role.yaml | 28 + .../server-acl-init-cleanup-rolebinding.yaml | 23 + ...erver-acl-init-cleanup-serviceaccount.yaml | 22 + .../0.49.0/templates/server-acl-init-job.yaml | 333 ++ .../server-acl-init-podsecuritypolicy.yaml | 41 + .../templates/server-acl-init-role.yaml | 38 + .../server-acl-init-rolebinding.yaml | 23 + .../server-acl-init-serviceaccount.yaml | 22 + .../templates/server-config-configmap.yaml | 202 ++ .../templates/server-disruptionbudget.yaml | 26 + .../templates/server-podsecuritypolicy.yaml | 53 + .../consul/0.49.0/templates/server-role.yaml | 34 + .../0.49.0/templates/server-rolebinding.yaml | 20 + .../server-securitycontextconstraints.yaml | 49 + .../0.49.0/templates/server-service.yaml | 76 + .../templates/server-serviceaccount.yaml | 23 + .../0.49.0/templates/server-statefulset.yaml | 432 +++ .../templates/sync-catalog-clusterrole.yaml | 41 + .../sync-catalog-clusterrolebinding.yaml | 21 + .../templates/sync-catalog-deployment.yaml | 313 ++ .../sync-catalog-podsecuritypolicy.yaml | 40 + .../sync-catalog-serviceaccount.yaml | 24 + .../terminating-gateways-deployment.yaml | 440 +++ ...erminating-gateways-podsecuritypolicy.yaml | 45 + .../templates/terminating-gateways-role.yaml | 43 + .../terminating-gateways-rolebinding.yaml | 26 + .../terminating-gateways-serviceaccount.yaml | 35 + .../0.49.0/templates/tests/test-runner.yaml | 78 + .../templates/tls-init-cleanup-job.yaml | 67 + .../tls-init-cleanup-podsecuritypolicy.yaml | 43 + .../templates/tls-init-cleanup-role.yaml | 41 + .../tls-init-cleanup-rolebinding.yaml | 27 + .../tls-init-cleanup-serviceaccount.yaml | 26 + .../consul/0.49.0/templates/tls-init-job.yaml | 109 + .../templates/tls-init-podsecuritypolicy.yaml | 43 + .../0.49.0/templates/tls-init-role.yaml | 38 + .../templates/tls-init-rolebinding.yaml | 27 + .../templates/tls-init-serviceaccount.yaml | 26 + .../consul/0.49.0/templates/ui-ingress.yaml | 83 + .../consul/0.49.0/templates/ui-service.yaml | 46 + .../webhook-cert-manager-clusterrole.yaml | 53 + ...bhook-cert-manager-clusterrolebinding.yaml | 21 + .../webhook-cert-manager-configmap.yaml | 43 + .../webhook-cert-manager-deployment.yaml | 69 + ...ebhook-cert-manager-podsecuritypolicy.yaml | 41 + .../webhook-cert-manager-serviceaccount.yaml | 20 + charts/hashicorp/consul/0.49.0/values.yaml | 3078 +++++++++++++++++ charts/hashicorp/vault/0.22.0/.helmignore | 28 + charts/hashicorp/vault/0.22.0/CHANGELOG.md | 427 +++ charts/hashicorp/vault/0.22.0/CONTRIBUTING.md | 247 ++ charts/hashicorp/vault/0.22.0/Chart.yaml | 26 + charts/hashicorp/vault/0.22.0/LICENSE.md | 353 ++ charts/hashicorp/vault/0.22.0/Makefile | 101 + charts/hashicorp/vault/0.22.0/README.md | 43 + .../vault/0.22.0/templates/NOTES.txt | 14 + .../vault/0.22.0/templates/_helpers.tpl | 953 +++++ .../0.22.0/templates/csi-clusterrole.yaml | 18 + .../templates/csi-clusterrolebinding.yaml | 19 + .../vault/0.22.0/templates/csi-daemonset.yaml | 100 + .../0.22.0/templates/csi-serviceaccount.yaml | 16 + .../templates/injector-certs-secret.yaml | 14 + .../templates/injector-clusterrole.yaml | 19 + .../injector-clusterrolebinding.yaml | 19 + .../0.22.0/templates/injector-deployment.yaml | 156 + .../templates/injector-disruptionbudget.yaml | 20 + .../templates/injector-mutating-webhook.yaml | 39 + .../templates/injector-network-policy.yaml | 24 + .../0.22.0/templates/injector-psp-role.yaml | 20 + .../templates/injector-psp-rolebinding.yaml | 21 + .../vault/0.22.0/templates/injector-psp.yaml | 46 + .../vault/0.22.0/templates/injector-role.yaml | 29 + .../templates/injector-rolebinding.yaml | 22 + .../0.22.0/templates/injector-service.yaml | 22 + .../templates/injector-serviceaccount.yaml | 13 + .../templates/prometheus-prometheusrules.yaml | 26 + .../templates/prometheus-servicemonitor.yaml | 44 + .../templates/server-clusterrolebinding.yaml | 24 + .../templates/server-config-configmap.yaml | 40 + .../templates/server-discovery-role.yaml | 21 + .../server-discovery-rolebinding.yaml | 29 + .../templates/server-disruptionbudget.yaml | 26 + .../templates/server-ha-active-service.yaml | 46 + .../templates/server-ha-standby-service.yaml | 45 + .../templates/server-headless-service.yaml | 34 + .../0.22.0/templates/server-ingress.yaml | 77 + .../templates/server-network-policy.yaml | 26 + .../0.22.0/templates/server-psp-role.yaml | 20 + .../templates/server-psp-rolebinding.yaml | 21 + .../vault/0.22.0/templates/server-psp.yaml | 49 + .../vault/0.22.0/templates/server-route.yaml | 34 + .../0.22.0/templates/server-service.yaml | 44 + .../templates/server-serviceaccount.yaml | 14 + .../0.22.0/templates/server-statefulset.yaml | 206 ++ .../0.22.0/templates/tests/server-test.yaml | 51 + .../vault/0.22.0/templates/ui-service.yaml | 37 + .../vault/0.22.0/values.openshift.yaml | 18 + .../hashicorp/vault/0.22.0/values.schema.json | 1027 ++++++ charts/hashicorp/vault/0.22.0/values.yaml | 1119 ++++++ index.yaml | 71 + packages/hashicorp/consul/upstream.yaml | 4 + packages/hashicorp/vault/upstream.yaml | 4 + 212 files changed, 22549 insertions(+) create mode 100644 assets/hashicorp/consul-0.49.0.tgz create mode 100644 assets/hashicorp/vault-0.22.0.tgz create mode 100644 charts/hashicorp/consul/0.49.0/.helmignore create mode 100644 charts/hashicorp/consul/0.49.0/Chart.yaml create mode 100644 charts/hashicorp/consul/0.49.0/README.md create mode 100644 charts/hashicorp/consul/0.49.0/addons/gen.sh create mode 100644 charts/hashicorp/consul/0.49.0/addons/values/prometheus.yaml create mode 100644 charts/hashicorp/consul/0.49.0/assets/icon.png create mode 100644 charts/hashicorp/consul/0.49.0/templates/NOTES.txt create mode 100644 charts/hashicorp/consul/0.49.0/templates/_helpers.tpl create mode 100644 charts/hashicorp/consul/0.49.0/templates/api-gateway-controller-clusterrole.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/api-gateway-controller-clusterrolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/api-gateway-controller-deployment.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/api-gateway-controller-service.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/api-gateway-controller-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/api-gateway-gatewayclass.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/api-gateway-gatewayclassconfig.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/auth-method-clusterrole.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/auth-method-clusterrolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/auth-method-secret.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/auth-method-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/client-config-configmap.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/client-daemonset.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/client-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/client-role.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/client-rolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/client-securitycontextconstraints.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/client-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/client-snapshot-agent-deployment.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/client-snapshot-agent-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/client-snapshot-agent-role.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/client-snapshot-agent-rolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/client-snapshot-agent-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/cni-clusterrole.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/cni-clusterrolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/cni-daemonset.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/cni-networkattachmentdefinition.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/cni-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/cni-resourcequota.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/cni-securitycontextconstraints.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/cni-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/connect-inject-clusterrole.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/connect-inject-clusterrolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/connect-inject-deployment.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/connect-inject-leader-election-role.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/connect-inject-leader-election-rolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/connect-inject-mutatingwebhookconfiguration.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/connect-inject-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/connect-inject-service.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/connect-inject-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/connect-injector-disruptionbudget.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/controller-clusterrole.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/controller-clusterrolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/controller-deployment.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/controller-leader-election-role.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/controller-leader-election-rolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/controller-mutatingwebhookconfiguration.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/controller-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/controller-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/controller-webhook-service.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/crd-exportedservices.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/crd-ingressgateways.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/crd-meshes.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/crd-peeringacceptors.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/crd-peeringdialers.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/crd-proxydefaults.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/crd-servicedefaults.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/crd-serviceintentions.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/crd-serviceresolvers.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/crd-servicerouters.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/crd-servicesplitters.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/crd-terminatinggateways.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/create-federation-secret-job.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/create-federation-secret-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/create-federation-secret-role.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/create-federation-secret-rolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/create-federation-secret-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/dns-service.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/enterprise-license-job.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/enterprise-license-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/enterprise-license-role.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/enterprise-license-rolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/enterprise-license-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/expose-servers-service.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/gossip-encryption-autogenerate-job.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/gossip-encryption-autogenerate-role.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/gossip-encryption-autogenerate-rolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/gossip-encryption-autogenerate-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/ingress-gateways-deployment.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/ingress-gateways-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/ingress-gateways-role.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/ingress-gateways-rolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/ingress-gateways-service.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/ingress-gateways-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/mesh-gateway-clusterrole.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/mesh-gateway-clusterrolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/mesh-gateway-deployment.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/mesh-gateway-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/mesh-gateway-service.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/mesh-gateway-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/partition-init-job.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/partition-init-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/partition-init-role.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/partition-init-rolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/partition-init-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/partition-name-configmap.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/partition-service.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/prometheus.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/server-acl-init-cleanup-job.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/server-acl-init-cleanup-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/server-acl-init-cleanup-role.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/server-acl-init-cleanup-rolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/server-acl-init-cleanup-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/server-acl-init-job.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/server-acl-init-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/server-acl-init-role.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/server-acl-init-rolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/server-acl-init-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/server-config-configmap.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/server-disruptionbudget.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/server-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/server-role.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/server-rolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/server-securitycontextconstraints.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/server-service.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/server-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/server-statefulset.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/sync-catalog-clusterrole.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/sync-catalog-clusterrolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/sync-catalog-deployment.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/sync-catalog-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/sync-catalog-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/terminating-gateways-deployment.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/terminating-gateways-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/terminating-gateways-role.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/terminating-gateways-rolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/terminating-gateways-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/tests/test-runner.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/tls-init-cleanup-job.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/tls-init-cleanup-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/tls-init-cleanup-role.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/tls-init-cleanup-rolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/tls-init-cleanup-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/tls-init-job.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/tls-init-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/tls-init-role.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/tls-init-rolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/tls-init-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/ui-ingress.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/ui-service.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-clusterrole.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-clusterrolebinding.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-configmap.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-deployment.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-podsecuritypolicy.yaml create mode 100644 charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-serviceaccount.yaml create mode 100644 charts/hashicorp/consul/0.49.0/values.yaml create mode 100644 charts/hashicorp/vault/0.22.0/.helmignore create mode 100644 charts/hashicorp/vault/0.22.0/CHANGELOG.md create mode 100644 charts/hashicorp/vault/0.22.0/CONTRIBUTING.md create mode 100644 charts/hashicorp/vault/0.22.0/Chart.yaml create mode 100644 charts/hashicorp/vault/0.22.0/LICENSE.md create mode 100644 charts/hashicorp/vault/0.22.0/Makefile create mode 100644 charts/hashicorp/vault/0.22.0/README.md create mode 100644 charts/hashicorp/vault/0.22.0/templates/NOTES.txt create mode 100644 charts/hashicorp/vault/0.22.0/templates/_helpers.tpl create mode 100644 charts/hashicorp/vault/0.22.0/templates/csi-clusterrole.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/csi-clusterrolebinding.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/csi-daemonset.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/csi-serviceaccount.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/injector-certs-secret.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/injector-clusterrole.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/injector-clusterrolebinding.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/injector-deployment.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/injector-disruptionbudget.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/injector-mutating-webhook.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/injector-network-policy.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/injector-psp-role.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/injector-psp-rolebinding.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/injector-psp.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/injector-role.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/injector-rolebinding.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/injector-service.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/injector-serviceaccount.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/prometheus-prometheusrules.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/prometheus-servicemonitor.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/server-clusterrolebinding.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/server-config-configmap.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/server-discovery-role.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/server-discovery-rolebinding.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/server-disruptionbudget.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/server-ha-active-service.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/server-ha-standby-service.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/server-headless-service.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/server-ingress.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/server-network-policy.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/server-psp-role.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/server-psp-rolebinding.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/server-psp.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/server-route.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/server-service.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/server-serviceaccount.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/server-statefulset.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/tests/server-test.yaml create mode 100644 charts/hashicorp/vault/0.22.0/templates/ui-service.yaml create mode 100644 charts/hashicorp/vault/0.22.0/values.openshift.yaml create mode 100644 charts/hashicorp/vault/0.22.0/values.schema.json create mode 100644 charts/hashicorp/vault/0.22.0/values.yaml create mode 100644 packages/hashicorp/consul/upstream.yaml create mode 100644 packages/hashicorp/vault/upstream.yaml diff --git a/assets/hashicorp/consul-0.49.0.tgz b/assets/hashicorp/consul-0.49.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..475b72f5940f7f3adf49b859a7067c0d087ae0a3 GIT binary patch literal 108152 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 z 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 F{{zJo24?^O literal 0 HcmV?d00001 diff --git a/assets/hashicorp/vault-0.22.0.tgz b/assets/hashicorp/vault-0.22.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..d8f66706b45f9fa004d7138fed69a9211a93d9c4 GIT binary patch literal 43180 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 literal 0 HcmV?d00001 diff --git a/charts/hashicorp/consul/0.49.0/.helmignore b/charts/hashicorp/consul/0.49.0/.helmignore new file mode 100644 index 000000000..d1180d2fb --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/.helmignore @@ -0,0 +1,4 @@ +.git/ +.terraform/ +bin/ +test/ diff --git a/charts/hashicorp/consul/0.49.0/Chart.yaml b/charts/hashicorp/consul/0.49.0/Chart.yaml new file mode 100644 index 000000000..2f3e8fa62 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/Chart.yaml @@ -0,0 +1,35 @@ +annotations: + artifacthub.io/images: | + - name: consul + image: hashicorp/consul:1.13.2 + - name: consul-k8s-control-plane + image: hashicorp/consul-k8s-control-plane:0.49.0 + - name: envoy + image: envoyproxy/envoy:v1.23.1 + artifacthub.io/license: MPL-2.0 + artifacthub.io/links: | + - name: Documentation + url: https://www.consul.io/docs/k8s + - name: hashicorp/consul + url: https://github.com/hashicorp/consul + - name: hashicorp/consul-k8s + url: https://github.com/hashicorp/consul-k8s + artifacthub.io/prerelease: "false" + artifacthub.io/signKey: | + fingerprint: C874011F0AB405110D02105534365D9472D7468F + url: https://keybase.io/hashicorp/pgp_keys.asc + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Hashicorp Consul + catalog.cattle.io/kube-version: '>=1.21.0-0' + catalog.cattle.io/release-name: consul +apiVersion: v2 +appVersion: 1.13.2 +description: Official HashiCorp Consul Chart +home: https://www.consul.io +icon: https://raw.githubusercontent.com/hashicorp/consul-k8s/main/assets/icon.png +kubeVersion: '>=1.21.0-0' +name: consul +sources: +- https://github.com/hashicorp/consul +- https://github.com/hashicorp/consul-k8s +version: 0.49.0 diff --git a/charts/hashicorp/consul/0.49.0/README.md b/charts/hashicorp/consul/0.49.0/README.md new file mode 100644 index 000000000..ccc695151 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/README.md @@ -0,0 +1,68 @@ +# Consul on Kubernetes Helm Chart + +--- + + **We're looking for feedback on how folks are using Consul on Kubernetes. Please fill out our brief [survey](https://hashicorp.sjc1.qualtrics.com/jfe/form/SV_4MANbw1BUku7YhL)!** + + + > **Warning** + > Please read the following issue to learn more about upcoming breaking changes that will be implemented by Q4 2022 for the default deployment of Consul on Kubernetes: [Enabling of service mesh by default and disabling of node-level client agents from Consul Service Mesh on Kubernetes and Catalog Sync](https://github.com/hashicorp/consul-k8s/issues/1438) + +## Overview + +This is the Official HashiCorp Helm chart for installing and configuring Consul on Kubernetes. This chart supports multiple use cases of Consul on Kubernetes, depending on the values provided. + +For full documentation on this Helm chart along with all the ways you can use Consul with Kubernetes, please see the Consul and Kubernetes documentation. + +> :warning: **Please note**: We take Consul's security and our users' trust very seriously. If +you believe you have found a security issue in Consul K8s, _please responsibly disclose_ +by contacting us at [security@hashicorp.com](mailto:security@hashicorp.com). + +## Features + + * [**Consul Service Mesh**](https://www.consul.io/docs/k8s/connect): + Run Consul Service Mesh on Kubernetes. This feature + injects Envoy sidecars and registers your Pods with Consul. + + * [**Catalog Sync**](https://www.consul.io/docs/k8s/service-sync): + Sync Consul services into first-class Kubernetes services and vice versa. + This enables Kubernetes to easily access external services and for + non-Kubernetes nodes to easily discover and access Kubernetes services. + +### Prerequisites + * **Helm 3.2+** (Helm 2 is not supported) + * **Kubernetes 1.21-1.24** - This is the earliest version of Kubernetes tested. + It is possible that this chart works with earlier versions but it is + untested. + +### Usage + +Detailed installation instructions for Consul on Kubernetes are found [here](https://www.consul.io/docs/k8s/installation/overview). + +1. Add the HashiCorp Helm Repository: + ``` bash + $ helm repo add hashicorp https://helm.releases.hashicorp.com + ``` + +2. Ensure you have access to the Consul Helm chart and you see the latest chart version listed. + If you have previously added the HashiCorp Helm repository, run `helm repo update`. + + ```bash + $ helm search repo hashicorp/consul + ``` + +3. Now you're ready to install Consul! To install Consul with the default configuration using Helm 3.2 run the following command below. + This will create a `consul` Kubernetes namespace if not already present, and install Consul on the dedicated namespace. + + ```bash + $ helm install consul hashicorp/consul --set global.name=consul --create-namespace -n consul + ``` + +Please see the many options supported in the `values.yaml` +file. These are also fully documented directly on the +[Consul website](https://www.consul.io/docs/platform/k8s/helm.html). + +# Tutorials + +You can find examples and complete tutorials on how to deploy Consul on +Kubernetes using Helm on the [HashiCorp Learn website](https://learn.hashicorp.com/consul). diff --git a/charts/hashicorp/consul/0.49.0/addons/gen.sh b/charts/hashicorp/consul/0.49.0/addons/gen.sh new file mode 100644 index 000000000..967b368c6 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/addons/gen.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +WD=$(dirname "$0") +WD=$(cd "$WD"; pwd) + +set -eux + +TEMPLATES="${WD}/../templates" +DASHBOARDS="${WD}/dashboards" +TMP=$(mktemp -d) + +# create Prometheus template +helm template prometheus prometheus \ + --repo https://prometheus-community.github.io/helm-charts \ + --namespace "replace-me-namespace" \ + --version 13.2.1 \ + -f "${WD}/values/prometheus.yaml" \ + > "${TEMPLATES}/prometheus.yaml" + +# Find and replace `replace-me-namespace` with `{{ .Release.Namespace }}` in Prometheus template. +sed -i'.orig' 's/replace-me-namespace/{{ .Release.Namespace }}/g' "${TEMPLATES}/prometheus.yaml" +# Add a comment to the top of the template file mentioning that the file is auto-generated. +sed -i'.orig' '1i\ +# This file is auto-generated, see addons/gen.sh +' "${TEMPLATES}/prometheus.yaml" +# Add `{{- if .Values.prometheus.enabled }} to the top of the Prometheus template to ensure it is only templated when enabled. +sed -i'.orig' '1i\ +{{- if .Values.prometheus.enabled }} +' "${TEMPLATES}/prometheus.yaml" +# Add `{{- end }} to the bottom of the Prometheus template to ensure it is only templated when enabled (closes the `if` statement). +sed -i'.orig' -e '$a\ +{{- end }}' "${TEMPLATES}/prometheus.yaml" +# Remove the `prometheus.yaml.orig` file that is created as a side-effect of the `sed` command on OS X. +rm "${TEMPLATES}/prometheus.yaml.orig" \ No newline at end of file diff --git a/charts/hashicorp/consul/0.49.0/addons/values/prometheus.yaml b/charts/hashicorp/consul/0.49.0/addons/values/prometheus.yaml new file mode 100644 index 000000000..9ffe9af5a --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/addons/values/prometheus.yaml @@ -0,0 +1,18 @@ +# Disable non-essential components +alertmanager: + enabled: false +pushgateway: + enabled: false +kubeStateMetrics: + enabled: false +nodeExporter: + enabled: false +server: + podAnnotations: + "consul.hashicorp.com/connect-inject": "false" + persistentVolume: + enabled: false + readinessProbeInitialDelay: 0 + # Speed up scraping a bit from the default + global: + scrape_interval: 15s diff --git a/charts/hashicorp/consul/0.49.0/assets/icon.png b/charts/hashicorp/consul/0.49.0/assets/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0798b2445de59405c89614338a3276bfb36d832f GIT binary patch literal 7981 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 OK}lX+u13Zp{Qm*>/dev/null | grep -E '".+"' + {{- if .Values.client.resources }} + resources: + {{- if eq (typeOf .Values.client.resources) "string" }} + {{ tpl .Values.client.resources . | nindent 12 | trim }} + {{- else }} + {{- toYaml .Values.client.resources | nindent 12 }} + {{- end }} + {{- end }} + {{- if not .Values.global.openshift.enabled }} + securityContext: + {{- toYaml .Values.client.containerSecurityContext.client | nindent 12 }} + {{- end }} + {{- if .Values.client.extraContainers }} + {{ toYaml .Values.client.extraContainers | nindent 8 }} + {{- end }} + {{- if (or .Values.global.acls.manageSystemACLs (and .Values.global.tls.enabled (not .Values.global.tls.enableAutoEncrypt))) }} + initContainers: + {{- if .Values.global.acls.manageSystemACLs }} + - name: client-acl-init + image: {{ .Values.global.imageK8S }} + env: + - name: CONSUL_HTTP_ADDR + {{- if .Values.global.tls.enabled }} + value: https://{{ template "consul.fullname" . }}-server.{{ .Release.Namespace }}.svc:8501 + {{- else }} + value: http://{{ template "consul.fullname" . }}-server.{{ .Release.Namespace }}.svc:8500 + {{- end }} + {{- if (and .Values.global.tls.enabled (not .Values.externalServers.useSystemRoots)) }} + - name: CONSUL_CACERT + {{- if .Values.global.secretsBackend.vault.enabled }} + value: "/vault/secrets/serverca.crt" + {{- else }} + value: "/consul/tls/ca/tls.crt" + {{- end }} + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + consul-k8s-control-plane acl-init \ + -component-name=client \ + -acl-auth-method="{{ template "consul.fullname" . }}-k8s-component-auth-method" \ + {{- if .Values.global.adminPartitions.enabled }} + -partition={{ .Values.global.adminPartitions.name }} \ + {{- end }} + -log-level={{ default .Values.global.logLevel .Values.client.logLevel }} \ + -log-json={{ .Values.global.logJSON }} \ + {{- if .Values.externalServers.enabled }} + {{- if .Values.global.tls.enabled }} + -use-https \ + {{- end }} + {{- range .Values.externalServers.hosts }} + -server-address={{ quote . }} \ + {{- end }} + -server-port={{ .Values.externalServers.httpsPort }} \ + {{- if .Values.externalServers.tlsServerName }} + -tls-server-name={{ .Values.externalServers.tlsServerName }} \ + {{- end }} + {{- end }} + -consul-api-timeout={{ .Values.global.consulAPITimeout }} \ + -init-type="client" + volumeMounts: + - name: aclconfig + mountPath: /consul/aclconfig + - mountPath: /consul/login + name: consul-data + readOnly: false + {{- if (and .Values.global.tls.enabled (not .Values.global.secretsBackend.vault.enabled) (not .Values.externalServers.useSystemRoots)) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: false + {{- end }} + resources: + requests: + memory: "25Mi" + cpu: "50m" + limits: + memory: "25Mi" + cpu: "50m" + {{- if not .Values.global.openshift.enabled }} + securityContext: + {{- toYaml .Values.client.containerSecurityContext.aclInit | nindent 10 }} + {{- end }} + {{- end }} + {{- if and .Values.global.tls.enabled (not .Values.global.tls.enableAutoEncrypt) }} + - name: client-tls-init + image: "{{ default .Values.global.image .Values.client.image }}" + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + command: + - "/bin/sh" + - "-ec" + - | + cd /consul/tls/client + consul tls cert create -client \ + -additional-ipaddress=${HOST_IP} \ + -additional-ipaddress=${POD_IP} \ + -dc={{ .Values.global.datacenter }} \ + -domain={{ .Values.global.domain }} \ + -ca=/consul/tls/ca/cert/tls.crt \ + -key=/consul/tls/ca/key/tls.key + mv {{ .Values.global.datacenter }}-client-{{ .Values.global.domain }}-0.pem tls.crt + mv {{ .Values.global.datacenter }}-client-{{ .Values.global.domain }}-0-key.pem tls.key + volumeMounts: + {{- if not .Values.global.secretsBackend.vault.enabled }} + - name: consul-client-cert + mountPath: /consul/tls/client + - name: consul-ca-cert + mountPath: /consul/tls/ca/cert + readOnly: true + - name: consul-ca-key + mountPath: /consul/tls/ca/key + readOnly: true + {{- end }} + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + {{- if not .Values.global.openshift.enabled }} + securityContext: + {{- toYaml .Values.client.containerSecurityContext.tlsInit | nindent 10 }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.client.nodeSelector }} + nodeSelector: + {{ tpl .Values.client.nodeSelector . | indent 8 | trim }} + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/client-podsecuritypolicy.yaml b/charts/hashicorp/consul/0.49.0/templates/client-podsecuritypolicy.yaml new file mode 100644 index 000000000..0121bdf58 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/client-podsecuritypolicy.yaml @@ -0,0 +1,76 @@ +{{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled))) }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-client + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: client +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + {{- if .Values.client.dataDirectoryHostPath }} + - 'hostPath' + {{- end }} + {{- if .Values.client.hostNetwork }} + hostNetwork: {{ .Values.client.hostNetwork }} + {{- else }} + hostNetwork: false + {{- end }} + hostPorts: + {{- if (not (and .Values.global.tls.enabled .Values.global.tls.httpsOnly)) }} + # HTTP Port + - min: 8500 + max: 8500 + {{- end }} + {{- if .Values.global.tls.enabled }} + # HTTPS port + - min: 8501 + max: 8501 + {{- end }} + {{- if .Values.client.grpc }} + # gRPC Port + - min: 8502 + max: 8502 + {{- end }} + {{- if (or .Values.client.exposeGossipPorts .Values.client.hostNetwork) }} + - min: 8301 + max: 8301 + {{- end }} + {{- if .Values.client.hostNetwork }} + - min: 8600 + max: 8600 + {{- end }} + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false + {{- if .Values.client.dataDirectoryHostPath }} + allowedHostPaths: + - pathPrefix: {{ .Values.client.dataDirectoryHostPath | quote }} + readOnly: false + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/client-role.yaml b/charts/hashicorp/consul/0.49.0/templates/client-role.yaml new file mode 100644 index 000000000..7f05b82e6 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/client-role.yaml @@ -0,0 +1,43 @@ +{{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-client + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: client +{{- if (or .Values.global.acls.manageSystemACLs .Values.global.enablePodSecurityPolicies .Values.global.openshift.enabled) }} +rules: +{{- if .Values.global.enablePodSecurityPolicies }} + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" . }}-client + verbs: + - use +{{- end }} +{{- if .Values.global.acls.manageSystemACLs }} + - apiGroups: [""] + resources: + - secrets + resourceNames: + - {{ template "consul.fullname" . }}-client-acl-token + verbs: + - get +{{- end }} +{{- if .Values.global.openshift.enabled}} + - apiGroups: ["security.openshift.io"] + resources: ["securitycontextconstraints"] + resourceNames: + - {{ template "consul.fullname" . }}-client + verbs: + - use +{{- end}} +{{- else}} +rules: [] +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/client-rolebinding.yaml b/charts/hashicorp/consul/0.49.0/templates/client-rolebinding.yaml new file mode 100644 index 000000000..b034c70e5 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/client-rolebinding.yaml @@ -0,0 +1,20 @@ +{{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-client + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: client +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-client +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-client +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/client-securitycontextconstraints.yaml b/charts/hashicorp/consul/0.49.0/templates/client-securitycontextconstraints.yaml new file mode 100644 index 000000000..07e771138 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/client-securitycontextconstraints.yaml @@ -0,0 +1,56 @@ +{{- if (and .Values.global.openshift.enabled (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled))) }} +apiVersion: security.openshift.io/v1 +kind: SecurityContextConstraints +metadata: + name: {{ template "consul.fullname" . }}-client + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: client + annotations: + kubernetes.io/description: {{ template "consul.fullname" . }}-client are the security context constraints required + to run the consul client. +{{- if .Values.client.dataDirectoryHostPath }} +allowHostDirVolumePlugin: true +{{- else }} +allowHostDirVolumePlugin: false +{{- end}} +allowHostIPC: false +allowHostNetwork: {{ .Values.client.hostNetwork }} +allowHostPID: false +allowHostPorts: true +allowPrivilegeEscalation: true +allowPrivilegedContainer: false +allowedCapabilities: null +defaultAddCapabilities: null +fsGroup: + type: MustRunAs +groups: [] +priority: null +readOnlyRootFilesystem: false +requiredDropCapabilities: +- KILL +- MKNOD +- SETUID +- SETGID +runAsUser: + type: MustRunAsRange +seLinuxContext: + type: MustRunAs +supplementalGroups: + type: MustRunAs +users: [] +volumes: +- configMap +- downwardAPI +- emptyDir +- persistentVolumeClaim +- projected +- secret +{{- if .Values.client.dataDirectoryHostPath }} +- hostPath +{{- end }} +{{- end}} diff --git a/charts/hashicorp/consul/0.49.0/templates/client-serviceaccount.yaml b/charts/hashicorp/consul/0.49.0/templates/client-serviceaccount.yaml new file mode 100644 index 000000000..addd757b8 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/client-serviceaccount.yaml @@ -0,0 +1,23 @@ +{{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-client + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: client + {{- if .Values.client.serviceAccount.annotations }} + annotations: + {{ tpl .Values.client.serviceAccount.annotations . | nindent 4 | trim }} + {{- end }} +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/client-snapshot-agent-deployment.yaml b/charts/hashicorp/consul/0.49.0/templates/client-snapshot-agent-deployment.yaml new file mode 100644 index 000000000..19ffff23c --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/client-snapshot-agent-deployment.yaml @@ -0,0 +1,281 @@ +{{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} +{{- if or (and .Values.client.snapshotAgent.configSecret.secretName (not .Values.client.snapshotAgent.configSecret.secretKey)) (and (not .Values.client.snapshotAgent.configSecret.secretName) .Values.client.snapshotAgent.configSecret.secretKey) }}{{fail "client.snapshotAgent.configSecret.secretKey and client.snapshotAgent.configSecret.secretName must both be specified." }}{{ end -}} +{{- if .Values.client.snapshotAgent.enabled }} +{{- if or (and .Values.client.snapshotAgent.configSecret.secretName (not .Values.client.snapshotAgent.configSecret.secretKey)) (and (not .Values.client.snapshotAgent.configSecret.secretName) .Values.client.snapshotAgent.configSecret.secretKey) }}{{fail "client.snapshotAgent.configSecret.secretKey and client.snapshotAgent.configSecret.secretName must both be specified." }}{{ end -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "consul.fullname" . }}-snapshot-agent + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: client-snapshot-agent +spec: + replicas: {{ .Values.client.snapshotAgent.replicas }} + selector: + matchLabels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: client-snapshot-agent + template: + metadata: + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: client-snapshot-agent + annotations: + "consul.hashicorp.com/connect-inject": "false" + {{- if .Values.global.secretsBackend.vault.enabled }} + {{- if .Values.client.snapshotAgent.configSecret.secretName }} + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.consulSnapshotAgentRole }} + {{- else if and .Values.global.tls.enabled }} + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.consulCARole }} + {{- end }} + {{- if .Values.global.tls.enabled }} + "vault.hashicorp.com/agent-init-first": "true" + "vault.hashicorp.com/agent-inject": "true" + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" . }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": "{{ .Values.global.secretsBackend.vault.ca.secretName }}" + "vault.hashicorp.com/ca-cert": "/vault/custom/{{ .Values.global.secretsBackend.vault.ca.secretKey }}" + {{- end }} + {{- if .Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl .Values.global.secretsBackend.vault.agentAnnotations . | nindent 8 | trim }} + {{- end }} + {{- end }} + {{- if .Values.global.enterpriseLicense.secretName }} + {{- with .Values.global.enterpriseLicense }} + "vault.hashicorp.com/agent-inject-secret-enterpriselicense.txt": "{{ .secretName }}" + "vault.hashicorp.com/agent-inject-template-enterpriselicense.txt": {{ template "consul.vaultSecretTemplate" . }} + {{- end }} + {{- end }} + {{- if .Values.client.snapshotAgent.configSecret.secretName }} + {{- with .Values.client.snapshotAgent.configSecret }} + "vault.hashicorp.com/agent-inject-secret-snapshot-agent-config.json": "{{ .secretName }}" + "vault.hashicorp.com/agent-inject-template-snapshot-agent-config.json": {{ template "consul.vaultSecretTemplate" . }} + {{- end }} + {{- end }} + {{- end }} + spec: + {{- if .Values.client.tolerations }} + tolerations: + {{ tpl .Values.client.tolerations . | nindent 8 | trim }} + {{- end }} + terminationGracePeriodSeconds: 10 + serviceAccountName: {{ template "consul.fullname" . }}-snapshot-agent + {{- if .Values.client.priorityClassName }} + priorityClassName: {{ .Values.client.priorityClassName | quote }} + {{- end }} + volumes: + {{- if .Values.client.snapshotAgent.caCert }} + - name: extra-ssl-certs + emptyDir: + medium: "Memory" + {{- end }} + {{- if (or .Values.global.acls.manageSystemACLs .Values.global.tls.enabled (and .Values.client.snapshotAgent.configSecret.secretName .Values.client.snapshotAgent.configSecret.secretKey) (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey .Values.global.enterpriseLicense.enableLicenseAutoload)) }} + - name: consul-data + emptyDir: + medium: "Memory" + {{- if (and .Values.client.snapshotAgent.configSecret.secretName .Values.client.snapshotAgent.configSecret.secretKey (not .Values.global.secretsBackend.vault.enabled)) }} + - name: snapshot-config + secret: + secretName: {{ .Values.client.snapshotAgent.configSecret.secretName }} + items: + - key: {{ .Values.client.snapshotAgent.configSecret.secretKey }} + path: snapshot-config.json + {{- end }} + {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey .Values.global.enterpriseLicense.enableLicenseAutoload (not .Values.global.secretsBackend.vault.enabled) (not .Values.global.acls.manageSystemACLs)) }} + - name: consul-license + secret: + secretName: {{ .Values.global.enterpriseLicense.secretName }} + {{- end }} + {{- if .Values.global.tls.enabled }} + {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + {{- if .Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + emptyDir: + medium: "Memory" + {{- end }} + {{- end }} + {{- end }} + containers: + - name: consul-snapshot-agent + image: "{{ default .Values.global.image .Values.client.image }}" + env: + {{- if .Values.client.snapshotAgent.caCert }} + - name: SSL_CERT_DIR + value: "/etc/ssl/certs:/extra-ssl-certs" + {{- end }} + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + {{- if .Values.global.tls.enabled }} + - name: CONSUL_HTTP_ADDR + value: https://$(HOST_IP):8501 + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- else }} + - name: CONSUL_HTTP_ADDR + value: http://$(HOST_IP):8500 + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + - name: CONSUL_HTTP_TOKEN_FILE + value: /consul/login/acl-token + {{- else }} + {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey .Values.global.enterpriseLicense.enableLicenseAutoload) }} + - name: CONSUL_LICENSE_PATH + {{- if .Values.global.secretsBackend.vault.enabled }} + value: /vault/secrets/enterpriselicense.txt + {{- else }} + value: /consul/license/{{ .Values.global.enterpriseLicense.secretKey }} + {{- end }} + {{- end }} + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + {{- if .Values.client.snapshotAgent.caCert }} + cat < /extra-ssl-certs/custom-ca.pem + {{- .Values.client.snapshotAgent.caCert | nindent 14 }} + EOF + {{- end }} + exec /bin/consul snapshot agent \ + -interval={{ .Values.client.snapshotAgent.interval }} \ + {{- if (and .Values.client.snapshotAgent.configSecret.secretName .Values.client.snapshotAgent.configSecret.secretKey) }} + {{- if .Values.global.secretsBackend.vault.enabled }} + -config-file=/vault/secrets/snapshot-agent-config.json \ + {{- else }} + -config-dir=/consul/config \ + {{- end }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + -config-dir=/consul/login \ + {{- end }} + volumeMounts: + {{- if .Values.client.snapshotAgent.caCert }} + - name: extra-ssl-certs + mountPath: /extra-ssl-certs + readOnly: false + {{- end }} + {{- if (or .Values.global.acls.manageSystemACLs .Values.global.tls.enabled (and .Values.client.snapshotAgent.configSecret.secretName .Values.client.snapshotAgent.configSecret.secretKey) (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey .Values.global.enterpriseLicense.enableLicenseAutoload)) }} + {{- if (and .Values.client.snapshotAgent.configSecret.secretName .Values.client.snapshotAgent.configSecret.secretKey (not .Values.global.secretsBackend.vault.enabled)) }} + - name: snapshot-config + mountPath: /consul/config + readOnly: true + {{- end }} + - mountPath: /consul/login + name: consul-data + readOnly: true + {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey .Values.global.enterpriseLicense.enableLicenseAutoload (not .Values.global.secretsBackend.vault.enabled) (not .Values.global.acls.manageSystemACLs))}} + - name: consul-license + mountPath: /consul/license + readOnly: true + {{- end }} + {{- if .Values.global.tls.enabled }} + {{- if .Values.global.tls.enableAutoEncrypt}} + - name: consul-auto-encrypt-ca-cert + {{- else }} + - name: consul-ca-cert + {{- end }} + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + lifecycle: + preStop: + exec: + command: + - "/bin/sh" + - "-ec" + - | + /bin/consul logout + {{- end }} + {{- with .Values.client.snapshotAgent.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if (or .Values.global.acls.manageSystemACLs (and .Values.global.tls.enabled .Values.global.tls.enableAutoEncrypt)) }} + initContainers: + {{- if (and .Values.global.tls.enabled .Values.global.tls.enableAutoEncrypt) }} + {{- include "consul.getAutoEncryptClientCA" . | nindent 6 }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + - name: snapshot-agent-acl-init + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + {{- if .Values.global.tls.enabled }} + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- end }} + - name: CONSUL_HTTP_ADDR + {{- if .Values.global.tls.enabled }} + value: https://$(HOST_IP):8501 + {{- else }} + value: http://$(HOST_IP):8500 + {{- end }} + image: {{ .Values.global.imageK8S }} + volumeMounts: + - mountPath: /consul/login + name: consul-data + readOnly: false + {{- if .Values.global.tls.enabled }} + {{- if .Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + {{- else }} + - name: consul-ca-cert + {{- end }} + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + consul-k8s-control-plane acl-init \ + -component-name=snapshot-agent \ + -acl-auth-method={{ template "consul.fullname" . }}-k8s-component-auth-method \ + {{- if .Values.global.adminPartitions.enabled }} + -partition={{ .Values.global.adminPartitions.name }} \ + {{- end }} + -token-sink-file=/consul/login/acl-token \ + -consul-api-timeout={{ .Values.global.consulAPITimeout }} \ + -log-level={{ default .Values.global.logLevel }} \ + -log-json={{ .Values.global.logJSON }} + resources: + requests: + memory: "25Mi" + cpu: "50m" + limits: + memory: "25Mi" + cpu: "50m" + {{- end }} + {{- end }} + {{- if .Values.client.nodeSelector }} + nodeSelector: + {{ tpl .Values.client.nodeSelector . | indent 8 | trim }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/client-snapshot-agent-podsecuritypolicy.yaml b/charts/hashicorp/consul/0.49.0/templates/client-snapshot-agent-podsecuritypolicy.yaml new file mode 100644 index 000000000..dd324a397 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/client-snapshot-agent-podsecuritypolicy.yaml @@ -0,0 +1,42 @@ +{{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled))) }} +{{- if .Values.client.snapshotAgent.enabled }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-snapshot-agent + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: client-snapshot-agent +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/client-snapshot-agent-role.yaml b/charts/hashicorp/consul/0.49.0/templates/client-snapshot-agent-role.yaml new file mode 100644 index 000000000..3077bc96f --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/client-snapshot-agent-role.yaml @@ -0,0 +1,26 @@ +{{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} +{{- if .Values.client.snapshotAgent.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-snapshot-agent + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: client-snapshot-agent +{{- if .Values.global.enablePodSecurityPolicies }} +rules: +- apiGroups: [ "policy" ] + resources: [ "podsecuritypolicies" ] + resourceNames: + - {{ template "consul.fullname" . }}-snapshot-agent + verbs: + - use +{{- else }} +rules: [ ] +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/client-snapshot-agent-rolebinding.yaml b/charts/hashicorp/consul/0.49.0/templates/client-snapshot-agent-rolebinding.yaml new file mode 100644 index 000000000..e966c4e2a --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/client-snapshot-agent-rolebinding.yaml @@ -0,0 +1,22 @@ +{{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} +{{- if .Values.client.snapshotAgent.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-snapshot-agent + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: client-snapshot-agent +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-snapshot-agent +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-snapshot-agent +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/client-snapshot-agent-serviceaccount.yaml b/charts/hashicorp/consul/0.49.0/templates/client-snapshot-agent-serviceaccount.yaml new file mode 100644 index 000000000..a485ff0a5 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/client-snapshot-agent-serviceaccount.yaml @@ -0,0 +1,25 @@ +{{- if (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} +{{- if .Values.client.snapshotAgent.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-snapshot-agent + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: client-snapshot-agent + {{- if .Values.client.snapshotAgent.serviceAccount.annotations }} + annotations: + {{ tpl .Values.client.snapshotAgent.serviceAccount.annotations . | nindent 4 | trim }} + {{- end }} +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/cni-clusterrole.yaml b/charts/hashicorp/consul/0.49.0/templates/cni-clusterrole.yaml new file mode 100644 index 000000000..39dc5ead5 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/cni-clusterrole.yaml @@ -0,0 +1,38 @@ +{{- if .Values.connectInject.cni.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "consul.fullname" . }}-cni + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: cni +rules: +- apiGroups: [""] + resources: + - pods + verbs: + - get + - list + - watch + - patch + - update +- apiGroups: ["policy"] + resources: + - podsecuritypolicies + resourceNames: + - {{ template "consul.fullname" . }}-cni + verbs: + - use +{{- if .Values.global.openshift.enabled}} +- apiGroups: ["security.openshift.io"] + resources: ["securitycontextconstraints"] + resourceNames: + - {{ template "consul.fullname" . }}-cni + verbs: + - use +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/cni-clusterrolebinding.yaml b/charts/hashicorp/consul/0.49.0/templates/cni-clusterrolebinding.yaml new file mode 100644 index 000000000..86c19d86a --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/cni-clusterrolebinding.yaml @@ -0,0 +1,20 @@ +{{- if .Values.connectInject.cni.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "consul.fullname" . }}-cni + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: cni +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "consul.fullname" . }}-cni +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-cni + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/cni-daemonset.yaml b/charts/hashicorp/consul/0.49.0/templates/cni-daemonset.yaml new file mode 100644 index 000000000..7b9f90d93 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/cni-daemonset.yaml @@ -0,0 +1,84 @@ +{{- if (and (.Values.connectInject.cni.enabled) (not .Values.connectInject.enabled)) }}{{ fail "connectInject.enabled must be true if connectInject.cni.enabled is true" }}{{ end -}} +{{- if .Values.connectInject.cni.enabled }} +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: {{ template "consul.fullname" . }}-cni + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: cni +spec: + {{- if .Values.connectInject.cni.updateStrategy }} + updateStrategy: + {{ tpl .Values.connectInject.cni.updateStrategy . | nindent 4 | trim }} + {{- end }} + selector: + matchLabels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: cni + template: + metadata: + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: cni + annotations: + consul.hashicorp.com/connect-inject: "false" + spec: + # consul-cni only runs on linux operating systems + nodeSelector: + kubernetes.io/os: linux + tolerations: + # Mark the pod as a critical add-on for rescheduling. + - key: CriticalAddonsOnly + operator: Exists + - effect: NoExecute + operator: Exists + # Tell kubernetes that this daemonset is critical so that it will be scheduled on a new node before other pods + priorityClassName: system-node-critical + serviceAccountName: {{ template "consul.fullname" . }}-cni + {{- if not .Values.global.openshift.enabled }} + securityContext: + {{- toYaml .Values.connectInject.cni.securityContext | nindent 8 -}} + {{- end }} + # Minimize downtime during a rolling upgrade or deletion; tell Kubernetes to do a "force + # deletion": https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods. + terminationGracePeriodSeconds: 10 + containers: + # This container installs the consul CNI binaries and CNI network config file on each node + - name: install-cni + image: {{ .Values.global.imageK8S }} + securityContext: + privileged: true + command: + - consul-k8s-control-plane + - install-cni + - -log-level={{ default .Values.global.logLevel .Values.connectInject.cni.logLevel }} + - -cni-bin-dir={{ .Values.connectInject.cni.cniBinDir }} + - -cni-net-dir={{ .Values.connectInject.cni.cniNetDir }} + - -multus={{ .Values.connectInject.cni.multus }} + {{- with .Values.connectInject.cni.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - mountPath: {{ .Values.connectInject.cni.cniBinDir }} + name: cni-bin-dir + - mountPath: {{ .Values.connectInject.cni.cniNetDir }} + name: cni-net-dir + volumes: + # Used to install CNI. + - name: cni-bin-dir + hostPath: + path: {{ .Values.connectInject.cni.cniBinDir }} + - name: cni-net-dir + hostPath: + path: {{ .Values.connectInject.cni.cniNetDir }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/cni-networkattachmentdefinition.yaml b/charts/hashicorp/consul/0.49.0/templates/cni-networkattachmentdefinition.yaml new file mode 100644 index 000000000..d0feaf5cb --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/cni-networkattachmentdefinition.yaml @@ -0,0 +1,25 @@ +{{- if (and (.Values.connectInject.cni.enabled) (.Values.connectInject.cni.multus)) }} +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: {{ template "consul.fullname" . }}-cni + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: cni +spec: + config: '{ + "cniVersion": "0.3.1", + "type": "consul-cni", + "cni_bin_dir": "{{ .Values.connectInject.cni.cniBinDir }}", + "cni_net_dir": "{{ .Values.connectInject.cni.cniNetDir }}", + "kubeconfig": "ZZZ-consul-cni-kubeconfig", + "log_level": "{{ default .Values.global.logLevel .Values.connectInject.cni.logLevel }}", + "multus": true, + "name": "consul-cni", + "type": "consul-cni" + }' +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/cni-podsecuritypolicy.yaml b/charts/hashicorp/consul/0.49.0/templates/cni-podsecuritypolicy.yaml new file mode 100644 index 000000000..15b96bc23 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/cni-podsecuritypolicy.yaml @@ -0,0 +1,31 @@ +{{- if (and .Values.connectInject.cni.enabled .Values.global.enablePodSecurityPolicies) }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-cni + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: cni +spec: + privileged: true + # GKE requires that allowPrivilegeEscalation:true if privileged: true. + allowPrivilegeEscalation: true + volumes: + - hostPath + - secret + - emptyDir + hostNetwork: false + readOnlyRootFilesystem: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/cni-resourcequota.yaml b/charts/hashicorp/consul/0.49.0/templates/cni-resourcequota.yaml new file mode 100644 index 000000000..abfe5a887 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/cni-resourcequota.yaml @@ -0,0 +1,22 @@ +{{- if .Values.connectInject.cni.enabled }} +apiVersion: v1 +kind: ResourceQuota +metadata: + name: {{ template "consul.fullname" . }}-cni + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: cni +spec: + hard: + pods: {{ .Values.connectInject.cni.resourceQuota.pods | quote }} + scopeSelector: + matchExpressions: + - operator: In + scopeName: PriorityClass + values: + - system-node-critical +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/cni-securitycontextconstraints.yaml b/charts/hashicorp/consul/0.49.0/templates/cni-securitycontextconstraints.yaml new file mode 100644 index 000000000..95cfc555e --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/cni-securitycontextconstraints.yaml @@ -0,0 +1,50 @@ +{{- if (and (.Values.connectInject.cni.enabled) (.Values.global.openshift.enabled)) }} +apiVersion: security.openshift.io/v1 +kind: SecurityContextConstraints +metadata: + name: {{ template "consul.fullname" . }}-cni + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: cni + annotations: + kubernetes.io/description: {{ template "consul.fullname" . }}-cni are the security context constraints required + to run consul-cni. +allowHostDirVolumePlugin: true +allowHostIPC: false +allowHostNetwork: false +allowHostPID: false +allowHostPorts: false +allowPrivilegeEscalation: true +allowPrivilegedContainer: true +allowedCapabilities: null +defaultAddCapabilities: null +fsGroup: + type: MustRunAs +groups: [] +priority: null +readOnlyRootFilesystem: false +requiredDropCapabilities: +- KILL +- MKNOD +- SETUID +- SETGID +runAsUser: + type: MustRunAsRange +seLinuxContext: + type: MustRunAs +supplementalGroups: + type: MustRunAs +users: [] +volumes: +- configMap +- downwardAPI +- emptyDir +- persistentVolumeClaim +- projected +- secret +- hostPath +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/cni-serviceaccount.yaml b/charts/hashicorp/consul/0.49.0/templates/cni-serviceaccount.yaml new file mode 100644 index 000000000..6b2a7627f --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/cni-serviceaccount.yaml @@ -0,0 +1,19 @@ +{{- if .Values.connectInject.cni.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-cni + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: cni +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} +- name: {{ .name }} +{{- end }} +{{- end }} +{{- end}} diff --git a/charts/hashicorp/consul/0.49.0/templates/connect-inject-clusterrole.yaml b/charts/hashicorp/consul/0.49.0/templates/connect-inject-clusterrole.yaml new file mode 100644 index 000000000..a3f882296 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/connect-inject-clusterrole.yaml @@ -0,0 +1,109 @@ +{{- if or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled) }} +# The ClusterRole to enable the Connect injector to get, list, watch and patch MutatingWebhookConfiguration. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "consul.fullname" . }}-connect-injector + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: connect-injector +rules: +{{- if .Values.global.acls.manageSystemACLs }} +- apiGroups: [ "" ] + resources: ["serviceaccounts", "secrets"] + verbs: + - get +{{- end }} +- apiGroups: [ "" ] + resources: [ "endpoints", "services", "namespaces", "nodes" ] + verbs: + - "get" + - "list" + - "watch" +- apiGroups: [ "" ] + resources: + - pods + verbs: + - "get" + - "list" + - "watch" + - "update" +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - list + - update +{{- if (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName)}} +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + verbs: + - get + - list + - watch + - patch +{{- end }} +{{- if .Values.global.peering.enabled }} +- apiGroups: [ "" ] + resources: ["secrets"] + verbs: + - "get" + - "list" + - "watch" + - "create" + - "update" + - "delete" +- apiGroups: ["consul.hashicorp.com"] + resources: ["peeringacceptors"] + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - consul.hashicorp.com + resources: + - peeringacceptors/status + verbs: + - get + - patch + - update +- apiGroups: ["consul.hashicorp.com"] + resources: ["peeringdialers"] + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - consul.hashicorp.com + resources: + - peeringdialers/status + verbs: + - get + - patch + - update +{{- end }} +{{- if .Values.global.enablePodSecurityPolicies }} +- apiGroups: [ "policy" ] + resources: [ "podsecuritypolicies" ] + resourceNames: + - {{ template "consul.fullname" . }}-connect-injector + verbs: + - use +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/connect-inject-clusterrolebinding.yaml b/charts/hashicorp/consul/0.49.0/templates/connect-inject-clusterrolebinding.yaml new file mode 100644 index 000000000..c380adb31 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/connect-inject-clusterrolebinding.yaml @@ -0,0 +1,20 @@ +{{- if or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "consul.fullname" . }}-connect-injector + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: connect-injector +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "consul.fullname" . }}-connect-injector +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} +{{- end }} \ No newline at end of file diff --git a/charts/hashicorp/consul/0.49.0/templates/connect-inject-deployment.yaml b/charts/hashicorp/consul/0.49.0/templates/connect-inject-deployment.yaml new file mode 100644 index 000000000..779dceb57 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/connect-inject-deployment.yaml @@ -0,0 +1,437 @@ +{{- if and .Values.global.peering.enabled (not .Values.connectInject.enabled) }}{{ fail "setting global.peering.enabled to true requires connectInject.enabled to be true" }}{{ end }} +{{- if (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled)) }} +{{- if not (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }}{{ fail "clients must be enabled for connect injection" }}{{ end }} +{{- if not .Values.client.grpc }}{{ fail "client.grpc must be true for connect injection" }}{{ end }} +{{- if and .Values.connectInject.consulNamespaces.mirroringK8S (not .Values.global.enableConsulNamespaces) }}{{ fail "global.enableConsulNamespaces must be true if mirroringK8S=true" }}{{ end }} +{{- if and .Values.global.adminPartitions.enabled (not .Values.global.enableConsulNamespaces) }}{{ fail "global.enableConsulNamespaces must be true if global.adminPartitions.enabled=true" }}{{ end }} +{{- if .Values.connectInject.centralConfig }}{{- if eq (toString .Values.connectInject.centralConfig.enabled) "false" }}{{ fail "connectInject.centralConfig.enabled cannot be set to false; to disable, set enable_central_service_config to false in server.extraConfig and client.extraConfig" }}{{ end -}}{{ end -}} +{{- if .Values.connectInject.centralConfig }}{{- if .Values.connectInject.centralConfig.defaultProtocol }}{{ fail "connectInject.centralConfig.defaultProtocol is no longer supported; instead you must migrate to CRDs (see www.consul.io/docs/k8s/crds/upgrade-to-crds)" }}{{ end }}{{ end -}} +{{- if .Values.connectInject.centralConfig }}{{ if .Values.connectInject.centralConfig.proxyDefaults }}{{- if ne (trim .Values.connectInject.centralConfig.proxyDefaults) `{}` }}{{ fail "connectInject.centralConfig.proxyDefaults is no longer supported; instead you must migrate to CRDs (see www.consul.io/docs/k8s/crds/upgrade-to-crds)" }}{{ end }}{{ end }}{{ end -}} +{{- if .Values.connectInject.imageEnvoy }}{{ fail "connectInject.imageEnvoy must be specified in global.imageEnvoy" }}{{ end }} +{{- if .Values.global.lifecycleSidecarContainer }}{{ fail "global.lifecycleSidecarContainer has been renamed to global.consulSidecarContainer. Please set values using global.consulSidecarContainer." }}{{ end }} +{{ template "consul.validateVaultWebhookCertConfiguration" . }} +{{- template "consul.reservedNamesFailer" (list .Values.connectInject.consulNamespaces.consulDestinationNamespace "connectInject.consulNamespaces.consulDestinationNamespace") }} +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- $serverExposeServiceEnabled := (or (and (ne (.Values.server.exposeService.enabled | toString) "-") .Values.server.exposeService.enabled) (and (eq (.Values.server.exposeService.enabled | toString) "-") (or .Values.global.peering.enabled .Values.global.adminPartitions.enabled))) -}} +{{- if not (or (eq .Values.global.peering.tokenGeneration.serverAddresses.source "") (or (eq .Values.global.peering.tokenGeneration.serverAddresses.source "static") (eq .Values.global.peering.tokenGeneration.serverAddresses.source "consul"))) }}{{ fail "global.peering.tokenGeneration.serverAddresses.source must be one of empty string, 'consul' or 'static'" }}{{ end }} +# The deployment for running the Connect sidecar injector +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: connect-injector +spec: + replicas: {{ .Values.connectInject.replicas }} + selector: + matchLabels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: connect-injector + template: + metadata: + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: connect-injector + annotations: + "consul.hashicorp.com/connect-inject": "false" + {{- if .Values.connectInject.annotations }} + {{- tpl .Values.connectInject.annotations . | nindent 8 }} + {{- end }} + {{- if (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) }} + "vault.hashicorp.com/agent-init-first": "true" + "vault.hashicorp.com/agent-inject": "true" + {{- if .Values.global.secretsBackend.vault.connectInjectRole }} + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.connectInjectRole }} + {{ else }} + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.consulCARole }} + {{ end }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" . }} + {{- if .Values.global.secretsBackend.vault.connectInject.caCert.secretName }} + {{- with .Values.global.secretsBackend.vault.connectInject.caCert }} + "vault.hashicorp.com/agent-inject-secret-ca.crt": {{ .secretName }} + "vault.hashicorp.com/agent-inject-template-ca.crt": {{ template "consul.vaultCATemplate" . }} + "vault.hashicorp.com/secret-volume-path-ca.crt": "/vault/secrets/connect-injector/certs" + {{- end }} + {{- end }} + {{- if .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName }} + "vault.hashicorp.com/agent-inject-secret-tls.crt": {{ .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName }} + "vault.hashicorp.com/agent-inject-template-tls.crt": {{ include "consul.connectInjectWebhookTLSCertTemplate" . }} + "vault.hashicorp.com/secret-volume-path-tls.crt": "/vault/secrets/connect-injector/certs" + "vault.hashicorp.com/agent-inject-secret-tls.key": {{ .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName }} + "vault.hashicorp.com/agent-inject-template-tls.key": {{ include "consul.connectInjectWebhookTLSKeyTemplate" . }} + "vault.hashicorp.com/secret-volume-path-tls.key": "/vault/secrets/connect-injector/certs" + {{- end }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": "{{ .Values.global.secretsBackend.vault.ca.secretName }}" + "vault.hashicorp.com/ca-cert": "/vault/custom/{{ .Values.global.secretsBackend.vault.ca.secretKey }}" + {{- end }} + {{- if .Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl .Values.global.secretsBackend.vault.agentAnnotations . | nindent 8 | trim }} + {{- end }} + {{- end }} + spec: + serviceAccountName: {{ template "consul.fullname" . }}-connect-injector + containers: + - name: sidecar-injector + image: "{{ default .Values.global.imageK8S .Values.connectInject.image }}" + ports: + - containerPort: 8080 + name: webhook-server + protocol: TCP + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- if .Values.global.acls.manageSystemACLs }} + - name: CONSUL_HTTP_TOKEN_FILE + value: "/consul/login/acl-token" + {{- end }} + {{- if .Values.global.tls.enabled }} + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- end }} + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + {{- if (and .Values.connectInject.aclInjectToken.secretName .Values.connectInject.aclInjectToken.secretKey) }} + - name: CONSUL_HTTP_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Values.connectInject.aclInjectToken.secretName }} + key: {{ .Values.connectInject.aclInjectToken.secretKey }} + {{- end }} + - name: CONSUL_HTTP_ADDR + {{- if .Values.global.tls.enabled }} + value: https://$(HOST_IP):8501 + {{- else }} + value: http://$(HOST_IP):8500 + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + consul-k8s-control-plane inject-connect \ + -consul-api-timeout={{ .Values.global.consulAPITimeout }} \ + -log-level={{ default .Values.global.logLevel .Values.connectInject.logLevel }} \ + -log-json={{ .Values.global.logJSON }} \ + -default-inject={{ .Values.connectInject.default }} \ + -consul-image="{{ default .Values.global.image .Values.connectInject.imageConsul }}" \ + -envoy-image="{{ .Values.global.imageEnvoy }}" \ + -consul-k8s-image="{{ default .Values.global.imageK8S .Values.connectInject.image }}" \ + -release-name="{{ .Release.Name }}" \ + -release-namespace="{{ .Release.Namespace }}" \ + -resource-prefix={{ template "consul.fullname" . }} \ + -listen=:8080 \ + {{- if .Values.connectInject.transparentProxy.defaultEnabled }} + -default-enable-transparent-proxy=true \ + {{- else }} + -default-enable-transparent-proxy=false \ + {{- end }} + -enable-cni={{ .Values.connectInject.cni.enabled }} \ + {{- if .Values.global.peering.enabled }} + -enable-peering=true \ + {{- if (eq .Values.global.peering.tokenGeneration.serverAddresses.source "") }} + {{- if (and $serverEnabled $serverExposeServiceEnabled) }} + -read-server-expose-service=true \ + {{- else }} + {{- if .Values.externalServers.enabled }} + {{- $port := .Values.externalServers.grpcPort }} + {{- range $h := .Values.externalServers.hosts }} + -token-server-address="{{ $h }}:{{ $port }}" \ + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- if (eq .Values.global.peering.tokenGeneration.serverAddresses.source "static") }} + {{- range $addr := .Values.global.peering.tokenGeneration.serverAddresses.static }} + -token-server-address="{{ $addr }}" \ + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.global.openshift.enabled }} + -enable-openshift \ + {{- end }} + {{- if .Values.connectInject.transparentProxy.defaultOverwriteProbes }} + -transparent-proxy-default-overwrite-probes=true \ + {{- else }} + -transparent-proxy-default-overwrite-probes=false \ + {{- end }} + {{- if (and .Values.dns.enabled .Values.dns.enableRedirection) }} + -enable-consul-dns=true \ + {{- end }} + {{- if .Values.global.openshift.enabled }} + -enable-openshift \ + {{- end }} + {{- if (or (and (ne (.Values.connectInject.metrics.defaultEnabled | toString) "-") .Values.connectInject.metrics.defaultEnabled) (and (eq (.Values.connectInject.metrics.defaultEnabled | toString) "-") .Values.global.metrics.enabled)) }} + -default-enable-metrics=true \ + {{- else }} + -default-enable-metrics=false \ + {{- end }} + -default-enable-metrics-merging={{ .Values.connectInject.metrics.defaultEnableMerging }} \ + -default-merged-metrics-port={{ .Values.connectInject.metrics.defaultMergedMetricsPort }} \ + -default-prometheus-scrape-port={{ .Values.connectInject.metrics.defaultPrometheusScrapePort }} \ + -default-prometheus-scrape-path="{{ .Values.connectInject.metrics.defaultPrometheusScrapePath }}" \ + {{- if .Values.connectInject.envoyExtraArgs }} + -envoy-extra-args="{{ .Values.connectInject.envoyExtraArgs }}" \ + {{- end }} + {{- if .Values.connectInject.overrideAuthMethodName }} + -acl-auth-method="{{ .Values.connectInject.overrideAuthMethodName }}" \ + {{- else if .Values.global.acls.manageSystemACLs }} + -acl-auth-method="{{ template "consul.fullname" . }}-k8s-auth-method" \ + {{- end }} + {{- range $value := .Values.connectInject.k8sAllowNamespaces }} + -allow-k8s-namespace="{{ $value }}" \ + {{- end }} + {{- range $value := .Values.connectInject.k8sDenyNamespaces }} + -deny-k8s-namespace="{{ $value }}" \ + {{- end }} + {{- if .Values.global.adminPartitions.enabled }} + -enable-partitions=true \ + -partition={{ .Values.global.adminPartitions.name }} \ + {{- end }} + {{- if .Values.global.enableConsulNamespaces }} + -enable-namespaces=true \ + {{- if .Values.connectInject.consulNamespaces.consulDestinationNamespace }} + -consul-destination-namespace={{ .Values.connectInject.consulNamespaces.consulDestinationNamespace }} \ + {{- end }} + {{- if .Values.connectInject.consulNamespaces.mirroringK8S }} + -enable-k8s-namespace-mirroring=true \ + {{- if .Values.connectInject.consulNamespaces.mirroringK8SPrefix }} + -k8s-namespace-mirroring-prefix={{ .Values.connectInject.consulNamespaces.mirroringK8SPrefix }} \ + {{- end }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + -consul-cross-namespace-acl-policy=cross-namespace-policy \ + {{- end }} + {{- end }} + {{- if and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName }} + -tls-cert-dir=/vault/secrets/connect-injector/certs \ + -enable-webhook-ca-update \ + {{- else }} + -tls-cert-dir=/etc/connect-injector/certs \ + {{- end }} + {{- $resources := .Values.connectInject.sidecarProxy.resources }} + {{- /* kindIs is used here to differentiate between null and 0 */}} + {{- if not (kindIs "invalid" $resources.limits.memory) }} + -default-sidecar-proxy-memory-limit={{ $resources.limits.memory }} \ + {{- end }} + {{- if not (kindIs "invalid" $resources.requests.memory) }} + -default-sidecar-proxy-memory-request={{ $resources.requests.memory }} \ + {{- end }} + {{- if not (kindIs "invalid" $resources.limits.cpu) }} + -default-sidecar-proxy-cpu-limit={{ $resources.limits.cpu }} \ + {{- end }} + {{- if not (kindIs "invalid" $resources.requests.cpu) }} + -default-sidecar-proxy-cpu-request={{ $resources.requests.cpu }} \ + {{- end }} + -default-envoy-proxy-concurrency={{ .Values.connectInject.sidecarProxy.concurrency }} \ + + {{- if .Values.connectInject.initContainer }} + {{- $initResources := .Values.connectInject.initContainer.resources }} + {{- if not (kindIs "invalid" $initResources.limits.memory) }} + -init-container-memory-limit={{ $initResources.limits.memory }} \ + {{- end }} + {{- if not (kindIs "invalid" $initResources.requests.memory) }} + -init-container-memory-request={{ $initResources.requests.memory }} \ + {{- end }} + {{- if not (kindIs "invalid" $initResources.limits.cpu) }} + -init-container-cpu-limit={{ $initResources.limits.cpu }} \ + {{- end }} + {{- if not (kindIs "invalid" $initResources.requests.cpu) }} + -init-container-cpu-request={{ $initResources.requests.cpu }} \ + {{- end }} + {{- end }} + + {{- if .Values.global.consulSidecarContainer }} + {{- $consulSidecarResources := .Values.global.consulSidecarContainer.resources }} + {{- if not (kindIs "invalid" $consulSidecarResources.limits.memory) }} + -default-consul-sidecar-memory-limit={{ $consulSidecarResources.limits.memory }} \ + {{- end }} + {{- if not (kindIs "invalid" $consulSidecarResources.requests.memory) }} + -default-consul-sidecar-memory-request={{ $consulSidecarResources.requests.memory }} \ + {{- end }} + {{- if not (kindIs "invalid" $consulSidecarResources.limits.cpu) }} + -default-consul-sidecar-cpu-limit={{ $consulSidecarResources.limits.cpu }} \ + {{- end }} + {{- if not (kindIs "invalid" $consulSidecarResources.requests.cpu) }} + -default-consul-sidecar-cpu-request={{ $consulSidecarResources.requests.cpu }} \ + {{- end }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + lifecycle: + preStop: + exec: + command: + - "/bin/sh" + - "-ec" + - | + consul-k8s-control-plane consul-logout -consul-api-timeout={{ .Values.global.consulAPITimeout }} + {{- end }} + startupProbe: + httpGet: + path: /readyz/ready + port: 9445 + scheme: HTTP + failureThreshold: 15 + periodSeconds: 2 + timeoutSeconds: 5 + livenessProbe: + httpGet: + path: /readyz/ready + port: 9445 + scheme: HTTP + failureThreshold: 2 + initialDelaySeconds: 1 + successThreshold: 1 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: /readyz/ready + port: 9445 + scheme: HTTP + failureThreshold: 2 + initialDelaySeconds: 2 + successThreshold: 1 + timeoutSeconds: 5 + volumeMounts: + {{- if not (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName) }} + - name: certs + mountPath: /etc/connect-injector/certs + readOnly: true + {{- end }} + - mountPath: /consul/login + name: consul-data + readOnly: true + {{- if .Values.global.tls.enabled }} + {{- if .Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + {{- else }} + - name: consul-ca-cert + {{- end }} + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- with .Values.connectInject.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumes: + {{- if not (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName) }} + - name: certs + secret: + defaultMode: 420 + secretName: {{ template "consul.fullname" . }}-connect-inject-webhook-cert + {{- end }} + - name: consul-data + emptyDir: + medium: "Memory" + {{- if .Values.global.tls.enabled }} + {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + {{- if .Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + emptyDir: + medium: "Memory" + {{- end }} + {{- end }} + {{- if or (and .Values.global.acls.manageSystemACLs) (and .Values.global.tls.enabled .Values.global.tls.enableAutoEncrypt) }} + initContainers: + {{- if and .Values.global.tls.enabled .Values.global.tls.enableAutoEncrypt }} + {{- include "consul.getAutoEncryptClientCA" . | nindent 6 }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + - name: connect-injector-acl-init + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + {{- if .Values.global.tls.enabled }} + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- end }} + - name: CONSUL_HTTP_ADDR + {{- if .Values.global.tls.enabled }} + value: https://$(HOST_IP):8501 + {{- else }} + value: http://$(HOST_IP):8500 + {{- end }} + image: {{ .Values.global.imageK8S }} + volumeMounts: + - mountPath: /consul/login + name: consul-data + readOnly: false + {{- if .Values.global.tls.enabled }} + {{- if .Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + {{- else }} + - name: consul-ca-cert + {{- end }} + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + consul-k8s-control-plane acl-init \ + -component-name=connect-injector \ + {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter .Values.global.enableConsulNamespaces }} + -acl-auth-method={{ template "consul.fullname" . }}-k8s-component-auth-method-{{ .Values.global.datacenter }} \ + -primary-datacenter={{ .Values.global.federation.primaryDatacenter }} \ + {{- else }} + -acl-auth-method={{ template "consul.fullname" . }}-k8s-component-auth-method \ + {{- end }} + {{- if .Values.global.adminPartitions.enabled }} + -partition={{ .Values.global.adminPartitions.name }} \ + {{- end }} + -consul-api-timeout={{ .Values.global.consulAPITimeout }} \ + -log-level={{ default .Values.global.logLevel .Values.connectInject.logLevel }} \ + -log-json={{ .Values.global.logJSON }} + resources: + requests: + memory: "25Mi" + cpu: "50m" + limits: + memory: "25Mi" + cpu: "50m" + {{- end }} + {{- end }} + {{- if .Values.connectInject.priorityClassName }} + priorityClassName: {{ .Values.connectInject.priorityClassName | quote }} + {{- end }} + {{- if .Values.connectInject.nodeSelector }} + nodeSelector: + {{ tpl .Values.connectInject.nodeSelector . | indent 8 | trim }} + {{- end }} + {{- if .Values.connectInject.affinity }} + affinity: + {{ tpl .Values.connectInject.affinity . | indent 8 | trim }} + {{- end }} + {{- if .Values.connectInject.tolerations }} + tolerations: + {{ tpl .Values.connectInject.tolerations . | indent 8 | trim }} + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/connect-inject-leader-election-role.yaml b/charts/hashicorp/consul/0.49.0/templates/connect-inject-leader-election-role.yaml new file mode 100644 index 000000000..703aaffaa --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/connect-inject-leader-election-role.yaml @@ -0,0 +1,41 @@ +{{- if or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-connect-inject-leader-election + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: connect-injector +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/connect-inject-leader-election-rolebinding.yaml b/charts/hashicorp/consul/0.49.0/templates/connect-inject-leader-election-rolebinding.yaml new file mode 100644 index 000000000..9a27d3c86 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/connect-inject-leader-election-rolebinding.yaml @@ -0,0 +1,21 @@ +{{- if or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-connect-inject-leader-election + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: connect-injector +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-connect-inject-leader-election +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/connect-inject-mutatingwebhookconfiguration.yaml b/charts/hashicorp/consul/0.49.0/templates/connect-inject-mutatingwebhookconfiguration.yaml new file mode 100644 index 000000000..cd5bc8c29 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/connect-inject-mutatingwebhookconfiguration.yaml @@ -0,0 +1,85 @@ +{{- if (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled)) }} +# The MutatingWebhookConfiguration to enable the Connect injector. +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: connect-injector +webhooks: + - name: {{ template "consul.fullname" . }}-connect-injector.consul.hashicorp.com + # The webhook will fail scheduling all pods that are not part of consul if all replicas of the webhook are unhealthy. + objectSelector: + matchExpressions: + - key: app + operator: NotIn + values: [ {{ template "consul.name" . }} ] + failurePolicy: {{ .Values.connectInject.failurePolicy }} + sideEffects: None + admissionReviewVersions: + - "v1beta1" + - "v1" + clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: "/mutate" + rules: + - operations: [ "CREATE" ] + apiGroups: [""] + apiVersions: ["v1"] + resources: ["pods"] +{{- if .Values.connectInject.namespaceSelector }} + namespaceSelector: +{{ tpl .Values.connectInject.namespaceSelector . | indent 6 }} +{{- end }} +{{- if .Values.global.peering.enabled }} + - name: {{ template "consul.fullname" . }}-mutate-peeringacceptors.consul.hashicorp.com + clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: "/mutate-v1alpha1-peeringacceptors" + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - peeringacceptors + failurePolicy: Fail + sideEffects: None + admissionReviewVersions: + - "v1beta1" + - "v1" + - name: {{ template "consul.fullname" . }}-mutate-peeringdialers.consul.hashicorp.com + clientConfig: + service: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + path: "/mutate-v1alpha1-peeringdialers" + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - peeringdialers + failurePolicy: Fail + sideEffects: None + admissionReviewVersions: + - "v1beta1" + - "v1" +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/connect-inject-podsecuritypolicy.yaml b/charts/hashicorp/consul/0.49.0/templates/connect-inject-podsecuritypolicy.yaml new file mode 100644 index 000000000..0fafef7c4 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/connect-inject-podsecuritypolicy.yaml @@ -0,0 +1,40 @@ +{{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled))) }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: connect-injector +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/connect-inject-service.yaml b/charts/hashicorp/consul/0.49.0/templates/connect-inject-service.yaml new file mode 100644 index 000000000..b0284af74 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/connect-inject-service.yaml @@ -0,0 +1,23 @@ +{{- if (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled)) }} +# The service for the Connect sidecar injector +apiVersion: v1 +kind: Service +metadata: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: connect-injector +spec: + ports: + - port: 443 + targetPort: 8080 + selector: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + component: connect-injector +{{- end }} + diff --git a/charts/hashicorp/consul/0.49.0/templates/connect-inject-serviceaccount.yaml b/charts/hashicorp/consul/0.49.0/templates/connect-inject-serviceaccount.yaml new file mode 100644 index 000000000..ea2352c7a --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/connect-inject-serviceaccount.yaml @@ -0,0 +1,23 @@ +{{- if or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: connect-injector + {{- if .Values.connectInject.serviceAccount.annotations }} + annotations: + {{ tpl .Values.connectInject.serviceAccount.annotations . | nindent 4 | trim }} + {{- end }} +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} +- name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/connect-injector-disruptionbudget.yaml b/charts/hashicorp/consul/0.49.0/templates/connect-injector-disruptionbudget.yaml new file mode 100644 index 000000000..08f1401fb --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/connect-injector-disruptionbudget.yaml @@ -0,0 +1,26 @@ +{{- if (and .Values.connectInject.disruptionBudget.enabled (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled))) }} +# PodDisruptionBudget to prevent degrading the connectInject cluster through +# voluntary cluster changes. +{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" }} +apiVersion: policy/v1 +{{- else }} +apiVersion: policy/v1beta1 +{{- end }} +kind: PodDisruptionBudget +metadata: + name: {{ template "consul.fullname" . }}-connect-injector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: connect-injector +spec: + maxUnavailable: {{ template "consul.pdb.connectInject.maxUnavailable" . }} + selector: + matchLabels: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + component: connect-injector +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/controller-clusterrole.yaml b/charts/hashicorp/consul/0.49.0/templates/controller-clusterrole.yaml new file mode 100644 index 000000000..fc0753cc0 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/controller-clusterrole.yaml @@ -0,0 +1,79 @@ +{{- if .Values.controller.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "consul.fullname" . }}-controller + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: controller +rules: +- apiGroups: + - consul.hashicorp.com + resources: + - servicedefaults + - serviceresolvers + - proxydefaults + - meshes + - exportedservices + - servicerouters + - servicesplitters + - serviceintentions + - ingressgateways + - terminatinggateways + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - consul.hashicorp.com + resources: + - servicedefaults/status + - serviceresolvers/status + - proxydefaults/status + - meshes/status + - exportedservices/status + - servicerouters/status + - servicesplitters/status + - serviceintentions/status + - ingressgateways/status + - terminatinggateways/status + verbs: + - get + - patch + - update +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - list + - update +{{- if (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.controllerRole .Values.global.secretsBackend.vault.controller.tlsCert.secretName .Values.global.secretsBackend.vault.controller.caCert.secretName)}} +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + verbs: + - get + - list + - watch + - patch +{{- end }} +{{- if .Values.global.enablePodSecurityPolicies }} +- apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" . }}-controller + verbs: + - use +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/controller-clusterrolebinding.yaml b/charts/hashicorp/consul/0.49.0/templates/controller-clusterrolebinding.yaml new file mode 100644 index 000000000..71262fa47 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/controller-clusterrolebinding.yaml @@ -0,0 +1,20 @@ +{{- if .Values.controller.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "consul.fullname" . }}-controller + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: controller +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "consul.fullname" . }}-controller +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-controller + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/controller-deployment.yaml b/charts/hashicorp/consul/0.49.0/templates/controller-deployment.yaml new file mode 100644 index 000000000..44b13553b --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/controller-deployment.yaml @@ -0,0 +1,277 @@ +{{- if .Values.controller.enabled }} +{{- if and .Values.global.adminPartitions.enabled (not .Values.global.enableConsulNamespaces) }}{{ fail "global.enableConsulNamespaces must be true if global.adminPartitions.enabled=true" }}{{ end }} +{{ template "consul.validateVaultWebhookCertConfiguration" . }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "consul.fullname" . }}-controller + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: controller +spec: + replicas: {{ .Values.controller.replicas }} + selector: + matchLabels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: controller + template: + metadata: + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: controller + annotations: + "consul.hashicorp.com/connect-inject": "false" + {{- if (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) }} + "vault.hashicorp.com/agent-init-first": "true" + "vault.hashicorp.com/agent-inject": "true" + {{- if .Values.global.secretsBackend.vault.controllerRole }} + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.controllerRole }} + {{ else }} + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.consulCARole }} + {{ end }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" . }} + {{- if .Values.global.secretsBackend.vault.controller.caCert.secretName }} + {{- with .Values.global.secretsBackend.vault.controller.caCert }} + "vault.hashicorp.com/agent-inject-secret-ca.crt": {{ .secretName }} + "vault.hashicorp.com/agent-inject-template-ca.crt": {{ template "consul.vaultCATemplate" . }} + "vault.hashicorp.com/secret-volume-path-ca.crt": "/vault/secrets/controller-webhook/certs" + {{- end }} + {{- end }} + {{- if .Values.global.secretsBackend.vault.controller.tlsCert.secretName }} + "vault.hashicorp.com/agent-inject-secret-tls.crt": {{ .Values.global.secretsBackend.vault.controller.tlsCert.secretName }} + "vault.hashicorp.com/agent-inject-template-tls.crt": {{ include "consul.controllerWebhookTLSCertTemplate" . }} + "vault.hashicorp.com/secret-volume-path-tls.crt": "/vault/secrets/controller-webhook/certs" + "vault.hashicorp.com/agent-inject-secret-tls.key": {{ .Values.global.secretsBackend.vault.controller.tlsCert.secretName }} + "vault.hashicorp.com/agent-inject-template-tls.key": {{ include "consul.controllerWebhookTLSKeyTemplate" . }} + "vault.hashicorp.com/secret-volume-path-tls.key": "/vault/secrets/controller-webhook/certs" + {{- end }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": "{{ .Values.global.secretsBackend.vault.ca.secretName }}" + "vault.hashicorp.com/ca-cert": "/vault/custom/{{ .Values.global.secretsBackend.vault.ca.secretKey }}" + {{- end }} + {{- if .Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl .Values.global.secretsBackend.vault.agentAnnotations . | nindent 8 | trim }} + {{- end }} + {{- end }} + spec: + {{- if or .Values.global.acls.manageSystemACLs (and .Values.global.tls.enabled .Values.global.tls.enableAutoEncrypt) }} + initContainers: + {{- if and .Values.global.tls.enabled .Values.global.tls.enableAutoEncrypt }} + {{- include "consul.getAutoEncryptClientCA" . | nindent 6 }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + - name: controller-acl-init + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + {{- if .Values.global.tls.enabled }} + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- end }} + - name: CONSUL_HTTP_ADDR + {{- if .Values.global.tls.enabled }} + value: https://$(HOST_IP):8501 + {{- else }} + value: http://$(HOST_IP):8500 + {{- end }} + image: {{ .Values.global.imageK8S }} + volumeMounts: + - mountPath: /consul/login + name: consul-data + readOnly: false + {{- if .Values.global.tls.enabled }} + {{- if .Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + {{- else }} + - name: consul-ca-cert + {{- end }} + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + consul-k8s-control-plane acl-init \ + -component-name=controller \ + {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter }} + -acl-auth-method={{ template "consul.fullname" . }}-k8s-component-auth-method-{{ .Values.global.datacenter }} \ + -primary-datacenter={{ .Values.global.federation.primaryDatacenter }} \ + {{- else }} + -acl-auth-method={{ template "consul.fullname" . }}-k8s-component-auth-method \ + {{- end }} + {{- if .Values.global.adminPartitions.enabled }} + -partition={{ .Values.global.adminPartitions.name }} \ + {{- end }} + -consul-api-timeout={{ .Values.global.consulAPITimeout }} \ + -log-level={{ default .Values.global.logLevel .Values.controller.logLevel }} \ + -log-json={{ .Values.global.logJSON }} + resources: + requests: + memory: "25Mi" + cpu: "50m" + limits: + memory: "25Mi" + cpu: "50m" + {{- end }} + {{- end }} + containers: + - command: + - "/bin/sh" + - "-ec" + - | + consul-k8s-control-plane controller \ + -consul-api-timeout={{ .Values.global.consulAPITimeout }} \ + -log-level={{ default .Values.global.logLevel .Values.controller.logLevel }} \ + -log-json={{ .Values.global.logJSON }} \ + -resource-prefix={{ template "consul.fullname" . }} \ + {{- if and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.controller.tlsCert.secretName }} + -enable-webhook-ca-update \ + -webhook-tls-cert-dir=/vault/secrets/controller-webhook/certs \ + {{- else }} + -webhook-tls-cert-dir=/tmp/controller-webhook/certs \ + {{- end }} + -datacenter={{ .Values.global.datacenter }} \ + {{- if .Values.global.adminPartitions.enabled }} + -partition={{ .Values.global.adminPartitions.name }} \ + {{- end }} + -enable-leader-election \ + {{- if .Values.global.enableConsulNamespaces }} + -enable-namespaces=true \ + {{- if .Values.connectInject.consulNamespaces.consulDestinationNamespace }} + -consul-destination-namespace={{ .Values.connectInject.consulNamespaces.consulDestinationNamespace }} \ + {{- end }} + {{- if .Values.connectInject.consulNamespaces.mirroringK8S }} + -enable-k8s-namespace-mirroring=true \ + {{- if .Values.connectInject.consulNamespaces.mirroringK8SPrefix }} + -k8s-namespace-mirroring-prefix={{ .Values.connectInject.consulNamespaces.mirroringK8SPrefix }} \ + {{- end }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + -consul-cross-namespace-acl-policy=cross-namespace-policy \ + {{- end }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + lifecycle: + preStop: + exec: + command: + - "/bin/sh" + - "-ec" + - | + consul-k8s-control-plane consul-logout -consul-api-timeout={{ .Values.global.consulAPITimeout }} + {{- end }} + env: + {{- if .Values.global.acls.manageSystemACLs }} + - name: CONSUL_HTTP_TOKEN_FILE + value: "/consul/login/acl-token" + {{- end }} + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + {{- if (and .Values.controller.aclToken.secretName .Values.controller.aclToken.secretKey) }} + - name: CONSUL_HTTP_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Values.controller.aclToken.secretName }} + key: {{ .Values.controller.aclToken.secretKey }} + {{- end }} + {{- if .Values.global.tls.enabled }} + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- end }} + - name: CONSUL_HTTP_ADDR + {{- if .Values.global.tls.enabled }} + value: https://$(HOST_IP):8501 + {{- else }} + value: http://$(HOST_IP):8500 + {{- end }} + image: {{ .Values.global.imageK8S }} + name: controller + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + {{- with .Values.controller.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - mountPath: /consul/login + name: consul-data + readOnly: true + {{- if not (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.controller.tlsCert.secretName) }} + - mountPath: /tmp/controller-webhook/certs + name: cert + readOnly: true + {{- end }} + {{- if .Values.global.tls.enabled }} + {{- if .Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + {{- else }} + - name: consul-ca-cert + {{- end }} + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + terminationGracePeriodSeconds: 10 + volumes: + {{- if not (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.controller.tlsCert.secretName) }} + - name: cert + secret: + defaultMode: 420 + secretName: {{ template "consul.fullname" . }}-controller-webhook-cert + {{- end }} + {{- if .Values.global.tls.enabled }} + {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + {{- if .Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + emptyDir: + medium: "Memory" + {{- end }} + {{- end }} + - name: consul-data + emptyDir: + medium: "Memory" + serviceAccountName: {{ template "consul.fullname" . }}-controller + {{- if .Values.controller.nodeSelector }} + nodeSelector: + {{ tpl .Values.controller.nodeSelector . | indent 8 | trim }} + {{- end }} + {{- if .Values.controller.affinity }} + affinity: + {{ tpl .Values.controller.affinity . | indent 8 | trim }} + {{- end }} + {{- if .Values.controller.tolerations }} + tolerations: + {{ tpl .Values.controller.tolerations . | indent 8 | trim }} + {{- end }} + {{- if .Values.controller.priorityClassName }} + priorityClassName: {{ .Values.controller.priorityClassName | quote }} + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/controller-leader-election-role.yaml b/charts/hashicorp/consul/0.49.0/templates/controller-leader-election-role.yaml new file mode 100644 index 000000000..177a96895 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/controller-leader-election-role.yaml @@ -0,0 +1,41 @@ +{{- if .Values.controller.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-controller-leader-election + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: controller +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/controller-leader-election-rolebinding.yaml b/charts/hashicorp/consul/0.49.0/templates/controller-leader-election-rolebinding.yaml new file mode 100644 index 000000000..28fa84f8e --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/controller-leader-election-rolebinding.yaml @@ -0,0 +1,20 @@ +{{- if .Values.controller.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-controller-leader-election + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: controller +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-controller-leader-election +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-controller +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/controller-mutatingwebhookconfiguration.yaml b/charts/hashicorp/consul/0.49.0/templates/controller-mutatingwebhookconfiguration.yaml new file mode 100644 index 000000000..bf31ea862 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/controller-mutatingwebhookconfiguration.yaml @@ -0,0 +1,224 @@ +{{- if .Values.controller.enabled }} +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: {{ template "consul.fullname" . }}-controller + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: controller +webhooks: +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-controller-webhook + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-proxydefaults + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-proxydefaults.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - proxydefaults + sideEffects: None +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-controller-webhook + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-mesh + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-mesh.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - meshes + sideEffects: None +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-controller-webhook + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-servicedefaults + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-servicedefaults.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - servicedefaults + sideEffects: None +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-controller-webhook + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-serviceresolver + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-serviceresolver.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - serviceresolvers + sideEffects: None +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-controller-webhook + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-servicerouter + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-servicerouter.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - servicerouters + sideEffects: None +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-controller-webhook + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-servicesplitter + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-servicesplitter.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - servicesplitters + sideEffects: None +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-controller-webhook + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-serviceintentions + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-serviceintentions.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - serviceintentions + sideEffects: None +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-controller-webhook + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-ingressgateway + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-ingressgateway.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - ingressgateways + sideEffects: None +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-controller-webhook + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-terminatinggateway + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-terminatinggateway.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - terminatinggateways + sideEffects: None +- clientConfig: + service: + name: {{ template "consul.fullname" . }}-controller-webhook + namespace: {{ .Release.Namespace }} + path: /mutate-v1alpha1-exportedservices + failurePolicy: Fail + admissionReviewVersions: + - "v1beta1" + - "v1" + name: mutate-exportedservices.consul.hashicorp.com + rules: + - apiGroups: + - consul.hashicorp.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - exportedservices + sideEffects: None +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/controller-podsecuritypolicy.yaml b/charts/hashicorp/consul/0.49.0/templates/controller-podsecuritypolicy.yaml new file mode 100644 index 000000000..e774faaa9 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/controller-podsecuritypolicy.yaml @@ -0,0 +1,40 @@ +{{- if and .Values.controller.enabled .Values.global.enablePodSecurityPolicies }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-controller + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: controller +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/controller-serviceaccount.yaml b/charts/hashicorp/consul/0.49.0/templates/controller-serviceaccount.yaml new file mode 100644 index 000000000..c590efe30 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/controller-serviceaccount.yaml @@ -0,0 +1,23 @@ +{{- if .Values.controller.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-controller + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: controller + {{- if .Values.controller.serviceAccount.annotations }} + annotations: + {{ tpl .Values.controller.serviceAccount.annotations . | nindent 4 | trim }} + {{- end }} + {{- with .Values.global.imagePullSecrets }} +imagePullSecrets: + {{- range . }} +- name: {{ .name }} + {{- end }} + {{- end }} + {{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/controller-webhook-service.yaml b/charts/hashicorp/consul/0.49.0/templates/controller-webhook-service.yaml new file mode 100644 index 000000000..c6463f880 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/controller-webhook-service.yaml @@ -0,0 +1,23 @@ +{{- if .Values.controller.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "consul.fullname" . }}-controller-webhook + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: controller +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: controller +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/crd-exportedservices.yaml b/charts/hashicorp/consul/0.49.0/templates/crd-exportedservices.yaml new file mode 100644 index 000000000..27d03dbb0 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/crd-exportedservices.yaml @@ -0,0 +1,143 @@ +{{- if .Values.controller.enabled }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: exportedservices.consul.hashicorp.com + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd +spec: + group: consul.hashicorp.com + names: + kind: ExportedServices + listKind: ExportedServicesList + plural: exportedservices + shortNames: + - exported-services + singular: exportedservices + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ExportedServices is the Schema for the exportedservices API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ExportedServicesSpec defines the desired state of ExportedServices. + properties: + services: + description: Services is a list of services to be exported and the + list of partitions to expose them to. + items: + description: ExportedService manages the exporting of a service + in the local partition to other partitions. + properties: + consumers: + description: Consumers is a list of downstream consumers of + the service to be exported. + items: + description: ServiceConsumer represents a downstream consumer + of the service to be exported. + properties: + partition: + description: Partition is the admin partition to export + the service to. + type: string + peer: + description: '[Experimental] Peer is the name of the peer + to export the service to.' + type: string + type: object + type: array + name: + description: Name is the name of the service to be exported. + type: string + namespace: + description: Namespace is the namespace to export the service + from. + type: string + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/crd-ingressgateways.yaml b/charts/hashicorp/consul/0.49.0/templates/crd-ingressgateways.yaml new file mode 100644 index 000000000..f14789e83 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/crd-ingressgateways.yaml @@ -0,0 +1,336 @@ +{{- if .Values.controller.enabled }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: ingressgateways.consul.hashicorp.com + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd +spec: + group: consul.hashicorp.com + names: + kind: IngressGateway + listKind: IngressGatewayList + plural: ingressgateways + shortNames: + - ingress-gateway + singular: ingressgateway + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: IngressGateway is the Schema for the ingressgateways API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: IngressGatewaySpec defines the desired state of IngressGateway. + properties: + listeners: + description: Listeners declares what ports the ingress gateway should + listen on, and what services to associated to those ports. + items: + description: IngressListener manages the configuration for a listener + on a specific port. + properties: + port: + description: Port declares the port on which the ingress gateway + should listen for traffic. + type: integer + protocol: + description: 'Protocol declares what type of traffic this listener + is expected to receive. Depending on the protocol, a listener + might support multiplexing services over a single port, or + additional discovery chain features. The current supported + values are: (tcp | http | http2 | grpc).' + type: string + services: + description: Services declares the set of services to which + the listener forwards traffic. For "tcp" protocol listeners, + only a single service is allowed. For "http" listeners, multiple + services can be declared. + items: + description: IngressService manages configuration for services + that are exposed to ingress traffic. + properties: + hosts: + description: "Hosts is a list of hostnames which should + be associated to this service on the defined listener. + Only allowed on layer 7 protocols, this will be used + to route traffic to the service by matching the Host + header of the HTTP request. \n If a host is provided + for a service that also has a wildcard specifier defined, + the host will override the wildcard-specifier-provided + \".*\" domain for that listener. \n This + cannot be specified when using the wildcard specifier, + \"*\", or when using a \"tcp\" listener." + items: + type: string + type: array + name: + description: "Name declares the service to which traffic + should be forwarded. \n This can either be a specific + service, or the wildcard specifier, \"*\". If the wildcard + specifier is provided, the listener must be of \"http\" + protocol and means that the listener will forward traffic + to all services. \n A name can be specified on multiple + listeners, and will be exposed on both of the listeners." + type: string + namespace: + description: Namespace is the namespace where the service + is located. Namespacing is a Consul Enterprise feature. + type: string + partition: + description: Partition is the admin-partition where the + service is located. Partitioning is a Consul Enterprise + feature. + type: string + requestHeaders: + description: Allow HTTP header manipulation to be configured. + properties: + add: + additionalProperties: + type: string + description: Add is a set of name -> value pairs that + should be appended to the request or response (i.e. + allowing duplicates if the same header already exists). + type: object + remove: + description: Remove is the set of header names that + should be stripped from the request or response. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: Set is a set of name -> value pairs that + should be added to the request or response, overwriting + any existing header values of the same name. + type: object + type: object + responseHeaders: + description: HTTPHeaderModifiers is a set of rules for + HTTP header modification that should be performed by + proxies as the request passes through them. It can operate + on either request or response headers depending on the + context in which it is used. + properties: + add: + additionalProperties: + type: string + description: Add is a set of name -> value pairs that + should be appended to the request or response (i.e. + allowing duplicates if the same header already exists). + type: object + remove: + description: Remove is the set of header names that + should be stripped from the request or response. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: Set is a set of name -> value pairs that + should be added to the request or response, overwriting + any existing header values of the same name. + type: object + type: object + tls: + description: TLS allows specifying some TLS configuration + per listener. + properties: + sds: + description: SDS allows configuring TLS certificate + from an SDS service. + properties: + certResource: + description: CertResource is the SDS resource + name to request when fetching the certificate + from the SDS service. + type: string + clusterName: + description: ClusterName is the SDS cluster name + to connect to, to retrieve certificates. This + cluster must be specified in the Gateway's bootstrap + configuration. + type: string + type: object + type: object + type: object + type: array + tls: + description: TLS config for this listener. + properties: + cipherSuites: + description: Define a subset of cipher suites to restrict + Only applicable to connections negotiated via TLS 1.2 + or earlier. + items: + type: string + type: array + enabled: + description: Indicates that TLS should be enabled for this + gateway service. + type: boolean + sds: + description: SDS allows configuring TLS certificate from + an SDS service. + properties: + certResource: + description: CertResource is the SDS resource name to + request when fetching the certificate from the SDS + service. + type: string + clusterName: + description: ClusterName is the SDS cluster name to + connect to, to retrieve certificates. This cluster + must be specified in the Gateway's bootstrap configuration. + type: string + type: object + tlsMaxVersion: + description: TLSMaxVersion sets the default maximum TLS + version supported. Must be greater than or equal to `TLSMinVersion`. + One of `TLS_AUTO`, `TLSv1_0`, `TLSv1_1`, `TLSv1_2`, or + `TLSv1_3`. If unspecified, Envoy will default to TLS 1.3 + as a max version for incoming connections. + type: string + tlsMinVersion: + description: TLSMinVersion sets the default minimum TLS + version supported. One of `TLS_AUTO`, `TLSv1_0`, `TLSv1_1`, + `TLSv1_2`, or `TLSv1_3`. If unspecified, Envoy v1.22.0 + and newer will default to TLS 1.2 as a min version, while + older releases of Envoy default to TLS 1.0. + type: string + required: + - enabled + type: object + type: object + type: array + tls: + description: TLS holds the TLS configuration for this gateway. + properties: + cipherSuites: + description: Define a subset of cipher suites to restrict Only + applicable to connections negotiated via TLS 1.2 or earlier. + items: + type: string + type: array + enabled: + description: Indicates that TLS should be enabled for this gateway + service. + type: boolean + sds: + description: SDS allows configuring TLS certificate from an SDS + service. + properties: + certResource: + description: CertResource is the SDS resource name to request + when fetching the certificate from the SDS service. + type: string + clusterName: + description: ClusterName is the SDS cluster name to connect + to, to retrieve certificates. This cluster must be specified + in the Gateway's bootstrap configuration. + type: string + type: object + tlsMaxVersion: + description: TLSMaxVersion sets the default maximum TLS version + supported. Must be greater than or equal to `TLSMinVersion`. + One of `TLS_AUTO`, `TLSv1_0`, `TLSv1_1`, `TLSv1_2`, or `TLSv1_3`. + If unspecified, Envoy will default to TLS 1.3 as a max version + for incoming connections. + type: string + tlsMinVersion: + description: TLSMinVersion sets the default minimum TLS version + supported. One of `TLS_AUTO`, `TLSv1_0`, `TLSv1_1`, `TLSv1_2`, + or `TLSv1_3`. If unspecified, Envoy v1.22.0 and newer will default + to TLS 1.2 as a min version, while older releases of Envoy default + to TLS 1.0. + type: string + required: + - enabled + type: object + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/crd-meshes.yaml b/charts/hashicorp/consul/0.49.0/templates/crd-meshes.yaml new file mode 100644 index 000000000..b795e48d3 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/crd-meshes.yaml @@ -0,0 +1,198 @@ +{{- if .Values.controller.enabled }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: meshes.consul.hashicorp.com + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd +spec: + group: consul.hashicorp.com + names: + kind: Mesh + listKind: MeshList + plural: meshes + singular: mesh + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: Mesh is the Schema for the mesh API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: MeshSpec defines the desired state of Mesh. + properties: + http: + description: HTTP defines the HTTP configuration for the service mesh. + properties: + sanitizeXForwardedClientCert: + type: boolean + required: + - sanitizeXForwardedClientCert + type: object + tls: + description: TLS defines the TLS configuration for the service mesh. + properties: + incoming: + description: Incoming defines the TLS configuration for inbound + mTLS connections targeting the public listener on Connect and + TerminatingGateway proxy kinds. + properties: + cipherSuites: + description: CipherSuites sets the default list of TLS cipher + suites to support when negotiating connections using TLS + 1.2 or earlier. If unspecified, Envoy will use a default + server cipher list. The list of supported cipher suites + can be seen in https://github.com/hashicorp/consul/blob/v1.11.2/types/tls.go#L154-L169 + and is dependent on underlying support in Envoy. Future + releases of Envoy may remove currently-supported but insecure + cipher suites, and future releases of Consul may add new + supported cipher suites if any are added to Envoy. + items: + type: string + type: array + tlsMaxVersion: + description: TLSMaxVersion sets the default maximum TLS version + supported. Must be greater than or equal to `TLSMinVersion`. + One of `TLS_AUTO`, `TLSv1_0`, `TLSv1_1`, `TLSv1_2`, or `TLSv1_3`. + If unspecified, Envoy will default to TLS 1.3 as a max version + for incoming connections. + type: string + tlsMinVersion: + description: TLSMinVersion sets the default minimum TLS version + supported. One of `TLS_AUTO`, `TLSv1_0`, `TLSv1_1`, `TLSv1_2`, + or `TLSv1_3`. If unspecified, Envoy v1.22.0 and newer will + default to TLS 1.2 as a min version, while older releases + of Envoy default to TLS 1.0. + type: string + type: object + outgoing: + description: Outgoing defines the TLS configuration for outbound + mTLS connections dialing upstreams from Connect and IngressGateway + proxy kinds. + properties: + cipherSuites: + description: CipherSuites sets the default list of TLS cipher + suites to support when negotiating connections using TLS + 1.2 or earlier. If unspecified, Envoy will use a default + server cipher list. The list of supported cipher suites + can be seen in https://github.com/hashicorp/consul/blob/v1.11.2/types/tls.go#L154-L169 + and is dependent on underlying support in Envoy. Future + releases of Envoy may remove currently-supported but insecure + cipher suites, and future releases of Consul may add new + supported cipher suites if any are added to Envoy. + items: + type: string + type: array + tlsMaxVersion: + description: TLSMaxVersion sets the default maximum TLS version + supported. Must be greater than or equal to `TLSMinVersion`. + One of `TLS_AUTO`, `TLSv1_0`, `TLSv1_1`, `TLSv1_2`, or `TLSv1_3`. + If unspecified, Envoy will default to TLS 1.3 as a max version + for incoming connections. + type: string + tlsMinVersion: + description: TLSMinVersion sets the default minimum TLS version + supported. One of `TLS_AUTO`, `TLSv1_0`, `TLSv1_1`, `TLSv1_2`, + or `TLSv1_3`. If unspecified, Envoy v1.22.0 and newer will + default to TLS 1.2 as a min version, while older releases + of Envoy default to TLS 1.0. + type: string + type: object + type: object + transparentProxy: + description: TransparentProxy controls the configuration specific + to proxies in "transparent" mode. Added in v1.10.0. + properties: + meshDestinationsOnly: + description: MeshDestinationsOnly determines whether sidecar proxies + operating in "transparent" mode can proxy traffic to IP addresses + not registered in Consul's catalog. If enabled, traffic will + only be proxied to upstreams with service registrations in the + catalog. + type: boolean + type: object + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/crd-peeringacceptors.yaml b/charts/hashicorp/consul/0.49.0/templates/crd-peeringacceptors.yaml new file mode 100644 index 000000000..e06e830f0 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/crd-peeringacceptors.yaml @@ -0,0 +1,154 @@ +{{- if and .Values.connectInject.enabled .Values.global.peering.enabled }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: peeringacceptors.consul.hashicorp.com + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd +spec: + group: consul.hashicorp.com + names: + kind: PeeringAcceptor + listKind: PeeringAcceptorList + plural: peeringacceptors + shortNames: + - peering-acceptor + singular: peeringacceptor + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: PeeringAcceptor is the Schema for the peeringacceptors API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: PeeringAcceptorSpec defines the desired state of PeeringAcceptor. + properties: + peer: + description: Peer describes the information needed to create a peering. + properties: + secret: + description: Secret describes how to store the generated peering + token. + properties: + backend: + description: 'Backend is where the generated secret is stored. + Currently supports the value: "kubernetes".' + type: string + key: + description: Key is the key of the secret generated. + type: string + name: + description: Name is the name of the secret generated. + type: string + type: object + type: object + required: + - peer + type: object + status: + description: PeeringAcceptorStatus defines the observed state of PeeringAcceptor. + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + latestPeeringVersion: + description: LatestPeeringVersion is the latest version of the resource + that was reconciled. + format: int64 + type: integer + secret: + description: SecretRef shows the status of the secret. + properties: + backend: + description: 'Backend is where the generated secret is stored. + Currently supports the value: "kubernetes".' + type: string + key: + description: Key is the key of the secret generated. + type: string + name: + description: Name is the name of the secret generated. + type: string + resourceVersion: + description: ResourceVersion is the resource version for the secret. + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/crd-peeringdialers.yaml b/charts/hashicorp/consul/0.49.0/templates/crd-peeringdialers.yaml new file mode 100644 index 000000000..e24401e76 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/crd-peeringdialers.yaml @@ -0,0 +1,154 @@ +{{- if and .Values.connectInject.enabled .Values.global.peering.enabled }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: peeringdialers.consul.hashicorp.com + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd +spec: + group: consul.hashicorp.com + names: + kind: PeeringDialer + listKind: PeeringDialerList + plural: peeringdialers + shortNames: + - peering-dialer + singular: peeringdialer + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: PeeringDialer is the Schema for the peeringdialers API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: PeeringDialerSpec defines the desired state of PeeringDialer. + properties: + peer: + description: Peer describes the information needed to create a peering. + properties: + secret: + description: Secret describes how to store the generated peering + token. + properties: + backend: + description: 'Backend is where the generated secret is stored. + Currently supports the value: "kubernetes".' + type: string + key: + description: Key is the key of the secret generated. + type: string + name: + description: Name is the name of the secret generated. + type: string + type: object + type: object + required: + - peer + type: object + status: + description: PeeringDialerStatus defines the observed state of PeeringDialer. + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + latestPeeringVersion: + description: LatestPeeringVersion is the latest version of the resource + that was reconciled. + format: int64 + type: integer + secret: + description: SecretRef shows the status of the secret. + properties: + backend: + description: 'Backend is where the generated secret is stored. + Currently supports the value: "kubernetes".' + type: string + key: + description: Key is the key of the secret generated. + type: string + name: + description: Name is the name of the secret generated. + type: string + resourceVersion: + description: ResourceVersion is the resource version for the secret. + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/crd-proxydefaults.yaml b/charts/hashicorp/consul/0.49.0/templates/crd-proxydefaults.yaml new file mode 100644 index 000000000..947bdc885 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/crd-proxydefaults.yaml @@ -0,0 +1,186 @@ +{{- if .Values.controller.enabled }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: proxydefaults.consul.hashicorp.com + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd +spec: + group: consul.hashicorp.com + names: + kind: ProxyDefaults + listKind: ProxyDefaultsList + plural: proxydefaults + shortNames: + - proxy-defaults + singular: proxydefaults + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ProxyDefaults is the Schema for the proxydefaults API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ProxyDefaultsSpec defines the desired state of ProxyDefaults. + properties: + config: + description: Config is an arbitrary map of configuration values used + by Connect proxies. Any values that your proxy allows can be configured + globally here. Supports JSON config values. See https://www.consul.io/docs/connect/proxies/envoy#configuration-formatting + type: object + x-kubernetes-preserve-unknown-fields: true + expose: + description: Expose controls the default expose path configuration + for Envoy. + properties: + checks: + description: Checks defines whether paths associated with Consul + checks will be exposed. This flag triggers exposing all HTTP + and GRPC check paths registered for the service. + type: boolean + paths: + description: Paths is the list of paths exposed through the proxy. + items: + properties: + listenerPort: + description: ListenerPort defines the port of the proxy's + listener for exposed paths. + type: integer + localPathPort: + description: LocalPathPort is the port that the service + is listening on for the given path. + type: integer + path: + description: Path is the path to expose through the proxy, + ie. "/metrics". + type: string + protocol: + description: Protocol describes the upstream's service protocol. + Valid values are "http" and "http2", defaults to "http". + type: string + type: object + type: array + type: object + meshGateway: + description: MeshGateway controls the default mesh gateway configuration + for this service. + properties: + mode: + description: Mode is the mode that should be used for the upstream + connection. One of none, local, or remote. + type: string + type: object + mode: + description: 'Mode can be one of "direct" or "transparent". "transparent" + represents that inbound and outbound application traffic is being + captured and redirected through the proxy. This mode does not enable + the traffic redirection itself. Instead it signals Consul to configure + Envoy as if traffic is already being redirected. "direct" represents + that the proxy''s listeners must be dialed directly by the local + application and other proxies. Note: This cannot be set using the + CRD and should be set using annotations on the services that are + part of the mesh.' + type: string + transparentProxy: + description: 'TransparentProxy controls configuration specific to + proxies in transparent mode. Note: This cannot be set using the + CRD and should be set using annotations on the services that are + part of the mesh.' + properties: + dialedDirectly: + description: DialedDirectly indicates whether transparent proxies + can dial this proxy instance directly. The discovery chain is + not considered when dialing a service instance directly. This + setting is useful when addressing stateful services, such as + a database cluster with a leader node. + type: boolean + outboundListenerPort: + description: OutboundListenerPort is the port of the listener + where outbound application traffic is being redirected to. + type: integer + type: object + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/crd-servicedefaults.yaml b/charts/hashicorp/consul/0.49.0/templates/crd-servicedefaults.yaml new file mode 100644 index 000000000..636e8ce51 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/crd-servicedefaults.yaml @@ -0,0 +1,422 @@ +{{- if .Values.controller.enabled }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: servicedefaults.consul.hashicorp.com + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd +spec: + group: consul.hashicorp.com + names: + kind: ServiceDefaults + listKind: ServiceDefaultsList + plural: servicedefaults + shortNames: + - service-defaults + singular: servicedefaults + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ServiceDefaults is the Schema for the servicedefaults API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ServiceDefaultsSpec defines the desired state of ServiceDefaults. + properties: + destination: + description: Destination is an address(es)/port combination that represents + an endpoint outside the mesh. This is only valid when the mesh is + configured in "transparent" mode. Destinations live outside of Consul's + catalog, and because of this, they do not require an artificial + node to be created. + properties: + addresses: + description: Addresses is a list of IPs and/or hostnames that + can be dialed and routed through a terminating gateway. + items: + type: string + type: array + port: + description: Port is the port that can be dialed on any of the + addresses in this Destination. + format: int32 + type: integer + type: object + expose: + description: Expose controls the default expose path configuration + for Envoy. + properties: + checks: + description: Checks defines whether paths associated with Consul + checks will be exposed. This flag triggers exposing all HTTP + and GRPC check paths registered for the service. + type: boolean + paths: + description: Paths is the list of paths exposed through the proxy. + items: + properties: + listenerPort: + description: ListenerPort defines the port of the proxy's + listener for exposed paths. + type: integer + localPathPort: + description: LocalPathPort is the port that the service + is listening on for the given path. + type: integer + path: + description: Path is the path to expose through the proxy, + ie. "/metrics". + type: string + protocol: + description: Protocol describes the upstream's service protocol. + Valid values are "http" and "http2", defaults to "http". + type: string + type: object + type: array + type: object + externalSNI: + description: ExternalSNI is an optional setting that allows for the + TLS SNI value to be changed to a non-connect value when federating + with an external system. + type: string + maxInboundConnections: + description: MaxInboundConnections is the maximum number of concurrent + inbound connections to each service instance. Defaults to 0 (using + consul's default) if not set. + type: integer + meshGateway: + description: MeshGateway controls the default mesh gateway configuration + for this service. + properties: + mode: + description: Mode is the mode that should be used for the upstream + connection. One of none, local, or remote. + type: string + type: object + mode: + description: 'Mode can be one of "direct" or "transparent". "transparent" + represents that inbound and outbound application traffic is being + captured and redirected through the proxy. This mode does not enable + the traffic redirection itself. Instead it signals Consul to configure + Envoy as if traffic is already being redirected. "direct" represents + that the proxy''s listeners must be dialed directly by the local + application and other proxies. Note: This cannot be set using the + CRD and should be set using annotations on the services that are + part of the mesh.' + type: string + protocol: + description: Protocol sets the protocol of the service. This is used + by Connect proxies for things like observability features and to + unlock usage of the service-splitter and service-router config entries + for a service. + type: string + transparentProxy: + description: 'TransparentProxy controls configuration specific to + proxies in transparent mode. Note: This cannot be set using the + CRD and should be set using annotations on the services that are + part of the mesh.' + properties: + dialedDirectly: + description: DialedDirectly indicates whether transparent proxies + can dial this proxy instance directly. The discovery chain is + not considered when dialing a service instance directly. This + setting is useful when addressing stateful services, such as + a database cluster with a leader node. + type: boolean + outboundListenerPort: + description: OutboundListenerPort is the port of the listener + where outbound application traffic is being redirected to. + type: integer + type: object + upstreamConfig: + description: UpstreamConfig controls default configuration settings + that apply across all upstreams, and per-upstream configuration + overrides. Note that per-upstream configuration applies across all + federated datacenters to the pairing of source and upstream destination + services. + properties: + defaults: + description: Defaults contains default configuration for all upstreams + of a given service. The name field must be empty. + properties: + connectTimeoutMs: + description: ConnectTimeoutMs is the number of milliseconds + to timeout making a new connection to this upstream. Defaults + to 5000 (5 seconds) if not set. + type: integer + envoyClusterJSON: + description: 'EnvoyClusterJSON is a complete override ("escape + hatch") for the upstream''s cluster. The Connect client + TLS certificate and context will be injected overriding + any TLS settings present. Note: This escape hatch is NOT + compatible with the discovery chain and will be ignored + if a discovery chain is active.' + type: string + envoyListenerJSON: + description: 'EnvoyListenerJSON is a complete override ("escape + hatch") for the upstream''s listener. Note: This escape + hatch is NOT compatible with the discovery chain and will + be ignored if a discovery chain is active.' + type: string + limits: + description: Limits are the set of limits that are applied + to the proxy for a specific upstream of a service instance. + properties: + maxConcurrentRequests: + description: MaxConcurrentRequests is the maximum number + of in-flight requests that will be allowed to the upstream + cluster at a point in time. This is mostly applicable + to HTTP/2 clusters since all HTTP/1.1 requests are limited + by MaxConnections. + type: integer + maxConnections: + description: MaxConnections is the maximum number of connections + the local proxy can make to the upstream service. + type: integer + maxPendingRequests: + description: MaxPendingRequests is the maximum number + of requests that will be queued waiting for an available + connection. This is mostly applicable to HTTP/1.1 clusters + since all HTTP/2 requests are streamed over a single + connection. + type: integer + type: object + meshGateway: + description: MeshGatewayConfig controls how Mesh Gateways + are configured and used. + properties: + mode: + description: Mode is the mode that should be used for + the upstream connection. One of none, local, or remote. + type: string + type: object + name: + description: Name is only accepted within a service-defaults + config entry. + type: string + namespace: + description: Namespace is only accepted within a service-defaults + config entry. + type: string + partition: + description: Partition is only accepted within a service-defaults + config entry. + type: string + passiveHealthCheck: + description: PassiveHealthCheck configuration determines how + upstream proxy instances will be monitored for removal from + the load balancing pool. + properties: + enforcing_consecutive_5xx: + description: EnforcingConsecutive5xx is the % chance that + a host will be actually ejected when an outlier status + is detected through consecutive 5xx. This setting can + be used to disable ejection or to ramp it up slowly. + format: int32 + type: integer + interval: + description: Interval between health check analysis sweeps. + Each sweep may remove hosts or return hosts to the pool. + type: string + maxFailures: + description: MaxFailures is the count of consecutive failures + that results in a host being removed from the pool. + format: int32 + type: integer + type: object + protocol: + description: Protocol describes the upstream's service protocol. + Valid values are "tcp", "http" and "grpc". Anything else + is treated as tcp. This enables protocol aware features + like per-request metrics and connection pooling, tracing, + routing etc. + type: string + type: object + overrides: + description: Overrides is a slice of per-service configuration. + The name field is required. + items: + properties: + connectTimeoutMs: + description: ConnectTimeoutMs is the number of milliseconds + to timeout making a new connection to this upstream. Defaults + to 5000 (5 seconds) if not set. + type: integer + envoyClusterJSON: + description: 'EnvoyClusterJSON is a complete override ("escape + hatch") for the upstream''s cluster. The Connect client + TLS certificate and context will be injected overriding + any TLS settings present. Note: This escape hatch is NOT + compatible with the discovery chain and will be ignored + if a discovery chain is active.' + type: string + envoyListenerJSON: + description: 'EnvoyListenerJSON is a complete override ("escape + hatch") for the upstream''s listener. Note: This escape + hatch is NOT compatible with the discovery chain and will + be ignored if a discovery chain is active.' + type: string + limits: + description: Limits are the set of limits that are applied + to the proxy for a specific upstream of a service instance. + properties: + maxConcurrentRequests: + description: MaxConcurrentRequests is the maximum number + of in-flight requests that will be allowed to the + upstream cluster at a point in time. This is mostly + applicable to HTTP/2 clusters since all HTTP/1.1 requests + are limited by MaxConnections. + type: integer + maxConnections: + description: MaxConnections is the maximum number of + connections the local proxy can make to the upstream + service. + type: integer + maxPendingRequests: + description: MaxPendingRequests is the maximum number + of requests that will be queued waiting for an available + connection. This is mostly applicable to HTTP/1.1 + clusters since all HTTP/2 requests are streamed over + a single connection. + type: integer + type: object + meshGateway: + description: MeshGatewayConfig controls how Mesh Gateways + are configured and used. + properties: + mode: + description: Mode is the mode that should be used for + the upstream connection. One of none, local, or remote. + type: string + type: object + name: + description: Name is only accepted within a service-defaults + config entry. + type: string + namespace: + description: Namespace is only accepted within a service-defaults + config entry. + type: string + partition: + description: Partition is only accepted within a service-defaults + config entry. + type: string + passiveHealthCheck: + description: PassiveHealthCheck configuration determines + how upstream proxy instances will be monitored for removal + from the load balancing pool. + properties: + enforcing_consecutive_5xx: + description: EnforcingConsecutive5xx is the % chance + that a host will be actually ejected when an outlier + status is detected through consecutive 5xx. This setting + can be used to disable ejection or to ramp it up slowly. + format: int32 + type: integer + interval: + description: Interval between health check analysis + sweeps. Each sweep may remove hosts or return hosts + to the pool. + type: string + maxFailures: + description: MaxFailures is the count of consecutive + failures that results in a host being removed from + the pool. + format: int32 + type: integer + type: object + protocol: + description: Protocol describes the upstream's service protocol. + Valid values are "tcp", "http" and "grpc". Anything else + is treated as tcp. This enables protocol aware features + like per-request metrics and connection pooling, tracing, + routing etc. + type: string + type: object + type: array + type: object + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/crd-serviceintentions.yaml b/charts/hashicorp/consul/0.49.0/templates/crd-serviceintentions.yaml new file mode 100644 index 000000000..827fcfe2a --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/crd-serviceintentions.yaml @@ -0,0 +1,241 @@ +{{- if .Values.controller.enabled }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: serviceintentions.consul.hashicorp.com + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd +spec: + group: consul.hashicorp.com + names: + kind: ServiceIntentions + listKind: ServiceIntentionsList + plural: serviceintentions + shortNames: + - service-intentions + singular: serviceintentions + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ServiceIntentions is the Schema for the serviceintentions API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ServiceIntentionsSpec defines the desired state of ServiceIntentions. + properties: + destination: + description: Destination is the intention destination that will have + the authorization granted to. + properties: + name: + description: Name is the destination of all intentions defined + in this config entry. This may be set to the wildcard character + (*) to match all services that don't otherwise have intentions + defined. + type: string + namespace: + description: Namespace specifies the namespace the config entry + will apply to. This may be set to the wildcard character (*) + to match all services in all namespaces that don't otherwise + have intentions defined. + type: string + type: object + sources: + description: Sources is the list of all intention sources and the + authorization granted to those sources. The order of this list does + not matter, but out of convenience Consul will always store this + reverse sorted by intention precedence, as that is the order that + they will be evaluated at enforcement time. + items: + properties: + action: + description: Action is required for an L4 intention, and should + be set to one of "allow" or "deny" for the action that should + be taken if this intention matches a request. + type: string + description: + description: Description for the intention. This is not used + by Consul, but is presented in API responses to assist tooling. + type: string + name: + description: Name is the source of the intention. This is the + name of a Consul service. The service doesn't need to be registered. + type: string + namespace: + description: Namespace is the namespace for the Name parameter. + type: string + partition: + description: Partition is the Admin Partition for the Name parameter. + type: string + peer: + description: '[Experimental] Peer is the peer name for the Name + parameter.' + type: string + permissions: + description: Permissions is the list of all additional L7 attributes + that extend the intention match criteria. Permission precedence + is applied top to bottom. For any given request the first + permission to match in the list is terminal and stops further + evaluation. As with L4 intentions, traffic that fails to match + any of the provided permissions in this intention will be + subject to the default intention behavior is defined by the + default ACL policy. This should be omitted for an L4 intention + as it is mutually exclusive with the Action field. + items: + properties: + action: + description: Action is one of "allow" or "deny" for the + action that should be taken if this permission matches + a request. + type: string + http: + description: HTTP is a set of HTTP-specific authorization + criteria. + properties: + header: + description: Header is a set of criteria that can + match on HTTP request headers. If more than one + is configured all must match for the overall match + to apply. + items: + properties: + exact: + description: Exact matches if the header with + the given name is this value. + type: string + invert: + description: Invert inverts the logic of the + match. + type: boolean + name: + description: Name is the name of the header + to match. + type: string + prefix: + description: Prefix matches if the header with + the given name has this prefix. + type: string + present: + description: Present matches if the header with + the given name is present with any value. + type: boolean + regex: + description: Regex matches if the header with + the given name matches this pattern. + type: string + suffix: + description: Suffix matches if the header with + the given name has this suffix. + type: string + type: object + type: array + methods: + description: Methods is a list of HTTP methods for + which this match applies. If unspecified all HTTP + methods are matched. If provided the names must + be a valid method. + items: + type: string + type: array + pathExact: + description: PathExact is the exact path to match + on the HTTP request path. + type: string + pathPrefix: + description: PathPrefix is the path prefix to match + on the HTTP request path. + type: string + pathRegex: + description: PathRegex is the regular expression to + match on the HTTP request path. + type: string + type: object + type: object + type: array + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/crd-serviceresolvers.yaml b/charts/hashicorp/consul/0.49.0/templates/crd-serviceresolvers.yaml new file mode 100644 index 000000000..c06063f31 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/crd-serviceresolvers.yaml @@ -0,0 +1,283 @@ +{{- if .Values.controller.enabled }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: serviceresolvers.consul.hashicorp.com + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd +spec: + group: consul.hashicorp.com + names: + kind: ServiceResolver + listKind: ServiceResolverList + plural: serviceresolvers + shortNames: + - service-resolver + singular: serviceresolver + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ServiceResolver is the Schema for the serviceresolvers API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ServiceResolverSpec defines the desired state of ServiceResolver. + properties: + connectTimeout: + description: ConnectTimeout is the timeout for establishing new network + connections to this service. + type: string + defaultSubset: + description: DefaultSubset is the subset to use when no explicit subset + is requested. If empty the unnamed subset is used. + type: string + failover: + additionalProperties: + properties: + datacenters: + description: Datacenters is a fixed list of datacenters to try + during failover. + items: + type: string + type: array + namespace: + description: Namespace is the namespace to resolve the requested + service from to form the failover group of instances. If empty + the current namespace is used. + type: string + service: + description: Service is the service to resolve instead of the + default as the failover group of instances during failover. + type: string + serviceSubset: + description: ServiceSubset is the named subset of the requested + service to resolve as the failover group of instances. If + empty the default subset for the requested service is used. + type: string + type: object + description: Failover controls when and how to reroute traffic to + an alternate pool of service instances. The map is keyed by the + service subset it applies to and the special string "*" is a wildcard + that applies to any subset not otherwise specified here. + type: object + loadBalancer: + description: LoadBalancer determines the load balancing policy and + configuration for services issuing requests to this upstream service. + properties: + hashPolicies: + description: HashPolicies is a list of hash policies to use for + hashing load balancing algorithms. Hash policies are evaluated + individually and combined such that identical lists result in + the same hash. If no hash policies are present, or none are + successfully evaluated, then a random backend host will be selected. + items: + properties: + cookieConfig: + description: CookieConfig contains configuration for the + "cookie" hash policy type. + properties: + path: + description: Path is the path to set for the cookie. + type: string + session: + description: Session determines whether to generate + a session cookie with no expiration. + type: boolean + ttl: + description: TTL is the ttl for generated cookies. Cannot + be specified for session cookies. + type: string + type: object + field: + description: Field is the attribute type to hash on. Must + be one of "header", "cookie", or "query_parameter". Cannot + be specified along with sourceIP. + type: string + fieldValue: + description: FieldValue is the value to hash. ie. header + name, cookie name, URL query parameter name Cannot be + specified along with sourceIP. + type: string + sourceIP: + description: SourceIP determines whether the hash should + be of the source IP rather than of a field and field value. + Cannot be specified along with field or fieldValue. + type: boolean + terminal: + description: Terminal will short circuit the computation + of the hash when multiple hash policies are present. If + a hash is computed when a Terminal policy is evaluated, + then that hash will be used and subsequent hash policies + will be ignored. + type: boolean + type: object + type: array + leastRequestConfig: + description: LeastRequestConfig contains configuration for the + "leastRequest" policy type. + properties: + choiceCount: + description: ChoiceCount determines the number of random healthy + hosts from which to select the one with the least requests. + format: int32 + type: integer + type: object + policy: + description: Policy is the load balancing policy used to select + a host. + type: string + ringHashConfig: + description: RingHashConfig contains configuration for the "ringHash" + policy type. + properties: + maximumRingSize: + description: MaximumRingSize determines the maximum number + of entries in the hash ring. + format: int64 + type: integer + minimumRingSize: + description: MinimumRingSize determines the minimum number + of entries in the hash ring. + format: int64 + type: integer + type: object + type: object + redirect: + description: Redirect when configured, all attempts to resolve the + service this resolver defines will be substituted for the supplied + redirect EXCEPT when the redirect has already been applied. When + substituting the supplied redirect, all other fields besides Kind, + Name, and Redirect will be ignored. + properties: + datacenter: + description: Datacenter is the datacenter to resolve the service + from instead of the current one. + type: string + namespace: + description: Namespace is the Consul namespace to resolve the + service from instead of the current namespace. If empty the + current namespace is assumed. + type: string + partition: + description: Partition is the Consul partition to resolve the + service from instead of the current partition. If empty the + current partition is assumed. + type: string + service: + description: Service is a service to resolve instead of the current + service. + type: string + serviceSubset: + description: ServiceSubset is a named subset of the given service + to resolve instead of one defined as that service's DefaultSubset + If empty the default subset is used. + type: string + type: object + subsets: + additionalProperties: + properties: + filter: + description: Filter is the filter expression to be used for + selecting instances of the requested service. If empty all + healthy instances are returned. This expression can filter + on the same selectors as the Health API endpoint. + type: string + onlyPassing: + description: OnlyPassing specifies the behavior of the resolver's + health check interpretation. If this is set to false, instances + with checks in the passing as well as the warning states will + be considered healthy. If this is set to true, only instances + with checks in the passing state will be considered healthy. + type: boolean + type: object + description: Subsets is map of subset name to subset definition for + all usable named subsets of this service. The map key is the name + of the subset and all names must be valid DNS subdomain elements. + This may be empty, in which case only the unnamed default subset + will be usable. + type: object + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/crd-servicerouters.yaml b/charts/hashicorp/consul/0.49.0/templates/crd-servicerouters.yaml new file mode 100644 index 000000000..3d6aa58da --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/crd-servicerouters.yaml @@ -0,0 +1,316 @@ +{{- if .Values.controller.enabled }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: servicerouters.consul.hashicorp.com + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd +spec: + group: consul.hashicorp.com + names: + kind: ServiceRouter + listKind: ServiceRouterList + plural: servicerouters + shortNames: + - service-router + singular: servicerouter + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ServiceRouter is the Schema for the servicerouters API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ServiceRouterSpec defines the desired state of ServiceRouter. + properties: + routes: + description: Routes are the list of routes to consider when processing + L7 requests. The first route to match in the list is terminal and + stops further evaluation. Traffic that fails to match any of the + provided routes will be routed to the default service. + items: + properties: + destination: + description: Destination controls how to proxy the matching + request(s) to a service. + properties: + namespace: + description: Namespace is the Consul namespace to resolve + the service from instead of the current namespace. If + empty the current namespace is assumed. + type: string + numRetries: + description: NumRetries is the number of times to retry + the request when a retryable result occurs + format: int32 + type: integer + partition: + description: Partition is the Consul partition to resolve + the service from instead of the current partition. If + empty the current partition is assumed. + type: string + prefixRewrite: + description: PrefixRewrite defines how to rewrite the HTTP + request path before proxying it to its final destination. + This requires that either match.http.pathPrefix or match.http.pathExact + be configured on this route. + type: string + requestHeaders: + description: Allow HTTP header manipulation to be configured. + properties: + add: + additionalProperties: + type: string + description: Add is a set of name -> value pairs that + should be appended to the request or response (i.e. + allowing duplicates if the same header already exists). + type: object + remove: + description: Remove is the set of header names that + should be stripped from the request or response. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: Set is a set of name -> value pairs that + should be added to the request or response, overwriting + any existing header values of the same name. + type: object + type: object + requestTimeout: + description: RequestTimeout is the total amount of time + permitted for the entire downstream request (and retries) + to be processed. + type: string + responseHeaders: + description: HTTPHeaderModifiers is a set of rules for HTTP + header modification that should be performed by proxies + as the request passes through them. It can operate on + either request or response headers depending on the context + in which it is used. + properties: + add: + additionalProperties: + type: string + description: Add is a set of name -> value pairs that + should be appended to the request or response (i.e. + allowing duplicates if the same header already exists). + type: object + remove: + description: Remove is the set of header names that + should be stripped from the request or response. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: Set is a set of name -> value pairs that + should be added to the request or response, overwriting + any existing header values of the same name. + type: object + type: object + retryOnConnectFailure: + description: RetryOnConnectFailure allows for connection + failure errors to trigger a retry. + type: boolean + retryOnStatusCodes: + description: RetryOnStatusCodes is a flat list of http response + status codes that are eligible for retry. + items: + format: int32 + type: integer + type: array + service: + description: Service is the service to resolve instead of + the default service. If empty then the default service + name is used. + type: string + serviceSubset: + description: ServiceSubset is a named subset of the given + service to resolve instead of the one defined as that + service's DefaultSubset. If empty, the default subset + is used. + type: string + type: object + match: + description: Match is a set of criteria that can match incoming + L7 requests. If empty or omitted it acts as a catch-all. + properties: + http: + description: HTTP is a set of http-specific match criteria. + properties: + header: + description: Header is a set of criteria that can match + on HTTP request headers. If more than one is configured + all must match for the overall match to apply. + items: + properties: + exact: + description: Exact will match if the header with + the given name is this value. + type: string + invert: + description: Invert inverts the logic of the match. + type: boolean + name: + description: Name is the name of the header to + match. + type: string + prefix: + description: Prefix will match if the header with + the given name has this prefix. + type: string + present: + description: Present will match if the header + with the given name is present with any value. + type: boolean + regex: + description: Regex will match if the header with + the given name matches this pattern. + type: string + suffix: + description: Suffix will match if the header with + the given name has this suffix. + type: string + required: + - name + type: object + type: array + methods: + description: Methods is a list of HTTP methods for which + this match applies. If unspecified all http methods + are matched. + items: + type: string + type: array + pathExact: + description: PathExact is an exact path to match on + the HTTP request path. + type: string + pathPrefix: + description: PathPrefix is a path prefix to match on + the HTTP request path. + type: string + pathRegex: + description: PathRegex is a regular expression to match + on the HTTP request path. + type: string + queryParam: + description: QueryParam is a set of criteria that can + match on HTTP query parameters. If more than one is + configured all must match for the overall match to + apply. + items: + properties: + exact: + description: Exact will match if the query parameter + with the given name is this value. + type: string + name: + description: Name is the name of the query parameter + to match on. + type: string + present: + description: Present will match if the query parameter + with the given name is present with any value. + type: boolean + regex: + description: Regex will match if the query parameter + with the given name matches this pattern. + type: string + required: + - name + type: object + type: array + type: object + type: object + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/crd-servicesplitters.yaml b/charts/hashicorp/consul/0.49.0/templates/crd-servicesplitters.yaml new file mode 100644 index 000000000..532ca209b --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/crd-servicesplitters.yaml @@ -0,0 +1,194 @@ +{{- if .Values.controller.enabled }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: servicesplitters.consul.hashicorp.com + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd +spec: + group: consul.hashicorp.com + names: + kind: ServiceSplitter + listKind: ServiceSplitterList + plural: servicesplitters + shortNames: + - service-splitter + singular: servicesplitter + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ServiceSplitter is the Schema for the servicesplitters API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ServiceSplitterSpec defines the desired state of ServiceSplitter. + properties: + splits: + description: Splits defines how much traffic to send to which set + of service instances during a traffic split. The sum of weights + across all splits must add up to 100. + items: + properties: + namespace: + description: Namespace is the Consul namespace to resolve the + service from instead of the current namespace. If empty the + current namespace is assumed. + type: string + partition: + description: Partition is the Consul partition to resolve the + service from instead of the current partition. If empty the + current partition is assumed. + type: string + requestHeaders: + description: Allow HTTP header manipulation to be configured. + properties: + add: + additionalProperties: + type: string + description: Add is a set of name -> value pairs that should + be appended to the request or response (i.e. allowing + duplicates if the same header already exists). + type: object + remove: + description: Remove is the set of header names that should + be stripped from the request or response. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: Set is a set of name -> value pairs that should + be added to the request or response, overwriting any existing + header values of the same name. + type: object + type: object + responseHeaders: + description: HTTPHeaderModifiers is a set of rules for HTTP + header modification that should be performed by proxies as + the request passes through them. It can operate on either + request or response headers depending on the context in which + it is used. + properties: + add: + additionalProperties: + type: string + description: Add is a set of name -> value pairs that should + be appended to the request or response (i.e. allowing + duplicates if the same header already exists). + type: object + remove: + description: Remove is the set of header names that should + be stripped from the request or response. + items: + type: string + type: array + set: + additionalProperties: + type: string + description: Set is a set of name -> value pairs that should + be added to the request or response, overwriting any existing + header values of the same name. + type: object + type: object + service: + description: Service is the service to resolve instead of the + default. + type: string + serviceSubset: + description: ServiceSubset is a named subset of the given service + to resolve instead of one defined as that service's DefaultSubset. + If empty the default subset is used. + type: string + weight: + description: Weight is a value between 0 and 100 reflecting + what portion of traffic should be directed to this split. + The smallest representable weight is 1/10000 or .01%. + type: number + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/crd-terminatinggateways.yaml b/charts/hashicorp/consul/0.49.0/templates/crd-terminatinggateways.yaml new file mode 100644 index 000000000..50f3d8b67 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/crd-terminatinggateways.yaml @@ -0,0 +1,145 @@ +{{- if .Values.controller.enabled }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: terminatinggateways.consul.hashicorp.com + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd +spec: + group: consul.hashicorp.com + names: + kind: TerminatingGateway + listKind: TerminatingGatewayList + plural: terminatinggateways + shortNames: + - terminating-gateway + singular: terminatinggateway + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: TerminatingGateway is the Schema for the terminatinggateways + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: TerminatingGatewaySpec defines the desired state of TerminatingGateway. + properties: + services: + description: Services is a list of service names represented by the + terminating gateway. + items: + description: A LinkedService is a service represented by a terminating + gateway. + properties: + caFile: + description: CAFile is the optional path to a CA certificate + to use for TLS connections from the gateway to the linked + service. + type: string + certFile: + description: CertFile is the optional path to a client certificate + to use for TLS connections from the gateway to the linked + service. + type: string + keyFile: + description: KeyFile is the optional path to a private key to + use for TLS connections from the gateway to the linked service. + type: string + name: + description: Name is the name of the service, as defined in + Consul's catalog. + type: string + namespace: + description: The namespace the service is registered in. + type: string + sni: + description: SNI is the optional name to specify during the + TLS handshake with a linked service. + type: string + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/create-federation-secret-job.yaml b/charts/hashicorp/consul/0.49.0/templates/create-federation-secret-job.yaml new file mode 100644 index 000000000..6bc2b9f4d --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/create-federation-secret-job.yaml @@ -0,0 +1,161 @@ +{{- if .Values.global.federation.createFederationSecret }} +{{- if not .Values.global.federation.enabled }}{{ fail "global.federation.enabled must be true when global.federation.createFederationSecret is true" }}{{ end }} +{{- if and (not .Values.global.acls.createReplicationToken) .Values.global.acls.manageSystemACLs }}{{ fail "global.acls.createReplicationToken must be true when global.acls.manageSystemACLs is true because the federation secret must include the replication token" }}{{ end }} +{{- if eq (int .Values.server.updatePartition) 0 }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "consul.fullname" . }}-create-federation-secret + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: create-federation-secret + annotations: + "helm.sh/hook": post-install,post-upgrade + {{- /* Hook weight needs to be 1 so that the service account is provisioned first */}} + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": hook-succeeded +spec: + template: + metadata: + name: {{ template "consul.fullname" . }}-create-federation-secret + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: create-federation-secret + annotations: + "consul.hashicorp.com/connect-inject": "false" + spec: + restartPolicy: Never + serviceAccountName: {{ template "consul.fullname" . }}-create-federation-secret + {{- if .Values.client.tolerations }} + tolerations: + {{ tpl .Values.client.tolerations . | nindent 8 | trim }} + {{- end }} + {{- if .Values.client.priorityClassName }} + priorityClassName: {{ .Values.client.priorityClassName | quote }} + {{- end }} + {{- if .Values.client.nodeSelector }} + nodeSelector: + {{ tpl .Values.client.nodeSelector . | indent 8 | trim }} + {{- end }} + volumes: + {{- /* We can assume tls is enabled because there is a check in server-statefulset + that requires tls to be enabled if federation is enabled. */}} + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + - name: consul-ca-key + secret: + {{- if .Values.global.tls.caKey.secretName }} + secretName: {{ .Values.global.tls.caKey.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-key + {{- end }} + items: + - key: {{ default "tls.key" .Values.global.tls.caKey.secretKey }} + path: tls.key + {{- /* We must incude both auto-encrypt and server CAs because we make API calls to the local + Consul client (requiring the auto-encrypt CA) but the secret generated must include the server CA */}} + {{- if .Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + emptyDir: + medium: "Memory" + {{- end }} + {{- if (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey) }} + - name: gossip-encryption-key + secret: + secretName: {{ .Values.global.gossipEncryption.secretName }} + items: + - key: {{ .Values.global.gossipEncryption.secretKey }} + path: gossip.key + {{- else if .Values.global.gossipEncryption.autoGenerate }} + - name: gossip-encryption-key + secret: + secretName: {{ template "consul.fullname" . }}-gossip-encryption-key + items: + - key: key + path: gossip.key + {{- end }} + + {{- if .Values.global.tls.enableAutoEncrypt }} + initContainers: + {{- include "consul.getAutoEncryptClientCA" . | nindent 6 }} + {{- end }} + + containers: + - name: create-federation-secret + image: "{{ .Values.global.imageK8S }}" + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: CONSUL_HTTP_ADDR + value: https://$(HOST_IP):8501 + - name: CONSUL_CACERT + {{- if .Values.global.tls.enableAutoEncrypt }} + value: /consul/tls/client/ca/tls.crt + {{- else }} + value: /consul/tls/ca/tls.crt + {{- end }} + volumeMounts: + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + - name: consul-ca-key + mountPath: /consul/tls/server/ca + readOnly: true + {{- if .Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + mountPath: /consul/tls/client/ca + readOnly: true + {{- end }} + {{- if (or .Values.global.gossipEncryption.autoGenerate (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey)) }} + - name: gossip-encryption-key + mountPath: /consul/gossip + readOnly: true + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + consul-k8s-control-plane create-federation-secret \ + -log-level={{ .Values.global.logLevel }} \ + -log-json={{ .Values.global.logJSON }} \ + {{- if (or .Values.global.gossipEncryption.autoGenerate (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey)) }} + -gossip-key-file=/consul/gossip/gossip.key \ + {{- end }} + {{- if .Values.global.acls.createReplicationToken }} + -export-replication-token=true \ + {{- end }} + -mesh-gateway-service-name={{ .Values.meshGateway.consulServiceName }} \ + -k8s-namespace="${NAMESPACE}" \ + -resource-prefix="{{ template "consul.fullname" . }}" \ + -server-ca-cert-file=/consul/tls/ca/tls.crt \ + -server-ca-key-file=/consul/tls/server/ca/tls.key \ + -consul-api-timeout={{ .Values.global.consulAPITimeout }} + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/create-federation-secret-podsecuritypolicy.yaml b/charts/hashicorp/consul/0.49.0/templates/create-federation-secret-podsecuritypolicy.yaml new file mode 100644 index 000000000..821731199 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/create-federation-secret-podsecuritypolicy.yaml @@ -0,0 +1,42 @@ +{{- if .Values.global.enablePodSecurityPolicies }} +{{- if .Values.global.federation.createFederationSecret }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-create-federation-secret + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: create-federation-secret + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'secret' + - 'emptyDir' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/create-federation-secret-role.yaml b/charts/hashicorp/consul/0.49.0/templates/create-federation-secret-role.yaml new file mode 100644 index 000000000..086932a83 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/create-federation-secret-role.yaml @@ -0,0 +1,49 @@ +{{- if .Values.global.federation.createFederationSecret }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-create-federation-secret + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: create-federation-secret + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation +rules: + {{/* Must have separate rule for create secret permissions vs update because + can't set resourceNames for create (https://github.com/kubernetes/kubernetes/issues/80295) */}} + - apiGroups: [""] + resources: + - secrets + verbs: + - create + - apiGroups: [""] + resources: + - secrets + resourceNames: + - {{ template "consul.fullname" . }}-federation + verbs: + - update + {{- if .Values.global.acls.manageSystemACLs }} + - apiGroups: [""] + resources: + - secrets + resourceNames: + - {{ template "consul.fullname" . }}-acl-replication-acl-token + verbs: + - get + {{- end }} + {{- if .Values.global.enablePodSecurityPolicies }} + - apiGroups: ["policy"] + resources: + - podsecuritypolicies + verbs: + - use + resourceNames: + - {{ template "consul.fullname" . }}-create-federation-secret + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/create-federation-secret-rolebinding.yaml b/charts/hashicorp/consul/0.49.0/templates/create-federation-secret-rolebinding.yaml new file mode 100644 index 000000000..3db8e7cb0 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/create-federation-secret-rolebinding.yaml @@ -0,0 +1,23 @@ +{{- if .Values.global.federation.createFederationSecret }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-create-federation-secret + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: create-federation-secret + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-create-federation-secret +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-create-federation-secret +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/create-federation-secret-serviceaccount.yaml b/charts/hashicorp/consul/0.49.0/templates/create-federation-secret-serviceaccount.yaml new file mode 100644 index 000000000..e398ec69c --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/create-federation-secret-serviceaccount.yaml @@ -0,0 +1,22 @@ +{{- if .Values.global.federation.createFederationSecret }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-create-federation-secret + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: create-federation-secret + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/dns-service.yaml b/charts/hashicorp/consul/0.49.0/templates/dns-service.yaml new file mode 100644 index 000000000..0fc66595e --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/dns-service.yaml @@ -0,0 +1,41 @@ +{{- if (or (and (ne (.Values.dns.enabled | toString) "-") .Values.dns.enabled) (and (eq (.Values.dns.enabled | toString) "-") .Values.global.enabled)) }} +# Service for Consul DNS. +apiVersion: v1 +kind: Service +metadata: + name: {{ template "consul.fullname" . }}-dns + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: dns + {{- if .Values.dns.annotations }} + annotations: + {{ tpl .Values.dns.annotations . | nindent 4 | trim }} + {{- end }} +spec: +{{- if .Values.dns.type }} + type: {{ .Values.dns.type }} +{{- end }} +{{- if .Values.dns.clusterIP }} + clusterIP: {{ .Values.dns.clusterIP }} +{{- end }} + ports: + - name: dns-tcp + port: 53 + protocol: "TCP" + targetPort: dns-tcp + - name: dns-udp + port: 53 + protocol: "UDP" + targetPort: dns-udp + selector: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + hasDNS: "true" + {{- if .Values.dns.additionalSpec }} + {{ tpl .Values.dns.additionalSpec . | nindent 2 | trim }} + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/enterprise-license-job.yaml b/charts/hashicorp/consul/0.49.0/templates/enterprise-license-job.yaml new file mode 100644 index 000000000..02921db3b --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/enterprise-license-job.yaml @@ -0,0 +1,138 @@ +{{- if .Values.server.enterpriseLicense }}{{ fail "server.enterpriseLicense has been moved to global.enterpriseLicense" }}{{ end -}} +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey (not .Values.global.enterpriseLicense.enableLicenseAutoload)) }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "consul.fullname" . }}-enterprise-license + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/managed-by: {{.Release.Service | quote }} + app.kubernetes.io/instance: {{.Release.Name | quote }} + helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: license + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-weight": "100" + "helm.sh/hook-delete-policy": hook-succeeded +spec: + template: + metadata: + name: {{ template "consul.fullname" . }}-license + labels: + app.kubernetes.io/managed-by: {{.Release.Service | quote }} + app.kubernetes.io/instance: {{.Release.Name | quote }} + helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: license + annotations: + "consul.hashicorp.com/connect-inject": "false" + spec: + restartPolicy: Never + serviceAccountName: {{ template "consul.fullname" . }}-enterprise-license + {{- if .Values.global.tls.enabled }} + volumes: + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + containers: + - name: apply-enterprise-license + image: "{{ default .Values.global.image .Values.server.image }}" + env: + - name: ENTERPRISE_LICENSE + {{- if .Values.global.secretsBackend.vault.enabled }} + value: /vault/secrets/enterpriselicense.txt + {{- else }} + valueFrom: + secretKeyRef: + name: {{ .Values.global.enterpriseLicense.secretName }} + key: {{ .Values.global.enterpriseLicense.secretKey }} + {{- end }} + - name: CONSUL_HTTP_ADDR + {{- if .Values.global.tls.enabled }} + value: https://{{ template "consul.fullname" . }}-server:8501 + {{- else }} + value: http://{{ template "consul.fullname" . }}-server:8500 + {{- end }} + {{- if .Values.global.tls.enabled }} + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- end}} + {{- if .Values.global.acls.manageSystemACLs }} + - name: CONSUL_HTTP_TOKEN + valueFrom: + secretKeyRef: + name: "{{ template "consul.fullname" . }}-enterprise-license-acl-token" + key: "token" + {{- end}} + command: + - "/bin/sh" + - "-c" + - | + # Create a script that we can execute with the timeout command. + mkdir -p /tmp/scripts/ + cat > /tmp/scripts/apply-license.sh << 'EOF' + #!/bin/sh + while true; do + echo "Applying license..." + if consul license put "${ENTERPRISE_LICENSE}" 2>&1; then + echo "License applied successfully" + break + fi + echo "Retrying in 2s..." + sleep 2 + done + EOF + chmod +x /tmp/scripts/apply-license.sh + + # Time out after 20 minutes. Use || to support new timeout versions that don't accept -t + timeout -t 1200 /tmp/scripts/apply-license.sh 2> /dev/null || timeout 1200 /tmp/scripts/apply-license.sh 2> /dev/null + {{- if .Values.global.tls.enabled }} + volumeMounts: + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + {{- if .Values.global.acls.manageSystemACLs }} + initContainers: + - name: ent-license-acl-init + image: {{ .Values.global.imageK8S }} + command: + - "/bin/sh" + - "-ec" + - | + consul-k8s-control-plane acl-init \ + -secret-name="{{ template "consul.fullname" . }}-enterprise-license-acl-token" \ + -k8s-namespace={{ .Release.Namespace }} \ + -consul-api-timeout={{ .Values.global.consulAPITimeout }} + resources: + requests: + memory: "25Mi" + cpu: "50m" + limits: + memory: "25Mi" + cpu: "50m" + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/enterprise-license-podsecuritypolicy.yaml b/charts/hashicorp/consul/0.49.0/templates/enterprise-license-podsecuritypolicy.yaml new file mode 100644 index 000000000..cf9636747 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/enterprise-license-podsecuritypolicy.yaml @@ -0,0 +1,39 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey (not .Values.global.enterpriseLicense.enableLicenseAutoload)) }} +{{- if .Values.global.enablePodSecurityPolicies }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-enterprise-license + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: license +spec: + privileged: false + # Allow core volume types. + volumes: + - 'secret' + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/enterprise-license-role.yaml b/charts/hashicorp/consul/0.49.0/templates/enterprise-license-role.yaml new file mode 100644 index 000000000..6a1b7fdff --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/enterprise-license-role.yaml @@ -0,0 +1,37 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey (not .Values.global.enterpriseLicense.enableLicenseAutoload)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-enterprise-license + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: license +{{- if or .Values.global.acls.manageSystemACLs .Values.global.enablePodSecurityPolicies }} +rules: +{{- if .Values.global.acls.manageSystemACLs }} + - apiGroups: [""] + resources: + - secrets + resourceNames: + - {{ template "consul.fullname" . }}-enterprise-license-acl-token + verbs: + - get +{{- end }} +{{- if .Values.global.enablePodSecurityPolicies }} + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" . }}-enterprise-license + verbs: + - use +{{- end }} +{{- else }} +rules: [] +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/enterprise-license-rolebinding.yaml b/charts/hashicorp/consul/0.49.0/templates/enterprise-license-rolebinding.yaml new file mode 100644 index 000000000..a21118b43 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/enterprise-license-rolebinding.yaml @@ -0,0 +1,22 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey (not .Values.global.enterpriseLicense.enableLicenseAutoload)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-enterprise-license + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: license +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-enterprise-license +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-enterprise-license +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/enterprise-license-serviceaccount.yaml b/charts/hashicorp/consul/0.49.0/templates/enterprise-license-serviceaccount.yaml new file mode 100644 index 000000000..31c9da841 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/enterprise-license-serviceaccount.yaml @@ -0,0 +1,21 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey (not .Values.global.enterpriseLicense.enableLicenseAutoload)) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-enterprise-license + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: license +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/expose-servers-service.yaml b/charts/hashicorp/consul/0.49.0/templates/expose-servers-service.yaml new file mode 100644 index 000000000..fcfaf8038 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/expose-servers-service.yaml @@ -0,0 +1,63 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- $serverExposeServiceEnabled := (or (and (ne (.Values.server.exposeService.enabled | toString) "-") .Values.server.exposeService.enabled) (and (eq (.Values.server.exposeService.enabled | toString) "-") (or .Values.global.peering.enabled .Values.global.adminPartitions.enabled))) -}} +{{- if (and $serverEnabled $serverExposeServiceEnabled) }} + +# Service with an external IP to reach Consul servers. +# Used for exposing gRPC port for peering and ports for client partitions to discover servers. +apiVersion: v1 +kind: Service +metadata: + name: {{ template "consul.fullname" . }}-expose-servers + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server + annotations: + {{- if .Values.server.exposeService.annotations }} + {{ tpl .Values.server.exposeService.annotations . | nindent 4 | trim }} + {{- end }} +spec: + type: "{{ .Values.server.exposeService.type }}" + ports: + {{- if (or (not .Values.global.tls.enabled) (not .Values.global.tls.httpsOnly)) }} + - name: http + port: 8500 + targetPort: 8500 + {{ if (and (eq .Values.server.exposeService.type "NodePort") .Values.server.exposeService.nodePort.http) }} + nodePort: {{ .Values.server.exposeService.nodePort.http }} + {{- end }} + {{- end }} + {{- if .Values.global.tls.enabled }} + - name: https + port: 8501 + targetPort: 8501 + {{ if (and (eq .Values.server.exposeService.type "NodePort") .Values.server.exposeService.nodePort.https) }} + nodePort: {{ .Values.server.exposeService.nodePort.https }} + {{- end }} + {{- end }} + - name: serflan + port: 8301 + targetPort: 8301 + {{ if (and (eq .Values.server.exposeService.type "NodePort") .Values.server.exposeService.nodePort.serf) }} + nodePort: {{ .Values.server.exposeService.nodePort.serf }} + {{- end }} + - name: rpc + port: 8300 + targetPort: 8300 + {{ if (and (eq .Values.server.exposeService.type "NodePort") .Values.server.exposeService.nodePort.rpc) }} + nodePort: {{ .Values.server.exposeService.nodePort.rpc }} + {{- end }} + - name: grpc + port: 8502 + targetPort: 8503 + {{ if (and (eq .Values.server.exposeService.type "NodePort") .Values.server.exposeService.nodePort.grpc) }} + nodePort: {{ .Values.server.exposeService.nodePort.grpc }} + {{- end }} + selector: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + component: server +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/gossip-encryption-autogenerate-job.yaml b/charts/hashicorp/consul/0.49.0/templates/gossip-encryption-autogenerate-job.yaml new file mode 100644 index 000000000..e1a6e4982 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/gossip-encryption-autogenerate-job.yaml @@ -0,0 +1,62 @@ +{{- if .Values.global.gossipEncryption.autoGenerate }} +{{- if (or .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey) }} + {{ fail "If global.gossipEncryption.autoGenerate is true, global.gossipEncryption.secretName and global.gossipEncryption.secretKey must not be set." }} +{{ end }} +# automatically generate encryption key for gossip protocol and save it in Kubernetes secret +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gossip-encryption-autogenerate + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation +spec: + template: + metadata: + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: gossip-encryption-autogenerate + annotations: + "consul.hashicorp.com/connect-inject": "false" + spec: + restartPolicy: Never + serviceAccountName: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate + {{- if not .Values.global.openshift.enabled }} + securityContext: + runAsNonRoot: true + runAsGroup: 1000 + runAsUser: 100 + fsGroup: 1000 + {{- end }} + containers: + - name: gossip-encryption-autogen + image: "{{ .Values.global.imageK8S }}" + command: + - "/bin/sh" + - "-ec" + - | + consul-k8s-control-plane gossip-encryption-autogenerate \ + -namespace={{ .Release.Namespace }} \ + -secret-name={{ template "consul.fullname" . }}-gossip-encryption-key \ + -secret-key="key" \ + -log-level={{ .Values.global.logLevel }} \ + -log-json={{ .Values.global.logJSON }} + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml b/charts/hashicorp/consul/0.49.0/templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml new file mode 100644 index 000000000..707ebe57c --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml @@ -0,0 +1,40 @@ +{{- if .Values.global.gossipEncryption.autoGenerate }} +--- +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gossip-encryption-autogenerate + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'secret' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/gossip-encryption-autogenerate-role.yaml b/charts/hashicorp/consul/0.49.0/templates/gossip-encryption-autogenerate-role.yaml new file mode 100644 index 000000000..8c51c96ff --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/gossip-encryption-autogenerate-role.yaml @@ -0,0 +1,32 @@ +{{- if .Values.global.gossipEncryption.autoGenerate }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gossip-encryption-autogenerate + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +rules: +- apiGroups: [""] + resources: + - secrets + verbs: + - create + - get +{{- if .Values.global.enablePodSecurityPolicies }} +- apiGroups: ["policy"] + resources: + - podsecuritypolicies + verbs: + - use + resourceNames: + - {{ template "consul.fullname" . }}-gossip-encryption-autogenerate +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/gossip-encryption-autogenerate-rolebinding.yaml b/charts/hashicorp/consul/0.49.0/templates/gossip-encryption-autogenerate-rolebinding.yaml new file mode 100644 index 000000000..7118475f6 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/gossip-encryption-autogenerate-rolebinding.yaml @@ -0,0 +1,23 @@ +{{- if .Values.global.gossipEncryption.autoGenerate }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gossip-encryption-autogenerate + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/gossip-encryption-autogenerate-serviceaccount.yaml b/charts/hashicorp/consul/0.49.0/templates/gossip-encryption-autogenerate-serviceaccount.yaml new file mode 100644 index 000000000..1fd620237 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/gossip-encryption-autogenerate-serviceaccount.yaml @@ -0,0 +1,22 @@ +{{- if .Values.global.gossipEncryption.autoGenerate }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: gossip-encryption-autogenerate + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/ingress-gateways-deployment.yaml b/charts/hashicorp/consul/0.49.0/templates/ingress-gateways-deployment.yaml new file mode 100644 index 000000000..77a4d9dcd --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/ingress-gateways-deployment.yaml @@ -0,0 +1,489 @@ +{{- if .Values.ingressGateways.enabled }} +{{- if not .Values.connectInject.enabled }}{{ fail "connectInject.enabled must be true" }}{{ end -}} +{{- if not .Values.client.grpc }}{{ fail "client.grpc must be true" }}{{ end -}} +{{- if and .Values.global.adminPartitions.enabled (not .Values.global.enableConsulNamespaces) }}{{ fail "global.enableConsulNamespaces must be true if global.adminPartitions.enabled=true" }}{{ end }} +{{- if not (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }}{{ fail "clients must be enabled" }}{{ end -}} +{{- if .Values.global.lifecycleSidecarContainer }}{{ fail "global.lifecycleSidecarContainer has been renamed to global.consulSidecarContainer. Please set values using global.consulSidecarContainer." }}{{ end }} + +{{- $root := . }} +{{- $defaults := .Values.ingressGateways.defaults }} +{{- $names := dict }} + +{{- /* Check if gateway names are unique. */ -}} +{{- $gateways := .Values.ingressGateways.gateways }} +{{- range $outerIngressIndex, $outerIngressVal := $gateways }} + +{{- range $innerIngressIndex, $innerIngressVal := $gateways }} +{{- if (and (ne $outerIngressIndex $innerIngressIndex) (eq $outerIngressVal.name $innerIngressVal.name)) }} +{{ fail (cat "ingress gateways must have unique names but found duplicate name" $innerIngressVal.name) }} +{{ end -}} +{{ end -}} +{{ end -}} + +{{- range .Values.ingressGateways.gateways }} + +{{- $service := .service }} + +{{- if empty .name }} +# Check that the gateway name is provided +{{ fail "Ingress gateway names cannot be empty"}} +{{ end -}} +{{- if hasKey $names .name }} +# Check that the gateway name is unique +{{ fail "Ingress gateway names must be unique"}} +{{ end -}} +{{- /* Add the gateway name to the $names dict to ensure uniqueness */ -}} +{{- $_ := set $names .name .name }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: ingress-gateway + ingress-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} +spec: + replicas: {{ default $defaults.replicas .replicas }} + selector: + matchLabels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: ingress-gateway + ingress-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} + template: + metadata: + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: ingress-gateway + ingress-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} + annotations: + {{- if (and $root.Values.global.secretsBackend.vault.enabled $root.Values.global.tls.enabled) }} + "vault.hashicorp.com/agent-init-first": "true" + "vault.hashicorp.com/agent-inject": "true" + "vault.hashicorp.com/role": {{ $root.Values.global.secretsBackend.vault.consulCARole }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ $root.Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" $root }} + {{- if and $root.Values.global.secretsBackend.vault.ca.secretName $root.Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": {{ $root.Values.global.secretsBackend.vault.ca.secretName }} + "vault.hashicorp.com/ca-cert": /vault/custom/{{ $root.Values.global.secretsBackend.vault.ca.secretKey }} + {{- end }} + {{- if $root.Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl $root.Values.global.secretsBackend.vault.agentAnnotations $root | nindent 8 | trim }} + {{- end }} + {{- end }} + "consul.hashicorp.com/connect-inject": "false" + {{- if (and $root.Values.global.metrics.enabled $root.Values.global.metrics.enableGatewayMetrics) }} + "prometheus.io/scrape": "true" + "prometheus.io/path": "/metrics" + "prometheus.io/port": "20200" + {{- end }} + {{- if $defaults.annotations }} + # We allow both default annotations and gateway-specific annotations + {{- tpl $defaults.annotations $root | nindent 8 }} + {{- end }} + {{- if .annotations }} + {{- tpl .annotations $root | nindent 8 }} + {{- end }} + spec: + {{- if (or $defaults.affinity .affinity) }} + affinity: + {{ tpl (default $defaults.affinity .affinity) $root | nindent 8 | trim }} + {{- end }} + {{- if (or $defaults.tolerations .tolerations) }} + tolerations: + {{ tpl (default $defaults.tolerations .tolerations) $root | nindent 8 | trim }} + {{- end }} + {{- if (or $defaults.topologySpreadConstraints .topologySpreadConstraints) }} + topologySpreadConstraints: + {{ tpl (default $defaults.topologySpreadConstraints .topologySpreadConstraints) $root | nindent 8 | trim }} + {{- end }} + terminationGracePeriodSeconds: {{ default $defaults.terminationGracePeriodSeconds .terminationGracePeriodSeconds }} + serviceAccountName: {{ template "consul.fullname" $root }}-{{ .name }} + volumes: + - name: consul-bin + emptyDir: {} + - name: consul-service + emptyDir: + medium: "Memory" + {{- if $root.Values.global.tls.enabled }} + {{- if not (and $root.Values.externalServers.enabled $root.Values.externalServers.useSystemRoots) }} + - name: consul-ca-cert + secret: + {{- if $root.Values.global.tls.caCert.secretName }} + secretName: {{ $root.Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" $root }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" $root.Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + {{- if $root.Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + emptyDir: + medium: "Memory" + {{- end }} + {{- end }} + initContainers: + # We use the Envoy image as our base image so we use an init container to + # copy the Consul binary to a shared directory that can be used when + # starting Envoy. + - name: copy-consul-bin + image: {{ $root.Values.global.image | quote }} + command: + - cp + - /bin/consul + - /consul-bin/consul + volumeMounts: + - name: consul-bin + mountPath: /consul-bin + {{- $initContainer := .initCopyConsulContainer }} + {{- if (or $initContainer $defaults.initCopyConsulContainer) }} + {{- if (default $defaults.initCopyConsulContainer.resources $initContainer.resources) }} + resources: {{ toYaml (default $defaults.initCopyConsulContainer.resources $initContainer.resources) | nindent 12 }} + {{- end }} + {{- end }} + {{- if (and $root.Values.global.tls.enabled $root.Values.global.tls.enableAutoEncrypt) }} + {{- include "consul.getAutoEncryptClientCA" $root | nindent 8 }} + {{- end }} + # ingress-gateway-init registers the ingress gateway service with Consul. + - name: ingress-gateway-init + image: {{ $root.Values.global.imageK8S }} + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- if $root.Values.global.tls.enabled }} + - name: CONSUL_HTTP_ADDR + value: https://$(HOST_IP):8501 + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- else }} + - name: CONSUL_HTTP_ADDR + value: http://$(HOST_IP):8500 + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + {{- if $root.Values.global.acls.manageSystemACLs }} + consul-k8s-control-plane acl-init \ + -component-name=ingress-gateway/{{ template "consul.fullname" $root }}-{{ .name }} \ + -acl-auth-method={{ template "consul.fullname" $root }}-k8s-component-auth-method \ + {{- if $root.Values.global.adminPartitions.enabled }} + -partition={{ $root.Values.global.adminPartitions.name }} \ + {{- end }} + -token-sink-file=/consul/service/acl-token \ + -consul-api-timeout={{ $root.Values.global.consulAPITimeout }} \ + -log-level={{ default $root.Values.global.logLevel }} \ + -log-json={{ $root.Values.global.logJSON }} + {{ end }} + + {{- $serviceType := (default $defaults.service.type $service.type) }} + {{- if (eq $serviceType "NodePort") }} + WAN_ADDR="${HOST_IP}" + {{- else if (or (eq $serviceType "ClusterIP") (eq $serviceType "LoadBalancer")) }} + consul-k8s-control-plane service-address \ + -log-level={{ $root.Values.global.logLevel }} \ + -log-json={{ $root.Values.global.logJSON }} \ + -k8s-namespace={{ $root.Release.Namespace }} \ + -name={{ template "consul.fullname" $root }}-{{ .name }} \ + -output-file=/tmp/address.txt + WAN_ADDR="$(cat /tmp/address.txt)" + {{- else }} + {{- fail "currently set ingressGateway value service.type is not supported" }} + {{- end }} + + {{- if (eq $serviceType "NodePort") }} + {{- if $service.ports }} + {{- $firstPort := first $service.ports}} + {{- if $firstPort.nodePort }} + WAN_PORT={{ $firstPort.nodePort }} + {{- else }}{{ fail "if ingressGateways .service.type=NodePort and defining ingressGateways.gateways.service.ports, the first port entry must include a nodePort" }} + {{- end }} + {{- else if $defaults.service.ports }} + {{- $firstDefaultPort := first $defaults.service.ports}} + {{- if $firstDefaultPort.nodePort }} + WAN_PORT={{ $firstDefaultPort.nodePort }} + {{- else }}{{ fail "if ingressGateways .service.type=NodePort and using ingressGateways.defaults.service.ports, the first port entry must include a nodePort" }} + {{- end }} + {{- else }}{{ fail "if ingressGateways .service.type=NodePort, the first port entry in either the defaults or specific gateway must include a nodePort" }} + {{- end }} + + {{- else }} + {{- if $service.ports }} + {{- $firstPort := first $service.ports}} + {{- if $firstPort.port }} + WAN_PORT={{ $firstPort.port }} + {{- else }}{{ fail "if ingressGateways .service.type is not NodePort and defining ingressGateways.gateways.service.ports, the first port entry must include a port" }} + {{- end }} + {{- else if $defaults.service.ports }} + {{- $firstDefaultPort := first $defaults.service.ports}} + {{- if $firstDefaultPort.port }} + WAN_PORT={{ $firstDefaultPort.port }} + {{- else }}{{ fail "if ingressGateways .service.type is not NodePort and using ingressGateways.defaults.service.ports, the first port entry must include a port" }} + {{- end }} + {{- else }}{{ fail "if ingressGateways .service.type is not NodePort, the first port entry in either the defaults or specific gateway must include a port" }} + {{- end }} + {{- end }} + + cat > /consul/service/service.hcl << EOF + service { + kind = "ingress-gateway" + name = "{{ .name }}" + id = "${POD_NAME}" + {{- if $root.Values.global.enableConsulNamespaces }} + namespace = "{{ (default $defaults.consulNamespace .consulNamespace) }}" + {{- end }} + {{- if $root.Values.global.adminPartitions.enabled }} + partition = "{{ $root.Values.global.adminPartitions.name }}" + {{- end }} + port = ${WAN_PORT} + address = "${WAN_ADDR}" + tagged_addresses { + lan { + address = "${POD_IP}" + port = 21000 + } + wan { + address = "${WAN_ADDR}" + port = ${WAN_PORT} + } + } + proxy { + config { + {{- if (and $root.Values.global.metrics.enabled $root.Values.global.metrics.enableGatewayMetrics) }} + envoy_prometheus_bind_addr = "${POD_IP}:20200" + {{- end }} + envoy_gateway_no_default_bind = true + envoy_gateway_bind_addresses { + all-interfaces { + address = "0.0.0.0" + } + } + } + } + checks = [ + { + name = "Ingress Gateway Listening" + interval = "10s" + tcp = "${POD_IP}:21000" + deregister_critical_service_after = "6h" + } + ] + } + EOF + + /consul-bin/consul services register \ + {{- if $root.Values.global.acls.manageSystemACLs }} + -token-file=/consul/service/acl-token \ + {{- end }} + /consul/service/service.hcl + volumeMounts: + - name: consul-service + mountPath: /consul/service + - name: consul-bin + mountPath: /consul-bin + {{- if $root.Values.global.tls.enabled }} + {{- if $root.Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + {{- else }} + - name: consul-ca-cert + {{- end }} + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + containers: + - name: ingress-gateway + image: {{ $root.Values.global.imageEnvoy | quote }} + {{- if (default $defaults.resources .resources) }} + resources: {{ toYaml (default $defaults.resources .resources) | nindent 12 }} + {{- end }} + volumeMounts: + - name: consul-bin + mountPath: /consul-bin + - name: consul-service + mountPath: /consul/service + readOnly: true + {{- if $root.Values.global.tls.enabled }} + {{- if $root.Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + {{- else }} + - name: consul-ca-cert + {{- end }} + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- if $root.Values.global.acls.manageSystemACLs }} + - name: CONSUL_HTTP_TOKEN_FILE + value: "/consul/service/acl-token" + {{- end}} + {{- if $root.Values.global.tls.enabled }} + - name: CONSUL_HTTP_ADDR + value: https://$(HOST_IP):8501 + - name: CONSUL_GRPC_ADDR + value: https://$(HOST_IP):8502 + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- else }} + - name: CONSUL_HTTP_ADDR + value: http://$(HOST_IP):8500 + - name: CONSUL_GRPC_ADDR + value: $(HOST_IP):8502 + {{- end }} + command: + - /consul-bin/consul + - connect + - envoy + - -gateway=ingress + - -proxy-id=$(POD_NAME) + - -address=$(POD_IP):21000 + {{- if $root.Values.global.enableConsulNamespaces }} + - -namespace={{ default $defaults.consulNamespace .consulNamespace }} + {{- end }} + {{- if $root.Values.global.adminPartitions.enabled }} + - -partition={{ $root.Values.global.adminPartitions.name }} + {{- end }} + livenessProbe: + tcpSocket: + port: 21000 + failureThreshold: 3 + initialDelaySeconds: 30 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + readinessProbe: + tcpSocket: + port: 21000 + failureThreshold: 3 + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + ports: + - name: gateway-health + containerPort: 21000 + {{- range $index, $allPorts := (default $defaults.service.ports $service.ports) }} + - name: gateway-{{ $index }} + containerPort: {{ $allPorts.port }} + {{- end }} + lifecycle: + preStop: + exec: + command: + - "/bin/sh" + - "-ec" + - | + /consul-bin/consul services deregister \ + {{- if $root.Values.global.enableConsulNamespaces }} + -namespace={{ default $defaults.consulNamespace .consulNamespace }} \ + {{- end }} + {{- if $root.Values.global.adminPartitions.enabled }} + -partition={{ $root.Values.global.adminPartitions.name }} \ + {{- end }} + -id="${POD_NAME}" + {{- if $root.Values.global.acls.manageSystemACLs }} + - "/consul-bin/consul logout" + {{- end}} + + # consul-sidecar ensures the ingress gateway is always registered with + # the local Consul agent, even if it loses the initial registration. + - name: consul-sidecar + image: {{ $root.Values.global.imageK8S }} + volumeMounts: + - name: consul-service + mountPath: /consul/service + readOnly: true + - name: consul-bin + mountPath: /consul-bin + {{- if $root.Values.global.tls.enabled }} + {{- if $root.Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + {{- else }} + - name: consul-ca-cert + {{- end }} + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- if $root.Values.global.consulSidecarContainer }} + {{- if $root.Values.global.consulSidecarContainer.resources }} + resources: {{ toYaml $root.Values.global.consulSidecarContainer.resources | nindent 12 }} + {{- end }} + {{- end }} + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + {{- if $root.Values.global.tls.enabled }} + - name: CONSUL_HTTP_ADDR + value: https://$(HOST_IP):8501 + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- else }} + - name: CONSUL_HTTP_ADDR + value: http://$(HOST_IP):8500 + {{- end }} + command: + - consul-k8s-control-plane + - consul-sidecar + - -log-level={{ $root.Values.global.logLevel }} + - -log-json={{ $root.Values.global.logJSON }} + - -service-config=/consul/service/service.hcl + - -consul-binary=/consul-bin/consul + - -consul-api-timeout={{ $root.Values.global.consulAPITimeout }} + {{- if $root.Values.global.acls.manageSystemACLs }} + - -token-file=/consul/service/acl-token + {{- end }} + {{- if (default $defaults.priorityClassName .priorityClassName) }} + priorityClassName: {{ default $defaults.priorityClassName .priorityClassName | quote }} + {{- end }} + {{- if (default $defaults.nodeSelector .nodeSelector) }} + nodeSelector: + {{ tpl (default $defaults.nodeSelector .nodeSelector) $root | indent 8 | trim }} + {{- end }} +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/ingress-gateways-podsecuritypolicy.yaml b/charts/hashicorp/consul/0.49.0/templates/ingress-gateways-podsecuritypolicy.yaml new file mode 100644 index 000000000..f7354da2b --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/ingress-gateways-podsecuritypolicy.yaml @@ -0,0 +1,45 @@ +{{- if (and .Values.global.enablePodSecurityPolicies .Values.ingressGateways.enabled) }} +{{- $root := . }} +{{- range .Values.ingressGateways.gateways }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: ingress-gateway + ingress-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/ingress-gateways-role.yaml b/charts/hashicorp/consul/0.49.0/templates/ingress-gateways-role.yaml new file mode 100644 index 000000000..49e8486e5 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/ingress-gateways-role.yaml @@ -0,0 +1,46 @@ +{{- if .Values.ingressGateways.enabled }} + +{{- $root := . }} +{{- $defaults := .Values.ingressGateways.defaults }} + +{{- range .Values.ingressGateways.gateways }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: ingress-gateway + ingress-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} +rules: + - apiGroups: [""] + resources: + - services + resourceNames: + - {{ template "consul.fullname" $root }}-{{ .name }} + verbs: + - get +{{- if $root.Values.global.enablePodSecurityPolicies }} + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" $root }}-{{ .name }} + verbs: + - use +{{- end }} +{{- if $root.Values.global.acls.manageSystemACLs }} + - apiGroups: [""] + resources: + - secrets + resourceNames: + - {{ template "consul.fullname" $root }}-{{ .name }}-acl-token + verbs: + - get +{{- end }} +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/ingress-gateways-rolebinding.yaml b/charts/hashicorp/consul/0.49.0/templates/ingress-gateways-rolebinding.yaml new file mode 100644 index 000000000..601de775f --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/ingress-gateways-rolebinding.yaml @@ -0,0 +1,25 @@ +{{- if .Values.ingressGateways.enabled }} +{{- $root := . }} +{{- range .Values.ingressGateways.gateways }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: ingress-gateway + ingress-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" $root }}-{{ .name }} +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" $root }}-{{ .name }} +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/ingress-gateways-service.yaml b/charts/hashicorp/consul/0.49.0/templates/ingress-gateways-service.yaml new file mode 100644 index 000000000..cf54a740f --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/ingress-gateways-service.yaml @@ -0,0 +1,51 @@ +{{- if .Values.ingressGateways.enabled }} + +{{- $root := . }} +{{- $defaults := .Values.ingressGateways.defaults }} + +{{- range .Values.ingressGateways.gateways }} + +{{- $service := .service }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: ingress-gateway + ingress-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} + {{- if (or $defaults.service.annotations $service.annotations) }} + # We allow both default annotations and gateway-specific annotations + annotations: + {{- if $defaults.service.annotations }} + {{ tpl $defaults.service.annotations $root | nindent 4 | trim }} + {{- end }} + {{- if $service.annotations }} + {{ tpl $service.annotations $root | nindent 4 | trim }} + {{- end }} + {{- end }} +spec: + selector: + app: {{ template "consul.name" $root }} + release: "{{ $root.Release.Name }}" + component: ingress-gateway + ingress-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} + ports: + {{- range $index, $ports := (default $defaults.service.ports $service.ports) }} + - name: gateway-{{ $index }} + port: {{ $ports.port }} + {{- if (and (eq (default $defaults.service.type $service.type) "NodePort") $ports.nodePort) }} + nodePort: {{ $ports.nodePort }} + {{- end}} + {{- end }} + type: {{ default $defaults.service.type $service.type }} + {{- if (default $defaults.service.additionalSpec $service.additionalSpec) }} + {{ tpl (default $defaults.service.additionalSpec $service.additionalSpec) $root | nindent 2 | trim }} + {{- end }} +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/ingress-gateways-serviceaccount.yaml b/charts/hashicorp/consul/0.49.0/templates/ingress-gateways-serviceaccount.yaml new file mode 100644 index 000000000..cea6cafc2 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/ingress-gateways-serviceaccount.yaml @@ -0,0 +1,35 @@ +{{- if .Values.ingressGateways.enabled }} +{{- $root := . }} +{{- $defaults := .Values.ingressGateways.defaults }} +{{- range .Values.ingressGateways.gateways }} +{{- $serviceAccount := .serviceAccount }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: ingress-gateway + ingress-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} + {{- if (or $defaults.serviceAccount.annotations $serviceAccount.annotations) }} + annotations: + {{- if $defaults.serviceAccount.annotations }} + {{ tpl $defaults.serviceAccount.annotations $root | nindent 4 | trim }} + {{- end }} + {{- if $serviceAccount.annotations }} + {{ tpl $serviceAccount.annotations $root | nindent 4 | trim }} + {{- end }} + {{- end }} +{{- with $root.Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/mesh-gateway-clusterrole.yaml b/charts/hashicorp/consul/0.49.0/templates/mesh-gateway-clusterrole.yaml new file mode 100644 index 000000000..b951418b2 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/mesh-gateway-clusterrole.yaml @@ -0,0 +1,34 @@ +{{- if .Values.meshGateway.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "consul.fullname" . }}-mesh-gateway + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: mesh-gateway +{{- if or .Values.global.acls.manageSystemACLs .Values.global.enablePodSecurityPolicies (eq .Values.meshGateway.wanAddress.source "Service") }} +rules: +{{- if .Values.global.enablePodSecurityPolicies }} + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" . }}-mesh-gateway + verbs: + - use +{{- end }} +{{- if eq .Values.meshGateway.wanAddress.source "Service" }} + - apiGroups: [""] + resources: + - services + resourceNames: + - {{ template "consul.fullname" . }}-mesh-gateway + verbs: + - get + {{- end }} +{{- else }} +rules: [] +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/mesh-gateway-clusterrolebinding.yaml b/charts/hashicorp/consul/0.49.0/templates/mesh-gateway-clusterrolebinding.yaml new file mode 100644 index 000000000..f8150ebb5 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/mesh-gateway-clusterrolebinding.yaml @@ -0,0 +1,20 @@ +{{- if .Values.meshGateway.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "consul.fullname" . }}-mesh-gateway + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: mesh-gateway +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "consul.fullname" . }}-mesh-gateway +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-mesh-gateway + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/mesh-gateway-deployment.yaml b/charts/hashicorp/consul/0.49.0/templates/mesh-gateway-deployment.yaml new file mode 100644 index 000000000..ffe0036de --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/mesh-gateway-deployment.yaml @@ -0,0 +1,423 @@ +{{- if .Values.meshGateway.enabled }} +{{- if not .Values.connectInject.enabled }}{{ fail "connectInject.enabled must be true" }}{{ end -}} +{{- if not .Values.client.grpc }}{{ fail "client.grpc must be true" }}{{ end -}} +{{- if and .Values.global.acls.manageSystemACLs (ne .Values.meshGateway.consulServiceName "") (ne .Values.meshGateway.consulServiceName "mesh-gateway") }}{{ fail "if global.acls.manageSystemACLs is true, meshGateway.consulServiceName cannot be set" }}{{ end -}} +{{- if .Values.meshGateway.imageEnvoy }}{{ fail "meshGateway.imageEnvoy must be specified in global.imageEnvoy" }}{{ end -}} +{{- if .Values.meshGateway.globalMode }}{{ fail "meshGateway.globalMode is no longer supported; instead, you must migrate to CRDs (see www.consul.io/docs/k8s/crds/upgrade-to-crds)" }}{{ end -}} +{{- if .Values.global.lifecycleSidecarContainer }}{{ fail "global.lifecycleSidecarContainer has been renamed to global.consulSidecarContainer. Please set values using global.consulSidecarContainer." }}{{ end }} +{{- /* The below test checks if clients are disabled (and if so, fails). We use the conditional from other client files and prepend 'not' */ -}} +{{- if not (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }}{{ fail "clients must be enabled" }}{{ end -}} +{{- if and .Values.global.adminPartitions.enabled (not .Values.global.enableConsulNamespaces) }}{{ fail "global.enableConsulNamespaces must be true if global.adminPartitions.enabled=true" }}{{ end }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "consul.fullname" . }}-mesh-gateway + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: mesh-gateway +spec: + replicas: {{ .Values.meshGateway.replicas }} + selector: + matchLabels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: mesh-gateway + template: + metadata: + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: mesh-gateway + annotations: + "consul.hashicorp.com/connect-inject": "false" + {{- if (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) }} + "vault.hashicorp.com/agent-init-first": "true" + "vault.hashicorp.com/agent-inject": "true" + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.consulCARole }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" . }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": "{{ .Values.global.secretsBackend.vault.ca.secretName }}" + "vault.hashicorp.com/ca-cert": "/vault/custom/{{ .Values.global.secretsBackend.vault.ca.secretKey }}" + {{- end }} + {{- if .Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl .Values.global.secretsBackend.vault.agentAnnotations . | nindent 8 | trim }} + {{- end }} + {{- end }} + {{- if (and .Values.global.metrics.enabled .Values.global.metrics.enableGatewayMetrics) }} + "prometheus.io/scrape": "true" + "prometheus.io/path": "/metrics" + "prometheus.io/port": "20200" + {{- end }} + {{- if .Values.meshGateway.annotations }} + {{- tpl .Values.meshGateway.annotations . | nindent 8 }} + {{- end }} + spec: + {{- if .Values.meshGateway.affinity }} + affinity: + {{ tpl .Values.meshGateway.affinity . | nindent 8 | trim }} + {{- end }} + {{- if .Values.meshGateway.tolerations }} + tolerations: + {{ tpl .Values.meshGateway.tolerations . | nindent 8 | trim }} + {{- end }} + {{- if .Values.meshGateway.topologySpreadConstraints }} + topologySpreadConstraints: + {{ tpl .Values.meshGateway.topologySpreadConstraints . | nindent 8 | trim }} + {{- end }} + terminationGracePeriodSeconds: 10 + serviceAccountName: {{ template "consul.fullname" . }}-mesh-gateway + volumes: + - name: consul-bin + emptyDir: {} + - name: consul-service + emptyDir: + medium: "Memory" + {{- if .Values.global.tls.enabled }} + {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + {{- if .Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + emptyDir: + medium: "Memory" + {{- end }} + {{- end }} + {{- if .Values.meshGateway.hostNetwork }} + hostNetwork: {{ .Values.meshGateway.hostNetwork }} + {{- end }} + {{- if .Values.meshGateway.dnsPolicy }} + dnsPolicy: {{ .Values.meshGateway.dnsPolicy }} + {{- end }} + initContainers: + # We use the Envoy image as our base image so we use an init container to + # copy the Consul binary to a shared directory that can be used when + # starting Envoy. + - name: copy-consul-bin + image: {{ .Values.global.image | quote }} + command: + - cp + - /bin/consul + - /consul-bin/consul + volumeMounts: + - name: consul-bin + mountPath: /consul-bin + {{- if .Values.meshGateway.initCopyConsulContainer }} + {{- if .Values.meshGateway.initCopyConsulContainer.resources }} + resources: {{ toYaml .Values.meshGateway.initCopyConsulContainer.resources | nindent 12 }} + {{- end }} + {{- end }} + {{- if (and .Values.global.tls.enabled .Values.global.tls.enableAutoEncrypt) }} + {{- include "consul.getAutoEncryptClientCA" . | nindent 8 }} + {{- end }} + - name: mesh-gateway-init + image: {{ .Values.global.imageK8S }} + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + {{- if .Values.global.tls.enabled }} + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- end }} + - name: CONSUL_HTTP_ADDR + {{- if .Values.global.tls.enabled }} + value: https://$(HOST_IP):8501 + {{- else }} + value: http://$(HOST_IP):8500 + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + {{- if .Values.global.acls.manageSystemACLs }} + consul-k8s-control-plane acl-init \ + -component-name=mesh-gateway \ + -token-sink-file=/consul/service/acl-token \ + {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter }} + -acl-auth-method={{ template "consul.fullname" . }}-k8s-component-auth-method-{{ .Values.global.datacenter }} \ + -primary-datacenter={{ .Values.global.federation.primaryDatacenter }} \ + {{- else }} + -acl-auth-method={{ template "consul.fullname" . }}-k8s-component-auth-method \ + {{- end }} + {{- if .Values.global.adminPartitions.enabled }} + -partition={{ .Values.global.adminPartitions.name }} \ + {{- end }} + -consul-api-timeout={{ .Values.global.consulAPITimeout }} \ + -log-level={{ default .Values.global.logLevel }} \ + -log-json={{ .Values.global.logJSON }} + {{ end }} + + {{- $source := .Values.meshGateway.wanAddress.source }} + {{- $serviceType := .Values.meshGateway.service.type }} + {{- if and (eq $source "Service") (not .Values.meshGateway.service.enabled) }}{{ fail "if meshGateway.wanAddress.source=Service then meshGateway.service.enabled must be set to true" }}{{ end }} + {{- if or (eq $source "NodeIP") (and (eq $source "Service") (eq $serviceType "NodePort")) }} + WAN_ADDR="${HOST_IP}" + {{- else if eq $source "NodeName" }} + WAN_ADDR="${NODE_NAME}" + {{- else if and (eq $source "Service") (or (eq $serviceType "ClusterIP") (eq $serviceType "LoadBalancer")) }} + consul-k8s-control-plane service-address \ + -log-level={{ .Values.global.logLevel }} \ + -log-json={{ .Values.global.logJSON }} \ + -k8s-namespace={{ .Release.Namespace }} \ + -name={{ template "consul.fullname" . }}-mesh-gateway \ + -output-file=/tmp/address.txt + WAN_ADDR="$(cat /tmp/address.txt)" + {{- else if eq $source "Static" }} + {{- if eq .Values.meshGateway.wanAddress.static "" }}{{ fail "if meshGateway.wanAddress.source=Static then meshGateway.wanAddress.static cannot be empty" }}{{ end }} + WAN_ADDR="{{ .Values.meshGateway.wanAddress.static }}" + {{- else }} + {{- fail "currently set meshGateway values for wanAddress.source and service.type are not supported" }} + {{- end }} + + {{- if eq $source "Service" }} + {{- if eq $serviceType "NodePort" }} + {{- if not .Values.meshGateway.service.nodePort }}{{ fail "if meshGateway.wanAddress.source=Service and meshGateway.service.type=NodePort, meshGateway.service.nodePort must be set" }}{{ end }} + WAN_PORT="{{ .Values.meshGateway.service.nodePort }}" + {{- else }} + WAN_PORT="{{ .Values.meshGateway.service.port }}" + {{- end }} + {{- else }} + WAN_PORT="{{ .Values.meshGateway.wanAddress.port }}" + {{- end }} + + cat > /consul/service/service.hcl << EOF + service { + kind = "mesh-gateway" + name = "{{ .Values.meshGateway.consulServiceName }}" + {{- if .Values.global.federation.enabled }} + meta { + consul-wan-federation = "1" + } + {{- end }} + {{- if (and .Values.global.metrics.enabled .Values.global.metrics.enableGatewayMetrics) }} + proxy { config { envoy_prometheus_bind_addr = "${POD_IP}:20200" } } + {{- end }} + port = {{ .Values.meshGateway.containerPort }} + address = "${POD_IP}" + {{- if .Values.global.adminPartitions.enabled }} + partition = "{{ .Values.global.adminPartitions.name }}" + {{- end }} + tagged_addresses { + lan { + address = "${POD_IP}" + port = {{ .Values.meshGateway.containerPort }} + } + wan { + address = "${WAN_ADDR}" + port = ${WAN_PORT} + } + } + checks = [ + { + name = "Mesh Gateway Listening" + interval = "10s" + tcp = "${POD_IP}:{{ .Values.meshGateway.containerPort }}" + deregister_critical_service_after = "6h" + } + ] + } + EOF + + /consul-bin/consul services register \ + {{- if .Values.global.acls.manageSystemACLs }} + -token-file=/consul/service/acl-token \ + {{- end }} + /consul/service/service.hcl + volumeMounts: + - name: consul-service + mountPath: /consul/service + - name: consul-bin + mountPath: /consul-bin + {{- if .Values.global.tls.enabled }} + {{- if .Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + {{- else }} + - name: consul-ca-cert + {{- end }} + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- if .Values.meshGateway.initServiceInitContainer.resources }} + resources: {{ toYaml .Values.meshGateway.initServiceInitContainer.resources | nindent 12 }} + {{- end }} + containers: + - name: mesh-gateway + image: {{ .Values.global.imageEnvoy | quote }} + {{- if .Values.meshGateway.resources }} + resources: + {{- if eq (typeOf .Values.meshGateway.resources) "string" }} + {{ tpl .Values.meshGateway.resources . | nindent 12 | trim }} + {{- else }} + {{- toYaml .Values.meshGateway.resources | nindent 12 }} + {{- end }} + {{- end }} + volumeMounts: + - mountPath: /consul/service + name: consul-service + readOnly: true + - name: consul-bin + mountPath: /consul-bin + {{- if .Values.global.tls.enabled }} + {{- if .Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + {{- else }} + - name: consul-ca-cert + {{- end }} + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + {{- if eq .Values.meshGateway.wanAddress.source "NodeName" }} + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + - name: CONSUL_HTTP_TOKEN_FILE + value: /consul/service/acl-token + {{- end }} + {{- if .Values.global.tls.enabled }} + - name: CONSUL_HTTP_ADDR + value: https://$(HOST_IP):8501 + - name: CONSUL_GRPC_ADDR + value: https://$(HOST_IP):8502 + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- else }} + - name: CONSUL_HTTP_ADDR + value: http://$(HOST_IP):8500 + - name: CONSUL_GRPC_ADDR + value: $(HOST_IP):8502 + {{- end }} + command: + - /consul-bin/consul + - connect + - envoy + - -mesh-gateway + {{- if .Values.global.adminPartitions.enabled }} + - -partition={{ .Values.global.adminPartitions.name }} + {{- end }} + livenessProbe: + tcpSocket: + port: {{ .Values.meshGateway.containerPort }} + failureThreshold: 3 + initialDelaySeconds: 30 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + readinessProbe: + tcpSocket: + port: {{ .Values.meshGateway.containerPort }} + failureThreshold: 3 + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + ports: + - name: gateway + containerPort: {{ .Values.meshGateway.containerPort }} + {{- if .Values.meshGateway.hostPort }} + hostPort: {{ .Values.meshGateway.hostPort }} + {{- end }} + lifecycle: + preStop: + exec: + command: + - "/bin/sh" + - "-ec" + - "/consul-bin/consul services deregister -id=\"{{ .Values.meshGateway.consulServiceName }}\"" + {{- if .Values.global.acls.manageSystemACLs }} + - "/consul-bin/consul logout" + {{- end}} + + # consul-sidecar ensures the mesh gateway is always registered with + # the local Consul agent, even if it loses the initial registration. + - name: consul-sidecar + image: {{ .Values.global.imageK8S }} + volumeMounts: + - name: consul-service + mountPath: /consul/service + readOnly: true + - name: consul-bin + mountPath: /consul-bin + {{- if .Values.global.tls.enabled }} + {{- if .Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + {{- else }} + - name: consul-ca-cert + {{- end }} + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- if .Values.global.consulSidecarContainer }} + {{- if .Values.global.consulSidecarContainer.resources }} + resources: {{ toYaml .Values.global.consulSidecarContainer.resources | nindent 12 }} + {{- end }} + {{- end }} + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + {{- if .Values.global.tls.enabled }} + - name: CONSUL_HTTP_ADDR + value: https://$(HOST_IP):8501 + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- else }} + - name: CONSUL_HTTP_ADDR + value: http://$(HOST_IP):8500 + {{- end }} + command: + - consul-k8s-control-plane + - consul-sidecar + - -log-level={{ .Values.global.logLevel }} + - -log-json={{ .Values.global.logJSON }} + - -service-config=/consul/service/service.hcl + - -consul-binary=/consul-bin/consul + - -consul-api-timeout={{ .Values.global.consulAPITimeout }} + {{- if .Values.global.acls.manageSystemACLs }} + - -token-file=/consul/service/acl-token + {{- end }} + {{- if .Values.meshGateway.priorityClassName }} + priorityClassName: {{ .Values.meshGateway.priorityClassName | quote }} + {{- end }} + {{- if .Values.meshGateway.nodeSelector }} + nodeSelector: + {{ tpl .Values.meshGateway.nodeSelector . | indent 8 | trim }} + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/mesh-gateway-podsecuritypolicy.yaml b/charts/hashicorp/consul/0.49.0/templates/mesh-gateway-podsecuritypolicy.yaml new file mode 100644 index 000000000..b5bbb2fa0 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/mesh-gateway-podsecuritypolicy.yaml @@ -0,0 +1,52 @@ +{{- if and .Values.global.enablePodSecurityPolicies .Values.meshGateway.enabled }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-mesh-gateway + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: mesh-gateway +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + {{- if .Values.meshGateway.hostNetwork }} + hostNetwork: {{ .Values.meshGateway.hostNetwork }} + {{- else }} + hostNetwork: false + {{- end }} + hostPorts: + {{- if .Values.meshGateway.hostPort }} + - min: {{ .Values.meshGateway.hostPort }} + max: {{ .Values.meshGateway.hostPort }} + {{- else if .Values.meshGateway.hostNetwork }} + - min: {{ .Values.meshGateway.containerPort }} + max: {{ .Values.meshGateway.containerPort }} + {{- end }} + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/mesh-gateway-service.yaml b/charts/hashicorp/consul/0.49.0/templates/mesh-gateway-service.yaml new file mode 100644 index 000000000..7bd7ec2ac --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/mesh-gateway-service.yaml @@ -0,0 +1,33 @@ +{{- if and .Values.meshGateway.enabled .Values.meshGateway.service.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "consul.fullname" . }}-mesh-gateway + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: mesh-gateway + {{- if .Values.meshGateway.service.annotations }} + annotations: + {{ tpl .Values.meshGateway.service.annotations . | nindent 4 | trim }} + {{- end }} +spec: + selector: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + component: mesh-gateway + ports: + - name: gateway + port: {{ .Values.meshGateway.service.port }} + targetPort: {{ .Values.meshGateway.containerPort }} + {{- if .Values.meshGateway.service.nodePort }} + nodePort: {{ .Values.meshGateway.service.nodePort }} + {{- end}} + type: {{ .Values.meshGateway.service.type }} + {{- if .Values.meshGateway.service.additionalSpec }} + {{ tpl .Values.meshGateway.service.additionalSpec . | nindent 2 | trim }} + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/mesh-gateway-serviceaccount.yaml b/charts/hashicorp/consul/0.49.0/templates/mesh-gateway-serviceaccount.yaml new file mode 100644 index 000000000..8c2da5ae0 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/mesh-gateway-serviceaccount.yaml @@ -0,0 +1,23 @@ +{{- if .Values.meshGateway.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-mesh-gateway + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: mesh-gateway + {{- if .Values.meshGateway.serviceAccount.annotations }} + annotations: + {{ tpl .Values.meshGateway.serviceAccount.annotations . | nindent 4 | trim }} + {{- end }} +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/partition-init-job.yaml b/charts/hashicorp/consul/0.49.0/templates/partition-init-job.yaml new file mode 100644 index 000000000..4d6d97174 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/partition-init-job.yaml @@ -0,0 +1,138 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (and .Values.global.adminPartitions.enabled (not $serverEnabled) (ne .Values.global.adminPartitions.name "default")) }} +{{- template "consul.reservedNamesFailer" (list .Values.global.adminPartitions.name "global.adminPartitions.name") }} +{{- if and (not .Values.externalServers.enabled) (ne .Values.global.adminPartitions.name "default") }}{{ fail "externalServers.enabled needs to be true and configured to create a non-default partition." }}{{ end -}} +{{- if and .Values.global.secretsBackend.vault.enabled .Values.global.acls.manageSystemACLs (not .Values.global.secretsBackend.vault.adminPartitionsRole) }}{{ fail "global.secretsBackend.vault.adminPartitionsRole is required when global.secretsBackend.vault.enabled and global.acls.manageSystemACLs are true." }}{{ end -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "consul.fullname" . }}-partition-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: partition-init + annotations: + "helm.sh/hook": pre-install + "helm.sh/hook-weight": "2" + "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation +spec: + template: + metadata: + name: {{ template "consul.fullname" . }}-partition-init + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: partition-init + annotations: + "consul.hashicorp.com/connect-inject": "false" + {{- if (and .Values.global.secretsBackend.vault.enabled (or .Values.global.tls.enabled .Values.global.acls.manageSystemACLs)) }} + "vault.hashicorp.com/agent-pre-populate-only": "true" + "vault.hashicorp.com/agent-inject": "true" + {{- if .Values.global.acls.manageSystemACLs }} + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.adminPartitionsRole }} + {{- if .Values.global.acls.bootstrapToken.secretName }} + {{- with .Values.global.acls.bootstrapToken }} + "vault.hashicorp.com/agent-inject-secret-bootstrap-token": "{{ .secretName }}" + "vault.hashicorp.com/agent-inject-template-bootstrap-token": {{ template "consul.vaultSecretTemplate" . }} + {{- end }} + {{- end }} + {{- else }} + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.consulCARole }} + {{- end }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" . }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": "{{ .Values.global.secretsBackend.vault.ca.secretName }}" + "vault.hashicorp.com/ca-cert": "/vault/custom/{{ .Values.global.secretsBackend.vault.ca.secretKey }}" + {{- end }} + {{- if .Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl .Values.global.secretsBackend.vault.agentAnnotations . | nindent 8 | trim }} + {{- end }} + {{- end }} + spec: + restartPolicy: Never + serviceAccountName: {{ template "consul.fullname" . }}-partition-init + {{- if .Values.global.tls.enabled }} + {{- if not (or .Values.externalServers.useSystemRoots .Values.global.secretsBackend.vault.enabled) }} + volumes: + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + {{- end }} + containers: + - name: partition-init-job + image: {{ .Values.global.imageK8S }} + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- if (and .Values.global.acls.bootstrapToken.secretName .Values.global.acls.bootstrapToken.secretKey) }} + {{- if .Values.global.secretsBackend.vault.enabled }} + - name: CONSUL_HTTP_TOKEN_FILE + value: /vault/secrets/bootstrap-token + {{- else }} + - name: CONSUL_HTTP_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Values.global.acls.bootstrapToken.secretName }} + key: {{ .Values.global.acls.bootstrapToken.secretKey }} + {{- end }} + {{- end }} + {{- if .Values.global.tls.enabled }} + {{- if not (or .Values.externalServers.useSystemRoots .Values.global.secretsBackend.vault.enabled) }} + volumeMounts: + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + consul-k8s-control-plane partition-init \ + -consul-api-timeout={{ .Values.global.consulAPITimeout }} \ + -log-level={{ .Values.global.logLevel }} \ + -log-json={{ .Values.global.logJSON }} \ + + {{- if and .Values.externalServers.enabled (not .Values.externalServers.hosts) }}{{ fail "externalServers.hosts must be set if externalServers.enabled is true" }}{{ end -}} + {{- range .Values.externalServers.hosts }} + -server-address={{ quote . }} \ + {{- end }} + -server-port={{ .Values.externalServers.httpsPort }} \ + + {{- if .Values.global.tls.enabled }} + -use-https \ + {{- if not .Values.externalServers.useSystemRoots }} + {{- if .Values.global.secretsBackend.vault.enabled }} + -ca-file=/vault/secrets/serverca.crt \ + {{- else }} + -ca-file=/consul/tls/ca/tls.crt \ + {{- end }} + {{- end }} + {{- if .Values.externalServers.tlsServerName }} + -tls-server-name={{ .Values.externalServers.tlsServerName }} \ + {{- end }} + {{- end }} + -partition-name={{ .Values.global.adminPartitions.name }} + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/partition-init-podsecuritypolicy.yaml b/charts/hashicorp/consul/0.49.0/templates/partition-init-podsecuritypolicy.yaml new file mode 100644 index 000000000..8590a691d --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/partition-init-podsecuritypolicy.yaml @@ -0,0 +1,40 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (and .Values.global.adminPartitions.enabled (not $serverEnabled)) }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-partition-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: partition-init + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +spec: + privileged: false + # Allow core volume types. + volumes: + - 'secret' + - 'emptyDir' + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/partition-init-role.yaml b/charts/hashicorp/consul/0.49.0/templates/partition-init-role.yaml new file mode 100644 index 000000000..c13a5378e --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/partition-init-role.yaml @@ -0,0 +1,41 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (and .Values.global.adminPartitions.enabled (not $serverEnabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-partition-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: partition-init + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +rules: + - apiGroups: [""] + resources: + - secrets + verbs: + - create + - get +{{- if .Values.connectInject.enabled }} + - apiGroups: [""] + resources: + - serviceaccounts + resourceNames: + - {{ template "consul.fullname" . }}-connect-injector + verbs: + - get +{{- end }} +{{- if .Values.global.enablePodSecurityPolicies }} + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" . }}-partition-init + verbs: + - use +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/partition-init-rolebinding.yaml b/charts/hashicorp/consul/0.49.0/templates/partition-init-rolebinding.yaml new file mode 100644 index 000000000..432d6df6e --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/partition-init-rolebinding.yaml @@ -0,0 +1,24 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (and .Values.global.adminPartitions.enabled (not $serverEnabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-partition-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: partition-init + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-partition-init +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-partition-init +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/partition-init-serviceaccount.yaml b/charts/hashicorp/consul/0.49.0/templates/partition-init-serviceaccount.yaml new file mode 100644 index 000000000..65fcf43b0 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/partition-init-serviceaccount.yaml @@ -0,0 +1,23 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (and .Values.global.adminPartitions.enabled (not $serverEnabled)) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-partition-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: partition-init + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/partition-name-configmap.yaml b/charts/hashicorp/consul/0.49.0/templates/partition-name-configmap.yaml new file mode 100644 index 000000000..ee330b0f4 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/partition-name-configmap.yaml @@ -0,0 +1,19 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (and .Values.global.adminPartitions.enabled (not $serverEnabled)) }} +# Immutable ConfigMap which saves the partition name. Attempting to update this configmap +# with a new Admin Partition name will cause the helm upgrade to fail +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "consul.fullname" . }}-partition + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: partition-init +immutable: true +data: + partitionName: {{ .Values.global.adminPartitions.name }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/partition-service.yaml b/charts/hashicorp/consul/0.49.0/templates/partition-service.yaml new file mode 100644 index 000000000..b9266a11c --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/partition-service.yaml @@ -0,0 +1,45 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (and .Values.global.adminPartitions.enabled $serverEnabled) }} +# Service with an external IP for clients in non-default Admin Partitions +# to discover Consul servers. This service should only point to Consul servers. +apiVersion: v1 +kind: Service +metadata: + name: {{ template "consul.fullname" . }}-partition + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server + annotations: + {{- if .Values.global.adminPartitions.service.annotations }} + {{ tpl .Values.global.adminPartitions.service.annotations . | nindent 4 | trim }} + {{- end }} +spec: + type: "{{ .Values.global.adminPartitions.service.type }}" + ports: + - name: https + port: 8501 + targetPort: 8501 + {{ if (and (eq .Values.global.adminPartitions.service.type "NodePort") .Values.global.adminPartitions.service.nodePort.https) }} + nodePort: {{ .Values.global.adminPartitions.service.nodePort.https }} + {{- end }} + - name: serflan + port: 8301 + targetPort: 8301 + {{ if (and (eq .Values.global.adminPartitions.service.type "NodePort") .Values.global.adminPartitions.service.nodePort.serf) }} + nodePort: {{ .Values.global.adminPartitions.service.nodePort.serf }} + {{- end }} + - name: server + port: 8300 + targetPort: 8300 + {{ if (and (eq .Values.global.adminPartitions.service.type "NodePort") .Values.global.adminPartitions.service.nodePort.rpc) }} + nodePort: {{ .Values.global.adminPartitions.service.nodePort.rpc }} + {{- end }} + selector: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + component: server +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/prometheus.yaml b/charts/hashicorp/consul/0.49.0/templates/prometheus.yaml new file mode 100644 index 000000000..4dcede174 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/prometheus.yaml @@ -0,0 +1,488 @@ +{{- if .Values.prometheus.enabled }} +# This file is auto-generated, see addons/gen.sh +--- +# Source: prometheus/templates/server/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.2.1 + heritage: Helm + name: prometheus-server + namespace: {{ .Release.Namespace }} + annotations: + {} +--- +# Source: prometheus/templates/server/cm.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.2.1 + heritage: Helm + name: prometheus-server + namespace: {{ .Release.Namespace }} +data: + alerting_rules.yml: | + {} + alerts: | + {} + prometheus.yml: | + global: + evaluation_interval: 1m + scrape_interval: 15s + scrape_timeout: 10s + rule_files: + - /etc/config/recording_rules.yml + - /etc/config/alerting_rules.yml + - /etc/config/rules + - /etc/config/alerts + scrape_configs: + - job_name: prometheus + static_configs: + - targets: + - localhost:9090 + - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + job_name: kubernetes-apiservers + kubernetes_sd_configs: + - role: endpoints + relabel_configs: + - action: keep + regex: default;kubernetes;https + source_labels: + - __meta_kubernetes_namespace + - __meta_kubernetes_service_name + - __meta_kubernetes_endpoint_port_name + scheme: https + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + insecure_skip_verify: true + - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + job_name: kubernetes-nodes + kubernetes_sd_configs: + - role: node + relabel_configs: + - action: labelmap + regex: __meta_kubernetes_node_label_(.+) + - replacement: kubernetes.default.svc:443 + target_label: __address__ + - regex: (.+) + replacement: /api/v1/nodes/$1/proxy/metrics + source_labels: + - __meta_kubernetes_node_name + target_label: __metrics_path__ + scheme: https + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + insecure_skip_verify: true + - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + job_name: kubernetes-nodes-cadvisor + kubernetes_sd_configs: + - role: node + relabel_configs: + - action: labelmap + regex: __meta_kubernetes_node_label_(.+) + - replacement: kubernetes.default.svc:443 + target_label: __address__ + - regex: (.+) + replacement: /api/v1/nodes/$1/proxy/metrics/cadvisor + source_labels: + - __meta_kubernetes_node_name + target_label: __metrics_path__ + scheme: https + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + insecure_skip_verify: true + - job_name: kubernetes-service-endpoints + kubernetes_sd_configs: + - role: endpoints + relabel_configs: + - action: keep + regex: true + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_scrape + - action: replace + regex: (https?) + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_scheme + target_label: __scheme__ + - action: replace + regex: (.+) + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_path + target_label: __metrics_path__ + - action: replace + regex: ([^:]+)(?::\d+)?;(\d+) + replacement: $1:$2 + source_labels: + - __address__ + - __meta_kubernetes_service_annotation_prometheus_io_port + target_label: __address__ + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - action: replace + source_labels: + - __meta_kubernetes_namespace + target_label: kubernetes_namespace + - action: replace + source_labels: + - __meta_kubernetes_service_name + target_label: kubernetes_name + - action: replace + source_labels: + - __meta_kubernetes_pod_node_name + target_label: kubernetes_node + - job_name: kubernetes-service-endpoints-slow + kubernetes_sd_configs: + - role: endpoints + relabel_configs: + - action: keep + regex: true + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_scrape_slow + - action: replace + regex: (https?) + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_scheme + target_label: __scheme__ + - action: replace + regex: (.+) + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_path + target_label: __metrics_path__ + - action: replace + regex: ([^:]+)(?::\d+)?;(\d+) + replacement: $1:$2 + source_labels: + - __address__ + - __meta_kubernetes_service_annotation_prometheus_io_port + target_label: __address__ + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - action: replace + source_labels: + - __meta_kubernetes_namespace + target_label: kubernetes_namespace + - action: replace + source_labels: + - __meta_kubernetes_service_name + target_label: kubernetes_name + - action: replace + source_labels: + - __meta_kubernetes_pod_node_name + target_label: kubernetes_node + scrape_interval: 5m + scrape_timeout: 30s + - honor_labels: true + job_name: prometheus-pushgateway + kubernetes_sd_configs: + - role: service + relabel_configs: + - action: keep + regex: pushgateway + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_probe + - job_name: kubernetes-services + kubernetes_sd_configs: + - role: service + metrics_path: /probe + params: + module: + - http_2xx + relabel_configs: + - action: keep + regex: true + source_labels: + - __meta_kubernetes_service_annotation_prometheus_io_probe + - source_labels: + - __address__ + target_label: __param_target + - replacement: blackbox + target_label: __address__ + - source_labels: + - __param_target + target_label: instance + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - source_labels: + - __meta_kubernetes_namespace + target_label: kubernetes_namespace + - source_labels: + - __meta_kubernetes_service_name + target_label: kubernetes_name + - job_name: kubernetes-pods + kubernetes_sd_configs: + - role: pod + relabel_configs: + - action: keep + regex: true + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_scrape + - action: replace + regex: (https?) + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_scheme + target_label: __scheme__ + - action: replace + regex: (.+) + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_path + target_label: __metrics_path__ + - action: replace + regex: ([^:]+)(?::\d+)?;(\d+) + replacement: $1:$2 + source_labels: + - __address__ + - __meta_kubernetes_pod_annotation_prometheus_io_port + target_label: __address__ + - action: labelmap + regex: __meta_kubernetes_pod_label_(.+) + - action: replace + source_labels: + - __meta_kubernetes_namespace + target_label: kubernetes_namespace + - action: replace + source_labels: + - __meta_kubernetes_pod_name + target_label: kubernetes_pod_name + - action: drop + regex: Pending|Succeeded|Failed + source_labels: + - __meta_kubernetes_pod_phase + - job_name: kubernetes-pods-slow + kubernetes_sd_configs: + - role: pod + relabel_configs: + - action: keep + regex: true + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_scrape_slow + - action: replace + regex: (https?) + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_scheme + target_label: __scheme__ + - action: replace + regex: (.+) + source_labels: + - __meta_kubernetes_pod_annotation_prometheus_io_path + target_label: __metrics_path__ + - action: replace + regex: ([^:]+)(?::\d+)?;(\d+) + replacement: $1:$2 + source_labels: + - __address__ + - __meta_kubernetes_pod_annotation_prometheus_io_port + target_label: __address__ + - action: labelmap + regex: __meta_kubernetes_pod_label_(.+) + - action: replace + source_labels: + - __meta_kubernetes_namespace + target_label: kubernetes_namespace + - action: replace + source_labels: + - __meta_kubernetes_pod_name + target_label: kubernetes_pod_name + - action: drop + regex: Pending|Succeeded|Failed + source_labels: + - __meta_kubernetes_pod_phase + scrape_interval: 5m + scrape_timeout: 30s + recording_rules.yml: | + {} + rules: | + {} +--- +# Source: prometheus/templates/server/clusterrole.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.2.1 + heritage: Helm + name: prometheus-server +rules: + - apiGroups: + - "" + resources: + - nodes + - nodes/proxy + - nodes/metrics + - services + - endpoints + - pods + - ingresses + - configmaps + verbs: + - get + - list + - watch + - apiGroups: + - "extensions" + - "networking.k8s.io" + resources: + - ingresses/status + - ingresses + verbs: + - get + - list + - watch + - nonResourceURLs: + - "/metrics" + verbs: + - get +--- +# Source: prometheus/templates/server/clusterrolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.2.1 + heritage: Helm + name: prometheus-server +subjects: + - kind: ServiceAccount + name: prometheus-server + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: prometheus-server +--- +# Source: prometheus/templates/server/service.yaml +apiVersion: v1 +kind: Service +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.2.1 + heritage: Helm + name: prometheus-server + namespace: {{ .Release.Namespace }} +spec: + ports: + - name: http + port: 80 + protocol: TCP + targetPort: 9090 + selector: + component: "server" + app: prometheus + release: prometheus + sessionAffinity: None + type: "ClusterIP" +--- +# Source: prometheus/templates/server/deploy.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.2.1 + heritage: Helm + name: prometheus-server + namespace: {{ .Release.Namespace }} +spec: + selector: + matchLabels: + component: "server" + app: prometheus + release: prometheus + replicas: 1 + template: + metadata: + annotations: + + consul.hashicorp.com/connect-inject: "false" + labels: + component: "server" + app: prometheus + release: prometheus + chart: prometheus-13.2.1 + heritage: Helm + spec: + serviceAccountName: prometheus-server + containers: + - name: prometheus-server-configmap-reload + image: "jimmidyson/configmap-reload:v0.4.0" + imagePullPolicy: "IfNotPresent" + args: + - --volume-dir=/etc/config + - --webhook-url=http://127.0.0.1:9090/-/reload + resources: + {} + volumeMounts: + - name: config-volume + mountPath: /etc/config + readOnly: true + + - name: prometheus-server + image: "quay.io/prometheus/prometheus:v2.24.0" + imagePullPolicy: "IfNotPresent" + args: + - --storage.tsdb.retention.time=15d + - --config.file=/etc/config/prometheus.yml + - --storage.tsdb.path=/data + - --web.console.libraries=/etc/prometheus/console_libraries + - --web.console.templates=/etc/prometheus/consoles + - --web.enable-lifecycle + ports: + - containerPort: 9090 + readinessProbe: + httpGet: + path: /-/ready + port: 9090 + initialDelaySeconds: 0 + periodSeconds: 5 + timeoutSeconds: 4 + failureThreshold: 3 + successThreshold: 1 + livenessProbe: + httpGet: + path: /-/healthy + port: 9090 + initialDelaySeconds: 30 + periodSeconds: 15 + timeoutSeconds: 10 + failureThreshold: 3 + successThreshold: 1 + resources: + {} + volumeMounts: + - name: config-volume + mountPath: /etc/config + - name: storage-volume + mountPath: /data + subPath: "" + securityContext: + fsGroup: 65534 + runAsGroup: 65534 + runAsNonRoot: true + runAsUser: 65534 + terminationGracePeriodSeconds: 300 + volumes: + - name: config-volume + configMap: + name: prometheus-server + - name: storage-volume + emptyDir: + {} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/server-acl-init-cleanup-job.yaml b/charts/hashicorp/consul/0.49.0/templates/server-acl-init-cleanup-job.yaml new file mode 100644 index 000000000..4db5e356e --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/server-acl-init-cleanup-job.yaml @@ -0,0 +1,67 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (or $serverEnabled .Values.externalServers.enabled) }} +{{- if .Values.global.acls.manageSystemACLs }} +{{- /* See reason for this in server-acl-init-job.yaml */ -}} +{{- if eq (int .Values.server.updatePartition) 0 }} +# This job deletes the server-acl-init job once it completes successfully. +# It runs as a helm hook because it only needs to run when the server-acl-init +# Job gets recreated which only happens during an install or upgrade. +# We also utilize the helm hook-delete-policy to delete this job itself. +# We want to delete the server-acl-init job because once it runs successfully +# it's not needed and also because if it stays around then when users run +# helm upgrade with values that change the spec of the job, Kubernetes errors +# because the job spec is immutable. If the job is deleted, then a new job +# is created and there's no error. +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "consul.fullname" . }}-server-acl-init-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server-acl-init-cleanup + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-weight": "0" + # If the hook fails then all that happens is we didn't delete the job. + # There's no reason for *this* job to stay around in that case so delete + # regardless of success. + "helm.sh/hook-delete-policy": hook-succeeded,hook-failed +spec: + template: + metadata: + name: {{ template "consul.fullname" . }}-server-acl-init-cleanup + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: server-acl-init-cleanup + annotations: + "consul.hashicorp.com/connect-inject": "false" + spec: + restartPolicy: Never + serviceAccountName: {{ template "consul.fullname" . }}-server-acl-init-cleanup + containers: + - name: server-acl-init-cleanup + image: {{ .Values.global.imageK8S }} + command: + - consul-k8s-control-plane + args: + - delete-completed-job + - -log-level={{ .Values.global.logLevel }} + - -log-json={{ .Values.global.logJSON }} + - -k8s-namespace={{ .Release.Namespace }} + - {{ template "consul.fullname" . }}-server-acl-init + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + {{- end }} + {{- end }} + {{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/server-acl-init-cleanup-podsecuritypolicy.yaml b/charts/hashicorp/consul/0.49.0/templates/server-acl-init-cleanup-podsecuritypolicy.yaml new file mode 100644 index 000000000..dd5dad24d --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/server-acl-init-cleanup-podsecuritypolicy.yaml @@ -0,0 +1,40 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (or $serverEnabled .Values.externalServers.enabled) }} +{{- if .Values.global.acls.manageSystemACLs }} +{{- if .Values.global.enablePodSecurityPolicies }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-server-acl-init-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server-acl-init-cleanup +spec: + privileged: false + # Allow core volume types. + volumes: + - 'secret' + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false + {{- end }} + {{- end }} + {{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/server-acl-init-cleanup-role.yaml b/charts/hashicorp/consul/0.49.0/templates/server-acl-init-cleanup-role.yaml new file mode 100644 index 000000000..0a2f296a6 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/server-acl-init-cleanup-role.yaml @@ -0,0 +1,28 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (or $serverEnabled .Values.externalServers.enabled) }} +{{- if .Values.global.acls.manageSystemACLs }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-server-acl-init-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server-acl-init-cleanup +rules: + - apiGroups: ["batch"] + resources: ["jobs"] + verbs: ["get", "delete"] +{{- if .Values.global.enablePodSecurityPolicies }} + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" . }}-server-acl-init-cleanup + verbs: + - use +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/server-acl-init-cleanup-rolebinding.yaml b/charts/hashicorp/consul/0.49.0/templates/server-acl-init-cleanup-rolebinding.yaml new file mode 100644 index 000000000..268eaa567 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/server-acl-init-cleanup-rolebinding.yaml @@ -0,0 +1,23 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (or $serverEnabled .Values.externalServers.enabled) }} +{{- if .Values.global.acls.manageSystemACLs }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-server-acl-init-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server-acl-init-cleanup +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-server-acl-init-cleanup +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-server-acl-init-cleanup +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/server-acl-init-cleanup-serviceaccount.yaml b/charts/hashicorp/consul/0.49.0/templates/server-acl-init-cleanup-serviceaccount.yaml new file mode 100644 index 000000000..604e6d784 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/server-acl-init-cleanup-serviceaccount.yaml @@ -0,0 +1,22 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (or $serverEnabled .Values.externalServers.enabled) }} +{{- if .Values.global.acls.manageSystemACLs }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-server-acl-init-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server-acl-init-cleanup +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/server-acl-init-job.yaml b/charts/hashicorp/consul/0.49.0/templates/server-acl-init-job.yaml new file mode 100644 index 000000000..8709b82af --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/server-acl-init-job.yaml @@ -0,0 +1,333 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (and $serverEnabled .Values.externalServers.enabled) }}{{ fail "only one of server.enabled or externalServers.enabled can be set" }}{{ end -}} +{{- if (or $serverEnabled .Values.externalServers.enabled) }} +{{- if and .Values.global.acls.createReplicationToken (not .Values.global.acls.manageSystemACLs) }}{{ fail "if global.acls.createReplicationToken is true, global.acls.manageSystemACLs must be true" }}{{ end -}} +{{- if .Values.global.bootstrapACLs }}{{ fail "global.bootstrapACLs was removed, use global.acls.manageSystemACLs instead" }}{{ end -}} +{{- if .Values.global.acls.manageSystemACLs }} +{{- if or (and .Values.global.acls.bootstrapToken.secretName (not .Values.global.acls.bootstrapToken.secretKey)) (and .Values.global.acls.bootstrapToken.secretKey (not .Values.global.acls.bootstrapToken.secretName))}}{{ fail "both global.acls.bootstrapToken.secretKey and global.acls.bootstrapToken.secretName must be set if one of them is provided" }}{{ end -}} +{{- if or (and .Values.global.acls.replicationToken.secretName (not .Values.global.acls.replicationToken.secretKey)) (and .Values.global.acls.replicationToken.secretKey (not .Values.global.acls.replicationToken.secretName))}}{{ fail "both global.acls.replicationToken.secretKey and global.acls.replicationToken.secretName must be set if one of them is provided" }}{{ end -}} +{{- if (and .Values.global.secretsBackend.vault.enabled (and (not .Values.global.acls.bootstrapToken.secretName) (not .Values.global.acls.replicationToken.secretName ))) }}{{fail "global.acls.bootstrapToken or global.acls.replicationToken must be provided when global.secretsBackend.vault.enabled and global.acls.manageSystemACLs are true" }}{{ end -}} +{{- if (and .Values.global.secretsBackend.vault.enabled (not .Values.global.secretsBackend.vault.manageSystemACLsRole)) }}{{fail "global.secretsBackend.vault.manageSystemACLsRole is required when global.secretsBackend.vault.enabled and global.acls.manageSystemACLs are true" }}{{ end -}} + {{- /* We don't render this job when server.updatePartition > 0 because that + means a server rollout is in progress and this job won't complete unless + the rollout is finished (which won't happen until the partition is 0). + If we ran it in this case, then the job would not complete which would cause + the server-acl-init-cleanup hook to run indefinitely which would cause the + helm upgrade command to hang. +*/ -}} +{{- if eq (int .Values.server.updatePartition) 0 }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "consul.fullname" . }}-server-acl-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server-acl-init +spec: + template: + metadata: + name: {{ template "consul.fullname" . }}-server-acl-init + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: server-acl-init + annotations: + "consul.hashicorp.com/connect-inject": "false" + {{- if .Values.global.secretsBackend.vault.enabled }} + "vault.hashicorp.com/agent-pre-populate-only": "true" + "vault.hashicorp.com/agent-inject": "true" + {{- if .Values.global.acls.bootstrapToken.secretName }} + {{- with .Values.global.acls.bootstrapToken }} + "vault.hashicorp.com/agent-inject-secret-bootstrap-token": "{{ .secretName }}" + "vault.hashicorp.com/agent-inject-template-bootstrap-token": {{ template "consul.vaultSecretTemplate" . }} + {{- end }} + {{- end }} + {{- if .Values.global.acls.partitionToken.secretName }} + {{- with .Values.global.acls.partitionToken }} + "vault.hashicorp.com/agent-inject-secret-partition-token": "{{ .secretName }}" + "vault.hashicorp.com/agent-inject-template-partition-token": {{ template "consul.vaultSecretTemplate" . }} + {{- end }} + {{- end }} + {{- if .Values.global.tls.enabled }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" . }} + {{- end }} + {{- if .Values.global.secretsBackend.vault.manageSystemACLsRole }} + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.manageSystemACLsRole }} + {{- else if .Values.global.tls.enabled }} + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.consulCARole }} + {{- end }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": "{{ .Values.global.secretsBackend.vault.ca.secretName }}" + "vault.hashicorp.com/ca-cert": "/vault/custom/{{ .Values.global.secretsBackend.vault.ca.secretKey }}" + {{- end }} + {{- if .Values.global.acls.replicationToken.secretName }} + "vault.hashicorp.com/agent-inject-secret-replication-token": "{{ .Values.global.acls.replicationToken.secretName }}" + "vault.hashicorp.com/agent-inject-template-replication-token": {{ template "consul.vaultReplicationTokenTemplate" . }} + {{- end }} + {{- if .Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl .Values.global.secretsBackend.vault.agentAnnotations . | nindent 8 | trim }} + {{- end }} + {{- end }} + spec: + restartPolicy: Never + serviceAccountName: {{ template "consul.fullname" . }}-server-acl-init + {{- if (or .Values.global.tls.enabled .Values.global.acls.replicationToken.secretName .Values.global.acls.bootstrapToken.secretName) }} + volumes: + {{- if and .Values.global.tls.enabled (not .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + {{- if (and .Values.global.acls.bootstrapToken.secretName (not .Values.global.secretsBackend.vault.enabled)) }} + - name: bootstrap-token + secret: + secretName: {{ .Values.global.acls.bootstrapToken.secretName }} + items: + - key: {{ .Values.global.acls.bootstrapToken.secretKey }} + path: bootstrap-token + {{- else if and .Values.global.acls.replicationToken.secretName (not .Values.global.secretsBackend.vault.enabled) }} + - name: acl-replication-token + secret: + secretName: {{ .Values.global.acls.replicationToken.secretName }} + items: + - key: {{ .Values.global.acls.replicationToken.secretKey }} + path: acl-replication-token + {{- end }} + {{- end }} + containers: + - name: post-install-job + image: {{ .Values.global.imageK8S }} + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- if (or .Values.global.tls.enabled .Values.global.acls.replicationToken.secretName .Values.global.acls.bootstrapToken.secretName) }} + volumeMounts: + {{- if and .Values.global.tls.enabled (not .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- if (and .Values.global.acls.bootstrapToken.secretName (not .Values.global.secretsBackend.vault.enabled)) }} + - name: bootstrap-token + mountPath: /consul/acl/tokens + readOnly: true + {{- else if and .Values.global.acls.replicationToken.secretName (not .Values.global.secretsBackend.vault.enabled) }} + - name: acl-replication-token + mountPath: /consul/acl/tokens + readOnly: true + {{- end }} + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + CONSUL_FULLNAME="{{template "consul.fullname" . }}" + + consul-k8s-control-plane server-acl-init \ + -log-level={{ .Values.global.logLevel }} \ + -log-json={{ .Values.global.logJSON }} \ + -resource-prefix=${CONSUL_FULLNAME} \ + -k8s-namespace={{ .Release.Namespace }} \ + -set-server-tokens={{ $serverEnabled }} \ + -consul-api-timeout={{ .Values.global.consulAPITimeout }} \ + + {{- if .Values.externalServers.enabled }} + {{- if and .Values.externalServers.enabled (not .Values.externalServers.hosts) }}{{ fail "externalServers.hosts must be set if externalServers.enabled is true" }}{{ end -}} + {{- range .Values.externalServers.hosts }} + -server-address={{ quote . }} \ + {{- end }} + -server-port={{ .Values.externalServers.httpsPort }} \ + {{- else }} + {{- range $index := until (.Values.server.replicas | int) }} + -server-address="${CONSUL_FULLNAME}-server-{{ $index }}.${CONSUL_FULLNAME}-server.${NAMESPACE}.svc" \ + {{- end }} + {{- end }} + + {{- if .Values.global.tls.enabled }} + -use-https \ + {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} + {{- if .Values.global.secretsBackend.vault.enabled }} + -consul-ca-cert=/vault/secrets/serverca.crt \ + {{- else }} + -consul-ca-cert=/consul/tls/ca/tls.crt \ + {{- end }} + {{- end }} + {{- if not .Values.externalServers.enabled }} + -server-port=8501 \ + {{- end }} + {{- if .Values.externalServers.tlsServerName }} + -consul-tls-server-name={{ .Values.externalServers.tlsServerName }} \ + {{- end }} + {{- end }} + + {{- if .Values.syncCatalog.enabled }} + -sync-catalog=true \ + {{- if .Values.syncCatalog.consulNodeName }} + -sync-consul-node-name={{ .Values.syncCatalog.consulNodeName }} \ + {{- end }} + {{- end }} + {{- if .Values.global.adminPartitions.enabled }} + -enable-partitions=true \ + -partition={{ .Values.global.adminPartitions.name }} \ + {{- end }} + {{- if .Values.global.peering.enabled }} + -enable-peering=true \ + {{- end }} + {{- if (or (and (ne (.Values.dns.enabled | toString) "-") .Values.dns.enabled) (and (eq (.Values.dns.enabled | toString) "-") .Values.global.enabled)) }} + -allow-dns=true \ + {{- end }} + + {{- if (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled)) }} + -connect-inject=true \ + {{- end }} + {{- if and .Values.externalServers.enabled .Values.externalServers.k8sAuthMethodHost }} + -auth-method-host={{ .Values.externalServers.k8sAuthMethodHost }} \ + {{- end }} + + {{- if .Values.global.federation.k8sAuthMethodHost }} + -auth-method-host={{ .Values.global.federation.k8sAuthMethodHost }} \ + {{- end }} + + {{- if .Values.meshGateway.enabled }} + -mesh-gateway=true \ + {{- end }} + + {{- if .Values.ingressGateways.enabled }} + {{- if .Values.global.enableConsulNamespaces }} + {{- $root := . }} + {{- range .Values.ingressGateways.gateways }} + {{- if (or $root.Values.ingressGateways.defaults.consulNamespace .consulNamespace) }} + -ingress-gateway-name="{{ .name }}.{{ (default $root.Values.ingressGateways.defaults.consulNamespace .consulNamespace) }}" \ + {{- else }} + -ingress-gateway-name="{{ .name }}" \ + {{- end }} + {{- end }} + {{- else }} + {{- range .Values.ingressGateways.gateways }} + -ingress-gateway-name="{{ .name }}" \ + {{- end }} + {{- end }} + {{- end }} + + {{- if .Values.terminatingGateways.enabled }} + {{- if .Values.global.enableConsulNamespaces }} + {{- $root := . }} + {{- range .Values.terminatingGateways.gateways }} + {{- if (or $root.Values.terminatingGateways.defaults.consulNamespace .consulNamespace) }} + -terminating-gateway-name="{{ .name }}.{{ (default $root.Values.terminatingGateways.defaults.consulNamespace .consulNamespace) }}" \ + {{- else }} + -terminating-gateway-name="{{ .name }}" \ + {{- end }} + {{- end }} + {{- else }} + {{- range .Values.terminatingGateways.gateways }} + -terminating-gateway-name="{{ .name }}" \ + {{- end }} + {{- end }} + {{- end }} + + {{- if .Values.connectInject.aclBindingRuleSelector }} + -acl-binding-rule-selector={{ .Values.connectInject.aclBindingRuleSelector }} \ + {{- end }} + + {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.secretKey) }} + -create-enterprise-license-token=true \ + {{- end }} + + {{- if .Values.client.snapshotAgent.enabled }} + -snapshot-agent=true \ + {{- end }} + + {{- if not (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} + -client=false \ + {{- end }} + + {{- if .Values.global.acls.createReplicationToken }} + -create-acl-replication-token=true \ + {{- end }} + + {{- if .Values.global.federation.enabled }} + -federation=true \ + {{- end }} + + {{- if .Values.global.acls.bootstrapToken.secretName }} + {{- if .Values.global.secretsBackend.vault.enabled }} + -bootstrap-token-file=/vault/secrets/bootstrap-token \ + {{- else }} + -bootstrap-token-file=/consul/acl/tokens/bootstrap-token \ + {{- end }} + {{- end }} + {{- if .Values.global.acls.replicationToken.secretName }} + {{- if .Values.global.secretsBackend.vault.enabled }} + -acl-replication-token-file=/vault/secrets/replication-token \ + {{- else }} + -acl-replication-token-file=/consul/acl/tokens/acl-replication-token \ + {{- end }} + {{- end }} + {{- if and .Values.global.secretsBackend.vault.enabled .Values.global.acls.partitionToken.secretName }} + -partition-token-file=/vault/secrets/partition-token \ + {{- end }} + + {{- if .Values.controller.enabled }} + -controller=true \ + {{- end }} + + {{- if .Values.apiGateway.enabled }} + -api-gateway-controller=true \ + {{- end }} + + {{- if .Values.global.enableConsulNamespaces }} + -enable-namespaces=true \ + + {{- /* syncCatalog must be enabled to set sync flags */}} + {{- if (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled)) }} + {{- if .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} + -consul-sync-destination-namespace={{ .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} \ + {{- end }} + {{- if .Values.syncCatalog.consulNamespaces.mirroringK8S }} + -enable-sync-k8s-namespace-mirroring=true \ + {{- if .Values.syncCatalog.consulNamespaces.mirroringK8SPrefix }} + -sync-k8s-namespace-mirroring-prefix={{ .Values.syncCatalog.consulNamespaces.mirroringK8SPrefix }} \ + {{- end }} + {{- end }} + {{- end }} + + {{- /* connectInject must be enabled to set inject flags */}} + {{- if (or (and (ne (.Values.connectInject.enabled | toString) "-") .Values.connectInject.enabled) (and (eq (.Values.connectInject.enabled | toString) "-") .Values.global.enabled)) }} + {{- if .Values.connectInject.consulNamespaces.consulDestinationNamespace }} + -consul-inject-destination-namespace={{ .Values.connectInject.consulNamespaces.consulDestinationNamespace }} \ + {{- end }} + {{- if .Values.connectInject.consulNamespaces.mirroringK8S }} + -enable-inject-k8s-namespace-mirroring=true \ + {{- if .Values.connectInject.consulNamespaces.mirroringK8SPrefix }} + -inject-k8s-namespace-mirroring-prefix={{ .Values.connectInject.consulNamespaces.mirroringK8SPrefix }} \ + {{- end }} + {{- end }} + {{- end }} + + {{- end }} + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/server-acl-init-podsecuritypolicy.yaml b/charts/hashicorp/consul/0.49.0/templates/server-acl-init-podsecuritypolicy.yaml new file mode 100644 index 000000000..9bf93e255 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/server-acl-init-podsecuritypolicy.yaml @@ -0,0 +1,41 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (or $serverEnabled .Values.externalServers.enabled) }} +{{- if .Values.global.acls.manageSystemACLs }} +{{- if .Values.global.enablePodSecurityPolicies }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-server-acl-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server-acl-init +spec: + privileged: false + # Allow core volume types. + volumes: + - 'secret' + - 'emptyDir' + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false + {{- end }} + {{- end }} + {{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/server-acl-init-role.yaml b/charts/hashicorp/consul/0.49.0/templates/server-acl-init-role.yaml new file mode 100644 index 000000000..eb7b6a928 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/server-acl-init-role.yaml @@ -0,0 +1,38 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (or $serverEnabled .Values.externalServers.enabled) }} +{{- if .Values.global.acls.manageSystemACLs }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-server-acl-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server-acl-init +rules: +- apiGroups: [ "" ] + resources: + - secrets + verbs: + - create + - get +- apiGroups: [ "" ] + resources: + - serviceaccounts + resourceNames: + - {{ template "consul.fullname" . }}-auth-method + verbs: + - get +{{- if .Values.global.enablePodSecurityPolicies }} +- apiGroups: [ "policy" ] + resources: [ "podsecuritypolicies" ] + resourceNames: + - {{ template "consul.fullname" . }}-server-acl-init + verbs: + - use +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/server-acl-init-rolebinding.yaml b/charts/hashicorp/consul/0.49.0/templates/server-acl-init-rolebinding.yaml new file mode 100644 index 000000000..fda4726d9 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/server-acl-init-rolebinding.yaml @@ -0,0 +1,23 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (or $serverEnabled .Values.externalServers.enabled) }} +{{- if .Values.global.acls.manageSystemACLs }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-server-acl-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server-acl-init +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-server-acl-init +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-server-acl-init +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/server-acl-init-serviceaccount.yaml b/charts/hashicorp/consul/0.49.0/templates/server-acl-init-serviceaccount.yaml new file mode 100644 index 000000000..c0e257de9 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/server-acl-init-serviceaccount.yaml @@ -0,0 +1,22 @@ +{{- $serverEnabled := (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) -}} +{{- if (or $serverEnabled .Values.externalServers.enabled) }} +{{- if .Values.global.acls.manageSystemACLs }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-server-acl-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server-acl-init +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/server-config-configmap.yaml b/charts/hashicorp/consul/0.49.0/templates/server-config-configmap.yaml new file mode 100644 index 000000000..e35311a9c --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/server-config-configmap.yaml @@ -0,0 +1,202 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +# StatefulSet to run the actual Consul server cluster. +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "consul.fullname" . }}-server-config + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server +data: + server.json: | + { + {{- if and .Values.global.secretsBackend.vault.enabled }} + "auto_reload_config": true, + {{- end }} + "bind_addr": "0.0.0.0", + "bootstrap_expect": {{ if .Values.server.bootstrapExpect }}{{ .Values.server.bootstrapExpect }}{{ else }}{{ .Values.server.replicas }}{{ end }}, + "client_addr": "0.0.0.0", + "connect": { + "enabled": {{ .Values.server.connect }} + }, + "datacenter": "{{ .Values.global.datacenter }}", + "data_dir": "/consul/data", + "domain": "{{ .Values.global.domain }}", + "ports": { + "serf_lan": {{ .Values.server.ports.serflan.port }}, + "grpc": 8503 + }, + "recursors": {{ .Values.global.recursors | toJson }}, + "retry_join": ["{{template "consul.fullname" . }}-server.{{ .Release.Namespace }}.svc:{{ .Values.server.ports.serflan.port }}"], + {{- if .Values.global.peering.enabled }} + "peering": { + "enabled": true + }, + {{- end }} + "server": true + } + {{- $vaultConnectCAEnabled := and .Values.global.secretsBackend.vault.connectCA.address .Values.global.secretsBackend.vault.connectCA.rootPKIPath .Values.global.secretsBackend.vault.connectCA.intermediatePKIPath -}} + {{- if and .Values.global.secretsBackend.vault.enabled $vaultConnectCAEnabled }} + {{- with .Values.global.secretsBackend.vault }} + connect-ca-config.json: | + { + "connect": [ + { + "ca_config": [ + { + "address": "{{ .connectCA.address }}", + {{- if and .ca.secretName .ca.secretKey }} + "ca_file": "/consul/vault-ca/tls.crt", + {{- end }} + "intermediate_pki_path": "{{ .connectCA.intermediatePKIPath }}", + "root_pki_path": "{{ .connectCA.rootPKIPath }}", + "auth_method": { + "type": "kubernetes", + "mount_path": "{{ .connectCA.authMethodPath }}", + "params": { + "role": "{{ .consulServerRole }}" + } + } + } + ], + "ca_provider": "vault" + } + ] + } + {{- if .connectCA.additionalConfig }} + additional-connect-ca-config.json: | +{{ tpl .connectCA.additionalConfig $ | trimAll "\"" | indent 4 }} + {{- end }} + {{- end }} + {{- end }} + extra-from-values.json: |- +{{ tpl .Values.server.extraConfig . | trimAll "\"" | indent 4 }} + {{- if .Values.global.acls.manageSystemACLs }} + acl-config.json: |- + { + "acl": { + "enabled": true, + "default_policy": "deny", + "down_policy": "extend-cache", + {{- if (and .Values.global.acls.replicationToken.secretName .Values.global.acls.replicationToken.secretKey) }} + "enable_token_replication": true, + {{- end }} + "enable_token_persistence": true + } + } + {{- end }} + {{- if .Values.global.tls.enabled }} + tls-config.json: |- + { + {{- if .Values.global.peering.enabled }} + "tls": { + {{- if .Values.global.tls.verify }} + "internal_rpc": { + "verify_incoming": true, + "verify_server_hostname": true + }, + "grpc": { + "verify_incoming": false + }, + {{- end }} + "defaults": { + {{- if .Values.global.tls.verify }} + "verify_outgoing": true, + {{- end }} + {{- if .Values.global.secretsBackend.vault.enabled }} + "ca_file": "/vault/secrets/serverca.crt", + "cert_file": "/vault/secrets/servercert.crt", + "key_file": "/vault/secrets/servercert.key" + {{- else }} + "ca_file": "/consul/tls/ca/tls.crt", + "cert_file": "/consul/tls/server/tls.crt", + "key_file": "/consul/tls/server/tls.key" + {{- end }} + } + }, + {{- if .Values.global.tls.enableAutoEncrypt }} + "auto_encrypt": { + "allow_tls": true + }, + {{- end }} + "ports": { + {{- if .Values.global.tls.httpsOnly }} + "http": -1, + {{- end }} + "https": 8501 + } + {{- else }} + {{- if .Values.global.secretsBackend.vault.enabled }} + "ca_file": "/vault/secrets/serverca.crt", + "cert_file": "/vault/secrets/servercert.crt", + "key_file": "/vault/secrets/servercert.key", + {{- else }} + "ca_file": "/consul/tls/ca/tls.crt", + "cert_file": "/consul/tls/server/tls.crt", + "key_file": "/consul/tls/server/tls.key", + {{- end }} + {{- if .Values.global.tls.enableAutoEncrypt }} + "auto_encrypt": { + "allow_tls": true + }, + {{- end }} + {{- if .Values.global.tls.verify }} + "verify_incoming_rpc": true, + "verify_outgoing": true, + "verify_server_hostname": true, + {{- end }} + "ports": { + {{- if .Values.global.tls.httpsOnly }} + "http": -1, + {{- end }} + "https": 8501 + } + {{- end }} + } + {{- end }} + {{- if .Values.ui.enabled }} + ui-config.json: |- + { + "ui_config": { + {{- if (or (eq "true" (.Values.ui.metrics.enabled | toString) ) (and .Values.global.metrics.enabled (eq "-" (.Values.ui.metrics.enabled | toString)))) }} + "metrics_provider": "{{ .Values.ui.metrics.provider }}", + "metrics_proxy": { + "base_url": "{{ .Values.ui.metrics.baseURL }}" + }, + {{- end }} + {{- if .Values.ui.dashboardURLTemplates.service }} + "dashboard_url_templates": { + "service": "{{ .Values.ui.dashboardURLTemplates.service }}" + }, + {{- end }} + "enabled": true + } + } + {{- end }} + central-config.json: |- + { + "enable_central_service_config": true + } + {{- if .Values.global.federation.enabled }} + federation-config.json: |- + { + "primary_datacenter": "{{ .Values.global.federation.primaryDatacenter }}", + "primary_gateways": {{ .Values.global.federation.primaryGateways | toJson }}, + "connect": { + "enable_mesh_gateway_wan_federation": true + } + } + {{- end }} + {{- if (and .Values.global.metrics.enabled .Values.global.metrics.enableAgentMetrics) }} + telemetry-config.json: |- + { + "telemetry": { + "prometheus_retention_time": "{{ .Values.global.metrics.agentMetricsRetentionTime }}" + } + } + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/server-disruptionbudget.yaml b/charts/hashicorp/consul/0.49.0/templates/server-disruptionbudget.yaml new file mode 100644 index 000000000..edf9c1c57 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/server-disruptionbudget.yaml @@ -0,0 +1,26 @@ +{{- if (and .Values.server.disruptionBudget.enabled (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled))) }} +# PodDisruptionBudget to prevent degrading the server cluster through +# voluntary cluster changes. +{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" }} +apiVersion: policy/v1 +{{- else }} +apiVersion: policy/v1beta1 +{{- end }} +kind: PodDisruptionBudget +metadata: + name: {{ template "consul.fullname" . }}-server + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server +spec: + maxUnavailable: {{ template "consul.pdb.maxUnavailable" . }} + selector: + matchLabels: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + component: server +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/server-podsecuritypolicy.yaml b/charts/hashicorp/consul/0.49.0/templates/server-podsecuritypolicy.yaml new file mode 100644 index 000000000..507a07179 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/server-podsecuritypolicy.yaml @@ -0,0 +1,53 @@ +{{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled))) }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-server + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + - 'persistentVolumeClaim' + hostNetwork: false + hostPorts: + {{- if .Values.server.exposeGossipAndRPCPorts }} + - min: 8300 + max: 8300 + - min: {{ .Values.server.ports.serflan.port }} + max: {{ .Values.server.ports.serflan.port }} + - min: 8302 + max: 8302 + - min: 8503 + max: 8503 + {{- end }} + hostIPC: false + hostPID: false + runAsUser: + # Require the container to run without root privileges. + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/server-role.yaml b/charts/hashicorp/consul/0.49.0/templates/server-role.yaml new file mode 100644 index 000000000..202518bf6 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/server-role.yaml @@ -0,0 +1,34 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-server + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server +{{- if (or (and .Values.global.openshift.enabled .Values.server.exposeGossipAndRPCPorts) .Values.global.enablePodSecurityPolicies) }} +rules: +{{- if .Values.global.enablePodSecurityPolicies }} +- apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" . }}-server + verbs: + - use +{{- end }} +{{- if (and .Values.global.openshift.enabled .Values.server.exposeGossipAndRPCPorts ) }} +- apiGroups: ["security.openshift.io"] + resources: ["securitycontextconstraints"] + resourceNames: + - {{ template "consul.fullname" . }}-server + verbs: + - use +{{- end }} +{{- else}} +rules: [] +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/server-rolebinding.yaml b/charts/hashicorp/consul/0.49.0/templates/server-rolebinding.yaml new file mode 100644 index 000000000..8ab705ddb --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/server-rolebinding.yaml @@ -0,0 +1,20 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-server + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-server +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-server +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/server-securitycontextconstraints.yaml b/charts/hashicorp/consul/0.49.0/templates/server-securitycontextconstraints.yaml new file mode 100644 index 000000000..8edd784ea --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/server-securitycontextconstraints.yaml @@ -0,0 +1,49 @@ +{{- if (and .Values.global.openshift.enabled .Values.server.exposeGossipAndRPCPorts (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled))) }} +apiVersion: security.openshift.io/v1 +kind: SecurityContextConstraints +metadata: + name: {{ template "consul.fullname" . }}-server + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server + annotations: + kubernetes.io/description: {{ template "consul.fullname" . }}-server are the security context constraints required + to run the consul server. +allowHostPorts: true +allowHostDirVolumePlugin: false +allowHostIPC: false +allowHostPID: false +allowHostNetwork: false +allowPrivilegeEscalation: false +allowPrivilegedContainer: false +allowedCapabilities: null +defaultAddCapabilities: null +fsGroup: + type: MustRunAs +groups: [] +priority: null +readOnlyRootFilesystem: false +requiredDropCapabilities: +- KILL +- MKNOD +- SETUID +- SETGID +runAsUser: + type: MustRunAsRange +seLinuxContext: + type: MustRunAs +supplementalGroups: + type: MustRunAs +users: [] +volumes: +- configMap +- downwardAPI +- emptyDir +- persistentVolumeClaim +- projected +- secret +{{- end -}} diff --git a/charts/hashicorp/consul/0.49.0/templates/server-service.yaml b/charts/hashicorp/consul/0.49.0/templates/server-service.yaml new file mode 100644 index 000000000..4b1c714c1 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/server-service.yaml @@ -0,0 +1,76 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +# Headless service for Consul server DNS entries. This service should only +# point to Consul servers. For access to an agent, one should assume that +# the agent is installed locally on the node and the NODE_IP should be used. +# If the node can't run a Consul agent, then this service can be used to +# communicate directly to a server agent. +apiVersion: v1 +kind: Service +metadata: + name: {{ template "consul.fullname" . }}-server + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server + annotations: + {{- if .Values.server.service.annotations }} + {{ tpl .Values.server.service.annotations . | nindent 4 | trim }} + {{- end }} + # This must be set in addition to publishNotReadyAddresses due + # to an open issue where it may not work: + # https://github.com/kubernetes/kubernetes/issues/58662 + service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" +spec: + clusterIP: None + # We want the servers to become available even if they're not ready + # since this DNS is also used for join operations. + publishNotReadyAddresses: true + ports: + {{- if (or (not .Values.global.tls.enabled) (not .Values.global.tls.httpsOnly)) }} + - name: http + port: 8500 + targetPort: 8500 + {{- end }} + {{- if .Values.global.tls.enabled }} + - name: https + port: 8501 + targetPort: 8501 + {{- end }} + - name: grpc + port: 8503 + targetPort: 8503 + - name: serflan-tcp + protocol: "TCP" + port: 8301 + targetPort: 8301 + - name: serflan-udp + protocol: "UDP" + port: 8301 + targetPort: 8301 + - name: serfwan-tcp + protocol: "TCP" + port: 8302 + targetPort: 8302 + - name: serfwan-udp + protocol: "UDP" + port: 8302 + targetPort: 8302 + - name: server + port: 8300 + targetPort: 8300 + - name: dns-tcp + protocol: "TCP" + port: 8600 + targetPort: dns-tcp + - name: dns-udp + protocol: "UDP" + port: 8600 + targetPort: dns-udp + selector: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + component: server +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/server-serviceaccount.yaml b/charts/hashicorp/consul/0.49.0/templates/server-serviceaccount.yaml new file mode 100644 index 000000000..a1617975a --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/server-serviceaccount.yaml @@ -0,0 +1,23 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-server + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server + {{- if .Values.server.serviceAccount.annotations }} + annotations: + {{ tpl .Values.server.serviceAccount.annotations . | nindent 4 | trim }} + {{- end }} +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/server-statefulset.yaml b/charts/hashicorp/consul/0.49.0/templates/server-statefulset.yaml new file mode 100644 index 000000000..71b424cc6 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/server-statefulset.yaml @@ -0,0 +1,432 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if and .Values.global.federation.enabled .Values.global.adminPartitions.enabled }}{{ fail "If global.federation.enabled is true, global.adminPartitions.enabled must be false because they are mutually exclusive" }}{{ end }} +{{- if and .Values.global.federation.enabled (not .Values.global.tls.enabled) }}{{ fail "If global.federation.enabled is true, global.tls.enabled must be true because federation is only supported with TLS enabled" }}{{ end }} +{{- if and .Values.global.federation.enabled (not .Values.meshGateway.enabled) }}{{ fail "If global.federation.enabled is true, meshGateway.enabled must be true because mesh gateways are required for federation" }}{{ end }} +{{- if and .Values.server.serverCert.secretName (not .Values.global.tls.caCert.secretName) }}{{ fail "If server.serverCert.secretName is provided, global.tls.caCert must also be provided" }}{{ end }} +{{- if .Values.server.disableFsGroupSecurityContext }}{{ fail "server.disableFsGroupSecurityContext has been removed. Please use global.openshift.enabled instead." }}{{ end }} +{{- if .Values.server.bootstrapExpect }}{{ if lt (int .Values.server.bootstrapExpect) (int .Values.server.replicas) }}{{ fail "server.bootstrapExpect cannot be less than server.replicas" }}{{ end }}{{ end }} +{{- if (and .Values.global.gossipEncryption.secretName (not .Values.global.gossipEncryption.secretKey)) }}{{fail "gossipEncryption.secretKey and secretName must both be specified." }}{{ end -}} +{{- if (and (not .Values.global.gossipEncryption.secretName) .Values.global.gossipEncryption.secretKey) }}{{fail "gossipEncryption.secretKey and secretName must both be specified." }}{{ end -}} +{{- if (and .Values.global.secretsBackend.vault.enabled (not .Values.global.secretsBackend.vault.consulServerRole)) }}{{ fail "global.secretsBackend.vault.consulServerRole must be provided if global.secretsBackend.vault.enabled=true." }}{{ end -}} +{{- if (and .Values.server.serverCert.secretName (not .Values.global.tls.caCert.secretName)) }}{{ fail "If server.serverCert.secretName is provided, global.tls.caCert.secretName must also be provided" }}{{ end }} +{{- if (and (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) (not .Values.global.tls.caCert.secretName)) }}{{ fail "global.tls.caCert.secretName must be provided if global.tls.enabled=true and global.secretsBackend.vault.enabled=true." }}{{ end -}} +{{- if (and (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) (not .Values.global.tls.enableAutoEncrypt)) }}{{ fail "global.tls.enableAutoEncrypt must be true if global.secretsBackend.vault.enabled=true and global.tls.enabled=true" }}{{ end -}} +{{- if (and (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) (not .Values.global.secretsBackend.vault.consulCARole)) }}{{ fail "global.secretsBackend.vault.consulCARole must be provided if global.secretsBackend.vault.enabled=true and global.tls.enabled=true" }}{{ end -}} +{{- if (and .Values.global.enterpriseLicense.secretName (not .Values.global.enterpriseLicense.secretKey)) }}{{fail "enterpriseLicense.secretKey and secretName must both be specified." }}{{ end -}} +{{- if (and (not .Values.global.enterpriseLicense.secretName) .Values.global.enterpriseLicense.secretKey) }}{{fail "enterpriseLicense.secretKey and secretName must both be specified." }}{{ end -}} +{{- if (and .Values.global.acls.bootstrapToken.secretName (not .Values.global.acls.bootstrapToken.secretKey)) }}{{fail "both global.acls.bootstrapToken.secretKey and global.acls.bootstrapToken.secretName must be set if one of them is provided." }}{{ end -}} +{{- if (and (not .Values.global.acls.bootstrapToken.secretName) .Values.global.acls.bootstrapToken.secretKey) }}{{fail "both global.acls.bootstrapToken.secretKey and global.acls.bootstrapToken.secretName must be set if one of them is provided." }}{{ end -}} +# StatefulSet to run the actual Consul server cluster. +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ template "consul.fullname" . }}-server + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: server +spec: + serviceName: {{ template "consul.fullname" . }}-server + podManagementPolicy: Parallel + replicas: {{ .Values.server.replicas }} + {{- if (gt (int .Values.server.updatePartition) 0) }} + updateStrategy: + type: RollingUpdate + rollingUpdate: + partition: {{ .Values.server.updatePartition }} + {{- end }} + selector: + matchLabels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: server + hasDNS: "true" + template: + metadata: + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: server + hasDNS: "true" + {{- if .Values.server.extraLabels }} + {{- toYaml .Values.server.extraLabels | nindent 8 }} + {{- end }} + annotations: + {{- if .Values.global.secretsBackend.vault.enabled }} + "vault.hashicorp.com/agent-inject": "true" + "vault.hashicorp.com/role": "{{ .Values.global.secretsBackend.vault.consulServerRole }}" + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": {{ .Values.global.secretsBackend.vault.ca.secretName }} + "vault.hashicorp.com/ca-cert": /vault/custom/{{ .Values.global.secretsBackend.vault.ca.secretKey }} + {{- end }} + {{- if .Values.global.gossipEncryption.secretName }} + {{- with .Values.global.gossipEncryption }} + "vault.hashicorp.com/agent-inject-secret-gossip.txt": "{{ .secretName }}" + "vault.hashicorp.com/agent-inject-template-gossip.txt": {{ template "consul.vaultSecretTemplate" . }} + {{- end }} + {{- end }} + {{- if .Values.server.serverCert.secretName }} + "vault.hashicorp.com/agent-inject-secret-servercert.crt": {{ .Values.server.serverCert.secretName }} + "vault.hashicorp.com/agent-inject-template-servercert.crt": {{ include "consul.serverTLSCertTemplate" . }} + "vault.hashicorp.com/agent-inject-secret-servercert.key": {{ .Values.server.serverCert.secretName }} + "vault.hashicorp.com/agent-inject-template-servercert.key": {{ include "consul.serverTLSKeyTemplate" . }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ include "consul.serverTLSCATemplate" . }} + {{- end }} + {{- if (and .Values.global.acls.replicationToken.secretName (not .Values.global.acls.createReplicationToken)) }} + "vault.hashicorp.com/agent-inject-secret-replication-token-config.hcl": "{{ .Values.global.acls.replicationToken.secretName }}" + "vault.hashicorp.com/agent-inject-template-replication-token-config.hcl": {{ template "consul.vaultReplicationTokenConfigTemplate" . }} + {{- end }} + {{- if (and .Values.global.acls.manageSystemACLs .Values.global.acls.bootstrapToken.secretName) }} + "vault.hashicorp.com/agent-inject-secret-bootstrap-token-config.hcl": "{{ .Values.global.acls.bootstrapToken.secretName }}" + "vault.hashicorp.com/agent-inject-template-bootstrap-token-config.hcl": {{ template "consul.vaultBootstrapTokenConfigTemplate" . }} + {{- end }} + {{- if .Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl .Values.global.secretsBackend.vault.agentAnnotations . | nindent 8 | trim }} + {{- end }} + {{- if .Values.global.enterpriseLicense.secretName }} + {{- with .Values.global.enterpriseLicense }} + "vault.hashicorp.com/agent-inject-secret-enterpriselicense.txt": "{{ .secretName }}" + "vault.hashicorp.com/agent-inject-template-enterpriselicense.txt": {{ template "consul.vaultSecretTemplate" . }} + {{- end }} + {{- end }} + {{- end }} + "consul.hashicorp.com/connect-inject": "false" + "consul.hashicorp.com/config-checksum": {{ include (print $.Template.BasePath "/server-config-configmap.yaml") . | sha256sum }} + {{- if .Values.server.annotations }} + {{- tpl .Values.server.annotations . | nindent 8 }} + {{- end }} + {{- if (and .Values.global.metrics.enabled .Values.global.metrics.enableAgentMetrics) }} + "prometheus.io/scrape": "true" + "prometheus.io/path": "/v1/agent/metrics" + "prometheus.io/port": "8500" + {{- end }} + spec: + {{- if .Values.server.affinity }} + affinity: + {{ tpl .Values.server.affinity . | nindent 8 | trim }} + {{- end }} + {{- if .Values.server.tolerations }} + tolerations: + {{ tpl .Values.server.tolerations . | nindent 8 | trim }} + {{- end }} + {{- if .Values.server.topologySpreadConstraints }} + topologySpreadConstraints: + {{ tpl .Values.server.topologySpreadConstraints . | nindent 8 | trim }} + {{- end }} + terminationGracePeriodSeconds: 30 + serviceAccountName: {{ template "consul.fullname" . }}-server + {{- if not .Values.global.openshift.enabled }} + securityContext: + {{- toYaml .Values.server.securityContext | nindent 8 }} + {{- end }} + volumes: + - name: config + configMap: + name: {{ template "consul.fullname" . }}-server-config + - name: extra-config + emptyDir: {} + {{- if (and .Values.global.tls.enabled (not .Values.global.secretsBackend.vault.enabled)) }} + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + - name: consul-server-cert + secret: + {{- if .Values.server.serverCert.secretName }} + secretName: {{ .Values.server.serverCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-server-cert + {{- end }} + {{- end }} + {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.enableLicenseAutoload (not .Values.global.secretsBackend.vault.enabled)) }} + - name: consul-license + secret: + secretName: {{ .Values.global.enterpriseLicense.secretName }} + {{- end }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + - name: vault-ca + secret: + secretName: {{ .Values.global.secretsBackend.vault.ca.secretName }} + items: + - key: {{ .Values.global.secretsBackend.vault.ca.secretKey }} + path: tls.crt + {{- end }} + {{- range .Values.server.extraVolumes }} + - name: userconfig-{{ .name }} + {{ .type }}: + {{- if (eq .type "configMap") }} + name: {{ .name }} + {{- else if (eq .type "secret") }} + secretName: {{ .name }} + {{- end }} + {{- with .items }} + items: + {{- range . }} + - key: {{.key}} + path: {{.path}} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.server.priorityClassName }} + priorityClassName: {{ .Values.server.priorityClassName | quote }} + {{- end }} + containers: + - name: consul + image: "{{ default .Values.global.image .Values.server.image }}" + env: + - name: ADVERTISE_IP + valueFrom: + fieldRef: + {{- if .Values.server.exposeGossipAndRPCPorts }} + {{- /* Server gossip and RPC ports will be exposed as a hostPort + on the hostIP, so they need to advertise their host ip + instead of their pod ip. This is to support external client + agents. */}} + fieldPath: status.hostIP + {{- else }} + fieldPath: status.podIP + {{- end }} + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: CONSUL_DISABLE_PERM_MGMT + value: "true" + {{- if (or .Values.global.gossipEncryption.autoGenerate (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey)) }} + {{- if not .Values.global.secretsBackend.vault.enabled }} + - name: GOSSIP_KEY + valueFrom: + secretKeyRef: + {{- if .Values.global.gossipEncryption.autoGenerate }} + name: {{ template "consul.fullname" . }}-gossip-encryption-key + key: key + {{- else if (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey) }} + name: {{ .Values.global.gossipEncryption.secretName }} + key: {{ .Values.global.gossipEncryption.secretKey }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.global.tls.enabled }} + - name: CONSUL_HTTP_ADDR + value: https://localhost:8501 + - name: CONSUL_CACERT + {{- if .Values.global.secretsBackend.vault.enabled }} + value: /vault/secrets/serverca.crt + {{- else }} + value: /consul/tls/ca/tls.crt + {{- end }} + {{- end }} + {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.enableLicenseAutoload) }} + - name: CONSUL_LICENSE_PATH + {{- if .Values.global.secretsBackend.vault.enabled }} + value: /vault/secrets/enterpriselicense.txt + {{- else }} + value: /consul/license/{{ .Values.global.enterpriseLicense.secretKey }} + {{- end }} + {{- end }} + {{- if and (not .Values.global.secretsBackend.vault.enabled) .Values.global.acls.bootstrapToken.secretName }} + - name: ACL_BOOTSTRAP_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Values.global.acls.bootstrapToken.secretName | quote }} + key: {{ .Values.global.acls.bootstrapToken.secretKey | quote }} + {{- end }} + {{- if (and .Values.global.acls.replicationToken.secretName .Values.global.acls.replicationToken.secretKey (not .Values.global.secretsBackend.vault.enabled)) }} + - name: ACL_REPLICATION_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Values.global.acls.replicationToken.secretName | quote }} + key: {{ .Values.global.acls.replicationToken.secretKey | quote }} + {{- end }} + {{- include "consul.extraEnvironmentVars" .Values.server | nindent 12 }} + command: + - "/bin/sh" + - "-ec" + - | + {{- if and .Values.global.secretsBackend.vault.enabled .Values.global.gossipEncryption.secretName }} + GOSSIP_KEY=`cat /vault/secrets/gossip.txt` + {{- end }} + + {{- if (and .Values.dns.enabled .Values.dns.enableRedirection) }} + {{ template "consul.recursors" }} + {{- end }} + + {{ template "consul.extraconfig" }} + + exec /usr/local/bin/docker-entrypoint.sh consul agent \ + -advertise="${ADVERTISE_IP}" \ + -config-dir=/consul/config \ + {{- if (or .Values.global.gossipEncryption.autoGenerate (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey)) }} + -encrypt="${GOSSIP_KEY}" \ + {{- end }} + {{- if (and .Values.global.acls.replicationToken.secretName .Values.global.acls.replicationToken.secretKey) }} + {{- if (and .Values.global.secretsBackend.vault.enabled (not .Values.global.acls.createReplicationToken)) }} + -config-file=/vault/secrets/replication-token-config.hcl \ + {{- else }} + -hcl="acl { tokens { agent = \"${ACL_REPLICATION_TOKEN}\", replication = \"${ACL_REPLICATION_TOKEN}\" } }" \ + {{- end }} + {{- end }} + {{- if (and .Values.dns.enabled .Values.dns.enableRedirection) }} + $recursor_flags \ + {{- end }} + {{- if and .Values.global.secretsBackend.vault.enabled .Values.global.acls.bootstrapToken.secretName }} + -config-file=/vault/secrets/bootstrap-token-config.hcl \ + {{- else if (and (not .Values.global.secretsBackend.vault.enabled) .Values.global.acls.bootstrapToken.secretName) }} + -hcl="acl { tokens { initial_management = \"${ACL_BOOTSTRAP_TOKEN}\" } }" \ + {{- end }} + {{- /* Always include the extraVolumes at the end so that users can + override other Consul settings. The last -config-dir takes + precedence. */}} + {{- range .Values.server.extraVolumes }} + {{- if .load }} + -config-dir=/consul/userconfig/{{ .name }} \ + {{- end }} + {{- end }} + -config-file=/consul/extra-config/extra-from-values.json + volumeMounts: + - name: data-{{ .Release.Namespace | trunc 58 | trimSuffix "-" }} + mountPath: /consul/data + - name: config + mountPath: /consul/config + - name: extra-config + mountPath: /consul/extra-config + {{- if (and .Values.global.tls.enabled (not .Values.global.secretsBackend.vault.enabled)) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca/ + readOnly: true + - name: consul-server-cert + mountPath: /consul/tls/server + readOnly: true + {{- end }} + {{- if (and .Values.global.enterpriseLicense.secretName .Values.global.enterpriseLicense.enableLicenseAutoload (not .Values.global.secretsBackend.vault.enabled)) }} + - name: consul-license + mountPath: /consul/license + readOnly: true + {{- end }} + {{- range .Values.server.extraVolumes }} + - name: userconfig-{{ .name }} + readOnly: true + mountPath: /consul/userconfig/{{ .name }} + {{- end }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + - name: vault-ca + mountPath: /consul/vault-ca/ + readOnly: true + {{- end }} + ports: + {{- if (or (not .Values.global.tls.enabled) (not .Values.global.tls.httpsOnly)) }} + - name: http + containerPort: 8500 + {{- end }} + {{- if .Values.global.tls.enabled }} + - name: https + containerPort: 8501 + {{- end }} + - containerPort: 8503 + {{- if .Values.server.exposeGossipAndRPCPorts }} + hostPort: 8503 + {{- end }} + name: grpc + - name: serflan-tcp + containerPort: {{ .Values.server.ports.serflan.port }} + {{- if .Values.server.exposeGossipAndRPCPorts }} + hostPort: {{ .Values.server.ports.serflan.port }} + {{- end }} + protocol: "TCP" + - name: serflan-udp + containerPort: {{ .Values.server.ports.serflan.port }} + {{- if .Values.server.exposeGossipAndRPCPorts }} + hostPort: {{ .Values.server.ports.serflan.port }} + {{- end }} + protocol: "UDP" + - name: serfwan-tcp + containerPort: 8302 + {{- if .Values.server.exposeGossipAndRPCPorts }} + hostPort: 8302 + {{- end }} + protocol: "TCP" + - name: serfwan-udp + containerPort: 8302 + {{- if .Values.server.exposeGossipAndRPCPorts }} + hostPort: 8302 + {{- end }} + protocol: "UDP" + - name: server + containerPort: 8300 + {{- if .Values.server.exposeGossipAndRPCPorts }} + hostPort: 8300 + {{- end }} + - name: dns-tcp + containerPort: 8600 + protocol: "TCP" + - name: dns-udp + containerPort: 8600 + protocol: "UDP" + readinessProbe: + # NOTE(mitchellh): when our HTTP status endpoints support the + # proper status codes, we should switch to that. This is temporary. + exec: + command: + - "/bin/sh" + - "-ec" + - | + {{- if .Values.global.tls.enabled }} + curl -k \ + https://127.0.0.1:8501/v1/status/leader \ + {{- else }} + curl http://127.0.0.1:8500/v1/status/leader \ + {{- end }} + 2>/dev/null | grep -E '".+"' + failureThreshold: 2 + initialDelaySeconds: 5 + periodSeconds: 3 + successThreshold: 1 + timeoutSeconds: 5 + {{- if .Values.server.resources }} + resources: + {{- if eq (typeOf .Values.server.resources) "string" }} + {{ tpl .Values.server.resources . | nindent 12 | trim }} + {{- else }} + {{- toYaml .Values.server.resources | nindent 12 }} + {{- end }} + {{- end }} + {{- if not .Values.global.openshift.enabled }} + securityContext: + {{- toYaml .Values.server.containerSecurityContext.server | nindent 12 }} + {{- end }} + {{- if .Values.server.extraContainers }} + {{ toYaml .Values.server.extraContainers | nindent 8 }} + {{- end }} + {{- if .Values.server.nodeSelector }} + nodeSelector: + {{ tpl .Values.server.nodeSelector . | indent 8 | trim }} + {{- end }} + volumeClaimTemplates: + - metadata: + name: data-{{ .Release.Namespace | trunc 58 | trimSuffix "-" }} + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.server.storage }} + {{- if .Values.server.storageClass }} + storageClassName: {{ .Values.server.storageClass }} + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/sync-catalog-clusterrole.yaml b/charts/hashicorp/consul/0.49.0/templates/sync-catalog-clusterrole.yaml new file mode 100644 index 000000000..0b0837c0d --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/sync-catalog-clusterrole.yaml @@ -0,0 +1,41 @@ +{{- $syncEnabled := (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled)) }} +{{- if $syncEnabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "consul.fullname" . }}-sync-catalog + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: sync-catalog +rules: + - apiGroups: [""] + resources: + - services + - endpoints + verbs: + - get + - list + - watch +{{- if .Values.syncCatalog.toK8S }} + - update + - patch + - delete + - create +{{- end }} + - apiGroups: [""] + resources: + - nodes + verbs: + - get +{{- if .Values.global.enablePodSecurityPolicies }} + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + verbs: + - use + resourceNames: + - {{ template "consul.fullname" . }}-sync-catalog +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/sync-catalog-clusterrolebinding.yaml b/charts/hashicorp/consul/0.49.0/templates/sync-catalog-clusterrolebinding.yaml new file mode 100644 index 000000000..818823cca --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/sync-catalog-clusterrolebinding.yaml @@ -0,0 +1,21 @@ +{{- $syncEnabled := (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled)) }} +{{- if $syncEnabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "consul.fullname" . }}-sync-catalog + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: sync-catalog +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "consul.fullname" . }}-sync-catalog +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-sync-catalog + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/sync-catalog-deployment.yaml b/charts/hashicorp/consul/0.49.0/templates/sync-catalog-deployment.yaml new file mode 100644 index 000000000..4c8b4359d --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/sync-catalog-deployment.yaml @@ -0,0 +1,313 @@ +{{- $clientEnabled := (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled)) }} +{{- template "consul.reservedNamesFailer" (list .Values.syncCatalog.consulNamespaces.consulDestinationNamespace "syncCatalog.consulNamespaces.consulDestinationNamespace") }} +# The deployment for running the sync-catalog pod +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "consul.fullname" . }}-sync-catalog + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: sync-catalog +spec: + replicas: 1 + selector: + matchLabels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: sync-catalog + template: + metadata: + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: sync-catalog + {{- if .Values.syncCatalog.extraLabels }} + {{- toYaml .Values.syncCatalog.extraLabels | nindent 8 }} + {{- end }} + annotations: + "consul.hashicorp.com/connect-inject": "false" + {{- if .Values.syncCatalog.annotations }} + {{- tpl .Values.syncCatalog.annotations . | nindent 8 }} + {{- end }} + {{- if (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) }} + "vault.hashicorp.com/agent-init-first": "true" + "vault.hashicorp.com/agent-inject": "true" + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.consulCARole }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" . }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": "{{ .Values.global.secretsBackend.vault.ca.secretName }}" + "vault.hashicorp.com/ca-cert": "/vault/custom/{{ .Values.global.secretsBackend.vault.ca.secretKey }}" + {{- end }} + {{- if .Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl .Values.global.secretsBackend.vault.agentAnnotations . | nindent 8 | trim }} + {{- end }} + {{- end }} + spec: + serviceAccountName: {{ template "consul.fullname" . }}-sync-catalog + volumes: + - name: consul-data + emptyDir: + medium: "Memory" + {{- if .Values.global.tls.enabled }} + {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + {{- if (and .Values.global.tls.enableAutoEncrypt $clientEnabled) }} + - name: consul-auto-encrypt-ca-cert + emptyDir: + medium: "Memory" + {{- end }} + {{- end }} + containers: + - name: sync-catalog + image: "{{ default .Values.global.imageK8S .Values.syncCatalog.image }}" + env: + {{- if .Values.global.acls.manageSystemACLs }} + - name: CONSUL_HTTP_TOKEN_FILE + value: "/consul/login/acl-token" + {{- end }} + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- if (and .Values.syncCatalog.aclSyncToken.secretName .Values.syncCatalog.aclSyncToken.secretKey) }} + - name: CONSUL_HTTP_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Values.syncCatalog.aclSyncToken.secretName }} + key: {{ .Values.syncCatalog.aclSyncToken.secretKey }} + {{- end }} + {{- if .Values.global.tls.enabled }} + {{- if .Values.client.enabled }} + - name: CONSUL_HTTP_ADDR + value: https://$(HOST_IP):8501 + {{- else }} + - name: CONSUL_HTTP_ADDR + value: https://{{ template "consul.fullname" . }}-server:8501 + {{- end }} + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- else }} + {{- if .Values.client.enabled }} + - name: CONSUL_HTTP_ADDR + value: http://$(HOST_IP):8500 + {{- else }} + - name: CONSUL_HTTP_ADDR + value: http://{{ template "consul.fullname" . }}-server:8500 + {{- end }} + {{- end }} + volumeMounts: + - mountPath: /consul/login + name: consul-data + readOnly: true + {{- if .Values.global.tls.enabled }} + {{- if and .Values.global.tls.enableAutoEncrypt $clientEnabled }} + - name: consul-auto-encrypt-ca-cert + {{- else }} + - name: consul-ca-cert + {{- end }} + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + consul-k8s-control-plane sync-catalog \ + -consul-api-timeout={{ .Values.global.consulAPITimeout }} \ + -log-level={{ default .Values.global.logLevel .Values.syncCatalog.logLevel }} \ + -log-json={{ .Values.global.logJSON }} \ + -k8s-default-sync={{ .Values.syncCatalog.default }} \ + {{- if (not .Values.syncCatalog.toConsul) }} + -to-consul=false \ + {{- end }} + {{- if (not .Values.syncCatalog.toK8S) }} + -to-k8s=false \ + {{- end }} + -consul-domain={{ .Values.global.domain }} \ + {{- if .Values.syncCatalog.k8sPrefix }} + -k8s-service-prefix="{{ .Values.syncCatalog.k8sPrefix}}" \ + {{- end }} + {{- if .Values.syncCatalog.k8sSourceNamespace }} + -k8s-source-namespace="{{ .Values.syncCatalog.k8sSourceNamespace}}" \ + {{- end }} + {{- range $value := .Values.syncCatalog.k8sAllowNamespaces }} + -allow-k8s-namespace="{{ $value }}" \ + {{- end }} + {{- range $value := .Values.syncCatalog.k8sDenyNamespaces }} + -deny-k8s-namespace="{{ $value }}" \ + {{- end }} + -k8s-write-namespace=${NAMESPACE} \ + {{- if (not .Values.syncCatalog.syncClusterIPServices) }} + -sync-clusterip-services=false \ + {{- end }} + {{- if .Values.syncCatalog.nodePortSyncType }} + -node-port-sync-type={{ .Values.syncCatalog.nodePortSyncType }} \ + {{- end }} + {{- if .Values.syncCatalog.consulWriteInterval }} + -consul-write-interval={{ .Values.syncCatalog.consulWriteInterval }} \ + {{- end }} + {{- if .Values.syncCatalog.k8sTag }} + -consul-k8s-tag={{ .Values.syncCatalog.k8sTag }} \ + {{- end }} + {{- if .Values.syncCatalog.consulNodeName }} + -consul-node-name={{ .Values.syncCatalog.consulNodeName }} \ + {{- end }} + {{- if .Values.global.adminPartitions.enabled }} + -partition={{ .Values.global.adminPartitions.name }} \ + {{- end }} + {{- if .Values.syncCatalog.consulPrefix}} + -consul-service-prefix="{{ .Values.syncCatalog.consulPrefix}}" \ + {{- end}} + {{- if .Values.syncCatalog.addK8SNamespaceSuffix}} + -add-k8s-namespace-suffix \ + {{- end}} + {{- if .Values.global.enableConsulNamespaces }} + -enable-namespaces=true \ + {{- if .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} + -consul-destination-namespace={{ .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} \ + {{- end }} + {{- if .Values.syncCatalog.consulNamespaces.mirroringK8S }} + -enable-k8s-namespace-mirroring=true \ + {{- if .Values.syncCatalog.consulNamespaces.mirroringK8SPrefix }} + -k8s-namespace-mirroring-prefix={{ .Values.syncCatalog.consulNamespaces.mirroringK8SPrefix }} \ + {{- end }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + -consul-cross-namespace-acl-policy=cross-namespace-policy \ + {{- end }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + lifecycle: + preStop: + exec: + command: + - "/bin/sh" + - "-ec" + - | + consul-k8s-control-plane consul-logout -consul-api-timeout={{ .Values.global.consulAPITimeout }} + {{- end }} + livenessProbe: + httpGet: + path: /health/ready + port: 8080 + scheme: HTTP + failureThreshold: 3 + initialDelaySeconds: 30 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: /health/ready + port: 8080 + scheme: HTTP + failureThreshold: 5 + initialDelaySeconds: 10 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 5 + {{- with .Values.syncCatalog.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if or .Values.global.acls.manageSystemACLs (and .Values.global.tls.enabled .Values.global.tls.enableAutoEncrypt $clientEnabled) }} + initContainers: + {{- if (and .Values.global.tls.enabled .Values.global.tls.enableAutoEncrypt $clientEnabled) }} + {{- include "consul.getAutoEncryptClientCA" . | nindent 6 }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + - name: sync-catalog-acl-init + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + {{- if .Values.global.tls.enabled }} + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- end }} + - name: CONSUL_HTTP_ADDR + {{- if .Values.global.tls.enabled }} + value: https://$(HOST_IP):8501 + {{- else }} + value: http://$(HOST_IP):8500 + {{- end }} + image: {{ .Values.global.imageK8S }} + volumeMounts: + - mountPath: /consul/login + name: consul-data + readOnly: false + {{- if .Values.global.tls.enabled }} + {{- if .Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + {{- else }} + - name: consul-ca-cert + {{- end }} + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + consul-k8s-control-plane acl-init \ + -component-name=sync-catalog \ + {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter .Values.global.enableConsulNamespaces }} + -acl-auth-method={{ template "consul.fullname" . }}-k8s-component-auth-method-{{ .Values.global.datacenter }} \ + -primary-datacenter={{ .Values.global.federation.primaryDatacenter }} \ + {{- else }} + -acl-auth-method={{ template "consul.fullname" . }}-k8s-component-auth-method \ + {{- end }} + {{- if .Values.global.adminPartitions.enabled }} + -partition={{ .Values.global.adminPartitions.name }} \ + {{- end }} + -consul-api-timeout={{ .Values.global.consulAPITimeout }} \ + -log-level={{ default .Values.global.logLevel .Values.syncCatalog.logLevel }} \ + -log-json={{ .Values.global.logJSON }} + resources: + requests: + memory: "25Mi" + cpu: "50m" + limits: + memory: "25Mi" + cpu: "50m" + {{- end }} + {{- end }} + {{- if .Values.syncCatalog.priorityClassName }} + priorityClassName: {{ .Values.syncCatalog.priorityClassName | quote }} + {{- end }} + {{- if .Values.syncCatalog.nodeSelector }} + nodeSelector: + {{ tpl .Values.syncCatalog.nodeSelector . | indent 8 | trim }} + {{- end }} + {{- if .Values.syncCatalog.affinity }} + affinity: + {{ tpl .Values.syncCatalog.affinity . | indent 8 | trim }} + {{- end }} + {{- if .Values.syncCatalog.tolerations }} + tolerations: + {{ tpl .Values.syncCatalog.tolerations . | indent 8 | trim }} + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/sync-catalog-podsecuritypolicy.yaml b/charts/hashicorp/consul/0.49.0/templates/sync-catalog-podsecuritypolicy.yaml new file mode 100644 index 000000000..cc70feaab --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/sync-catalog-podsecuritypolicy.yaml @@ -0,0 +1,40 @@ +{{- if (and .Values.global.enablePodSecurityPolicies (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled))) }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-sync-catalog + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: sync-catalog +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/sync-catalog-serviceaccount.yaml b/charts/hashicorp/consul/0.49.0/templates/sync-catalog-serviceaccount.yaml new file mode 100644 index 000000000..deab1ad07 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/sync-catalog-serviceaccount.yaml @@ -0,0 +1,24 @@ +{{- $syncEnabled := (or (and (ne (.Values.syncCatalog.enabled | toString) "-") .Values.syncCatalog.enabled) (and (eq (.Values.syncCatalog.enabled | toString) "-") .Values.global.enabled)) }} +{{- if $syncEnabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-sync-catalog + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: sync-catalog + {{- if .Values.syncCatalog.serviceAccount.annotations }} + annotations: + {{ tpl .Values.syncCatalog.serviceAccount.annotations . | nindent 4 | trim }} + {{- end }} +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/terminating-gateways-deployment.yaml b/charts/hashicorp/consul/0.49.0/templates/terminating-gateways-deployment.yaml new file mode 100644 index 000000000..acdab29af --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/terminating-gateways-deployment.yaml @@ -0,0 +1,440 @@ +{{- if .Values.terminatingGateways.enabled }} +{{- if not .Values.connectInject.enabled }}{{ fail "connectInject.enabled must be true" }}{{ end -}} +{{- if not .Values.client.grpc }}{{ fail "client.grpc must be true" }}{{ end -}} +{{- if and .Values.global.adminPartitions.enabled (not .Values.global.enableConsulNamespaces) }}{{ fail "global.enableConsulNamespaces must be true if global.adminPartitions.enabled=true" }}{{ end }} +{{- if not (or (and (ne (.Values.client.enabled | toString) "-") .Values.client.enabled) (and (eq (.Values.client.enabled | toString) "-") .Values.global.enabled)) }}{{ fail "clients must be enabled" }}{{ end -}} +{{- if .Values.global.lifecycleSidecarContainer }}{{ fail "global.lifecycleSidecarContainer has been renamed to global.consulSidecarContainer. Please set values using global.consulSidecarContainer." }}{{ end }} + +{{- $root := . }} +{{- $defaults := .Values.terminatingGateways.defaults }} +{{- $names := dict }} + +{{- $gateways := .Values.terminatingGateways.gateways }} +{{- range $outerTerminatingIndex, $outerTerminatingVal := $gateways }} + +{{- range $innerTerminatingIndex, $innerTerminatingVal := $gateways }} +{{- if (and (ne $outerTerminatingIndex $innerTerminatingIndex) (eq $outerTerminatingVal.name $innerTerminatingVal.name)) }} +{{ fail (cat "terminating gateways must have unique names but found duplicate name" $innerTerminatingVal.name) }} +{{ end -}} +{{ end -}} + +{{- range $outerIngressIndex, $outerIngressVal := $root.Values.ingressGateways.gateways }} +{{- if (eq $outerTerminatingVal.name $outerIngressVal.name) }} +{{ fail (cat "terminating gateways cannot have duplicate names of any ingress gateways but found duplicate name" $outerTerminatingVal.name) }} +{{ end -}} +{{ end -}} +{{ end -}} + +{{- range .Values.terminatingGateways.gateways }} + +{{- if empty .name }} +# Check that name is not empty +{{ fail "Terminating gateway names cannot be empty"}} +{{ end -}} +{{- if hasKey $names .name }} +# Check that the name doesn't already exist +{{ fail "Terminating gateway names must be unique"}} +{{ end -}} +{{- /* Add the gateway name to the $names dict to ensure uniqueness */ -}} +{{- $_ := set $names .name .name }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: terminating-gateway + terminating-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} +spec: + replicas: {{ default $defaults.replicas .replicas }} + selector: + matchLabels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: terminating-gateway + terminating-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} + template: + metadata: + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: terminating-gateway + terminating-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} + annotations: + {{- if (and $root.Values.global.secretsBackend.vault.enabled $root.Values.global.tls.enabled) }} + "vault.hashicorp.com/agent-init-first": "true" + "vault.hashicorp.com/agent-inject": "true" + "vault.hashicorp.com/role": {{ $root.Values.global.secretsBackend.vault.consulCARole }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ $root.Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" $root }} + {{- if and $root.Values.global.secretsBackend.vault.ca.secretName $root.Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": {{ $root.Values.global.secretsBackend.vault.ca.secretName }} + "vault.hashicorp.com/ca-cert": /vault/custom/{{ $root.Values.global.secretsBackend.vault.ca.secretKey }} + {{- end }} + {{- if $root.Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl $root.Values.global.secretsBackend.vault.agentAnnotations $root | nindent 8 | trim }} + {{- end }} + {{- end }} + "consul.hashicorp.com/connect-inject": "false" + {{- if (and $root.Values.global.metrics.enabled $root.Values.global.metrics.enableGatewayMetrics) }} + "prometheus.io/scrape": "true" + "prometheus.io/path": "/metrics" + "prometheus.io/port": "20200" + {{- end }} + {{- if $defaults.annotations }} + # We allow both default annotations and gateway-specific annotations + {{- tpl $defaults.annotations $root | nindent 8 }} + {{- end }} + {{- if .annotations }} + {{- tpl .annotations $root | nindent 8 }} + {{- end }} + spec: + {{- if (or $defaults.affinity .affinity) }} + affinity: + {{ tpl (default $defaults.affinity .affinity) $root | nindent 8 | trim }} + {{- end }} + {{- if (or $defaults.tolerations .tolerations) }} + tolerations: + {{ tpl (default $defaults.tolerations .tolerations) $root | nindent 8 | trim }} + {{- end }} + {{- if (or $defaults.topologySpreadConstraints .topologySpreadConstraints) }} + topologySpreadConstraints: + {{ tpl (default $defaults.topologySpreadConstraints .topologySpreadConstraints) $root | nindent 8 | trim }} + {{- end }} + terminationGracePeriodSeconds: 10 + serviceAccountName: {{ template "consul.fullname" $root }}-{{ .name }} + volumes: + - name: consul-bin + emptyDir: {} + - name: consul-service + emptyDir: + medium: "Memory" + {{- range (default $defaults.extraVolumes .extraVolumes) }} + - name: userconfig-{{ .name }} + {{ .type }}: + {{- if (eq .type "configMap") }} + name: {{ .name }} + {{- else if (eq .type "secret") }} + secretName: {{ .name }} + {{- end }} + {{- with .items }} + items: + {{- range . }} + - key: {{.key}} + path: {{.path}} + {{- end }} + {{- end }} + {{- end }} + {{- if $root.Values.global.tls.enabled }} + {{- if not (and $root.Values.externalServers.enabled $root.Values.externalServers.useSystemRoots) }} + - name: consul-ca-cert + secret: + {{- if $root.Values.global.tls.caCert.secretName }} + secretName: {{ $root.Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" $root }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" $root.Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + {{- if $root.Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + emptyDir: + medium: "Memory" + {{- end }} + {{- end }} + initContainers: + # We use the Envoy image as our base image so we use an init container to + # copy the Consul binary to a shared directory that can be used when + # starting Envoy. + - name: copy-consul-bin + image: {{ $root.Values.global.image | quote }} + command: + - cp + - /bin/consul + - /consul-bin/consul + volumeMounts: + - name: consul-bin + mountPath: /consul-bin + {{- $initContainer := .initCopyConsulContainer }} + {{- if (or $initContainer $defaults.initCopyConsulContainer) }} + {{- if (default $defaults.initCopyConsulContainer.resources $initContainer.resources) }} + resources: {{ toYaml (default $defaults.initCopyConsulContainer.resources $initContainer.resources) | nindent 12 }} + {{- end }} + {{- end }} + {{- if (and $root.Values.global.tls.enabled $root.Values.global.tls.enableAutoEncrypt) }} + {{- include "consul.getAutoEncryptClientCA" $root | nindent 8 }} + {{- end }} + # terminating-gateway-init registers the terminating gateway service with Consul. + - name: terminating-gateway-init + image: {{ $root.Values.global.imageK8S }} + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- if $root.Values.global.tls.enabled }} + - name: CONSUL_HTTP_ADDR + value: https://$(HOST_IP):8501 + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- else }} + - name: CONSUL_HTTP_ADDR + value: http://$(HOST_IP):8500 + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + {{- if $root.Values.global.acls.manageSystemACLs }} + consul-k8s-control-plane acl-init \ + -component-name=terminating-gateway/{{ template "consul.fullname" $root }}-{{ .name }} \ + -acl-auth-method={{ template "consul.fullname" $root }}-k8s-component-auth-method \ + {{- if $root.Values.global.adminPartitions.enabled }} + -partition={{ $root.Values.global.adminPartitions.name }} \ + {{- end }} + -token-sink-file=/consul/service/acl-token \ + -consul-api-timeout={{ $root.Values.global.consulAPITimeout }} \ + -log-level={{ default $root.Values.global.logLevel }} \ + -log-json={{ $root.Values.global.logJSON }} + {{- end }} + + cat > /consul/service/service.hcl << EOF + service { + kind = "terminating-gateway" + name = "{{ .name }}" + id = "${POD_NAME}" + {{- if $root.Values.global.enableConsulNamespaces }} + namespace = "{{ (default $defaults.consulNamespace .consulNamespace) }}" + {{- end }} + {{- if $root.Values.global.adminPartitions.enabled }} + partition = "{{ $root.Values.global.adminPartitions.name }}" + {{- end }} + address = "${POD_IP}" + port = 8443 + {{- if (and $root.Values.global.metrics.enabled $root.Values.global.metrics.enableGatewayMetrics) }} + proxy { config { envoy_prometheus_bind_addr = "${POD_IP}:20200" } } + {{- end }} + checks = [ + { + name = "Terminating Gateway Listening" + interval = "10s" + tcp = "${POD_IP}:8443" + deregister_critical_service_after = "6h" + } + ] + } + EOF + + /consul-bin/consul services register \ + {{- if $root.Values.global.acls.manageSystemACLs }} + -token-file=/consul/service/acl-token \ + {{- end }} + /consul/service/service.hcl + volumeMounts: + - name: consul-service + mountPath: /consul/service + - name: consul-bin + mountPath: /consul-bin + {{- if $root.Values.global.tls.enabled }} + {{- if $root.Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + {{- else }} + - name: consul-ca-cert + {{- end }} + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + containers: + - name: terminating-gateway + image: {{ $root.Values.global.imageEnvoy | quote }} + {{- if (default $defaults.resources .resources) }} + resources: {{ toYaml (default $defaults.resources .resources) | nindent 12 }} + {{- end }} + volumeMounts: + - name: consul-bin + mountPath: /consul-bin + - mountPath: /consul/service + name: consul-service + readOnly: true + {{- if $root.Values.global.tls.enabled }} + {{- if $root.Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + {{- else }} + - name: consul-ca-cert + {{- end }} + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- range (default $defaults.extraVolumes .extraVolumes) }} + - name: userconfig-{{ .name }} + readOnly: true + mountPath: /consul/userconfig/{{ .name }} + {{- end }} + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- if $root.Values.global.acls.manageSystemACLs }} + - name: CONSUL_HTTP_TOKEN_FILE + value: "/consul/service/acl-token" + {{- end }} + {{- if $root.Values.global.tls.enabled }} + - name: CONSUL_HTTP_ADDR + value: https://$(HOST_IP):8501 + - name: CONSUL_GRPC_ADDR + value: https://$(HOST_IP):8502 + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- else }} + - name: CONSUL_HTTP_ADDR + value: http://$(HOST_IP):8500 + - name: CONSUL_GRPC_ADDR + value: $(HOST_IP):8502 + {{- end }} + command: + - /consul-bin/consul + - connect + - envoy + - -gateway=terminating + - -proxy-id=$(POD_NAME) + {{- if $root.Values.global.enableConsulNamespaces }} + - -namespace={{ default $defaults.consulNamespace .consulNamespace }} + {{- end }} + {{- if $root.Values.global.adminPartitions.enabled }} + - -partition={{ $root.Values.global.adminPartitions.name }} + {{- end }} + livenessProbe: + tcpSocket: + port: 8443 + failureThreshold: 3 + initialDelaySeconds: 30 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + readinessProbe: + tcpSocket: + port: 8443 + failureThreshold: 3 + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + ports: + - name: gateway + containerPort: 8443 + lifecycle: + preStop: + exec: + command: + - "/bin/sh" + - "-ec" + - | + /consul-bin/consul services deregister \ + {{- if $root.Values.global.enableConsulNamespaces }} + -namespace={{ default $defaults.consulNamespace .consulNamespace }} \ + {{- end }} + {{- if $root.Values.global.adminPartitions.enabled }} + -partition={{ $root.Values.global.adminPartitions.name }} \ + {{- end }} + -id="${POD_NAME}" + {{- if $root.Values.global.acls.manageSystemACLs }} + - "/consul-bin/consul logout" + {{- end}} + + # consul-sidecar ensures the terminating gateway is always registered with + # the local Consul agent, even if it loses the initial registration. + - name: consul-sidecar + image: {{ $root.Values.global.imageK8S }} + volumeMounts: + - name: consul-service + mountPath: /consul/service + readOnly: true + - name: consul-bin + mountPath: /consul-bin + {{- if $root.Values.global.tls.enabled }} + {{- if $root.Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + {{- else }} + - name: consul-ca-cert + {{- end }} + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- if $root.Values.global.consulSidecarContainer }} + {{- if $root.Values.global.consulSidecarContainer.resources }} + resources: {{ toYaml $root.Values.global.consulSidecarContainer.resources | nindent 12 }} + {{- end }} + {{- end }} + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + {{- if $root.Values.global.tls.enabled }} + - name: CONSUL_HTTP_ADDR + value: https://$(HOST_IP):8501 + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- else }} + - name: CONSUL_HTTP_ADDR + value: http://$(HOST_IP):8500 + {{- end }} + command: + - consul-k8s-control-plane + - consul-sidecar + - -log-level={{ $root.Values.global.logLevel }} + - -log-json={{ $root.Values.global.logJSON }} + - -service-config=/consul/service/service.hcl + - -consul-binary=/consul-bin/consul + - -consul-api-timeout={{ $root.Values.global.consulAPITimeout }} + {{- if $root.Values.global.acls.manageSystemACLs }} + - -token-file=/consul/service/acl-token + {{- end }} + {{- if (default $defaults.priorityClassName .priorityClassName) }} + priorityClassName: {{ (default $defaults.priorityClassName .priorityClassName) | quote }} + {{- end }} + {{- if (default $defaults.nodeSelector .nodeSelector) }} + nodeSelector: + {{ tpl (default $defaults.nodeSelector .nodeSelector) $root | indent 8 | trim }} + {{- end }} +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/terminating-gateways-podsecuritypolicy.yaml b/charts/hashicorp/consul/0.49.0/templates/terminating-gateways-podsecuritypolicy.yaml new file mode 100644 index 000000000..97ad2af96 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/terminating-gateways-podsecuritypolicy.yaml @@ -0,0 +1,45 @@ +{{- if (and .Values.global.enablePodSecurityPolicies .Values.terminatingGateways.enabled) }} +{{- $root := . }} +{{- range .Values.terminatingGateways.gateways }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: terminating-gateway + terminating-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/terminating-gateways-role.yaml b/charts/hashicorp/consul/0.49.0/templates/terminating-gateways-role.yaml new file mode 100644 index 000000000..4ae280ca8 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/terminating-gateways-role.yaml @@ -0,0 +1,43 @@ +{{- if .Values.terminatingGateways.enabled }} + +{{- $root := . }} +{{- $defaults := .Values.terminatingGateways.defaults }} + +{{- range .Values.terminatingGateways.gateways }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: terminating-gateway + terminating-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} +{{- if (or $root.Values.global.acls.manageSystemACLs $root.Values.global.enablePodSecurityPolicies) }} +rules: +{{- if $root.Values.global.enablePodSecurityPolicies }} + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" $root }}-{{ .name }} + verbs: + - use +{{- end }} +{{- if $root.Values.global.acls.manageSystemACLs }} + - apiGroups: [""] + resources: + - secrets + resourceNames: + - {{ template "consul.fullname" $root }}-{{ .name }}-acl-token + verbs: + - get +{{- end }} +{{- else }} +rules: [] +{{- end }} +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/terminating-gateways-rolebinding.yaml b/charts/hashicorp/consul/0.49.0/templates/terminating-gateways-rolebinding.yaml new file mode 100644 index 000000000..4271f8f59 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/terminating-gateways-rolebinding.yaml @@ -0,0 +1,26 @@ +{{- if .Values.terminatingGateways.enabled }} +{{- $root := . }} +{{- range .Values.terminatingGateways.gateways }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: terminating-gateway + terminating-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" $root }}-{{ .name }} +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/terminating-gateways-serviceaccount.yaml b/charts/hashicorp/consul/0.49.0/templates/terminating-gateways-serviceaccount.yaml new file mode 100644 index 000000000..211fb5c72 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/terminating-gateways-serviceaccount.yaml @@ -0,0 +1,35 @@ +{{- if .Values.terminatingGateways.enabled }} +{{- $root := . }} +{{- $defaults := .Values.terminatingGateways.defaults }} +{{- range .Values.terminatingGateways.gateways }} +{{- $serviceAccount := .serviceAccount }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" $root }}-{{ .name }} + namespace: {{ $root.Release.Namespace }} + labels: + app: {{ template "consul.name" $root }} + chart: {{ template "consul.chart" $root }} + heritage: {{ $root.Release.Service }} + release: {{ $root.Release.Name }} + component: terminating-gateway + terminating-gateway-name: {{ template "consul.fullname" $root }}-{{ .name }} + {{- if (or $defaults.serviceAccount.annotations $serviceAccount.annotations) }} + annotations: + {{- if $defaults.serviceAccount.annotations }} + {{ tpl $defaults.serviceAccount.annotations $root | nindent 4 | trim }} + {{- end }} + {{- if $serviceAccount.annotations }} + {{ tpl $serviceAccount.annotations $root | nindent 4 | trim }} + {{- end }} + {{- end }} +{{- with $root.Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +--- +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/tests/test-runner.yaml b/charts/hashicorp/consul/0.49.0/templates/tests/test-runner.yaml new file mode 100644 index 000000000..b8b078003 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/tests/test-runner.yaml @@ -0,0 +1,78 @@ +{{- if .Values.tests.enabled }} +apiVersion: v1 +kind: Pod +metadata: + name: "{{ template "consul.fullname" . }}-test" + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + annotations: + "helm.sh/hook": test-success +spec: + {{- if .Values.global.tls.enabled }} + volumes: + {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + - name: consul-auto-encrypt-ca-cert + emptyDir: + medium: "Memory" + {{- end }} + {{- if and .Values.global.tls.enabled .Values.global.tls.enableAutoEncrypt }} + initContainers: + {{- include "consul.getAutoEncryptClientCA" . | nindent 2 }} + {{- end }} + containers: + - name: consul-test + image: "{{ .Values.global.image }}" + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + {{- if .Values.global.tls.enabled }} + - name: CONSUL_HTTP_ADDR + value: https://$(HOST_IP):8501 + - name: CONSUL_CACERT + value: /consul/tls/ca/tls.crt + {{- else }} + - name: CONSUL_HTTP_ADDR + value: http://$(HOST_IP):8500 + {{- end }} + {{- if .Values.global.tls.enabled }} + volumeMounts: + {{- if .Values.global.tls.enableAutoEncrypt }} + - name: consul-auto-encrypt-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- else }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + consul members | tee members.txt + if [ $(grep -c consul-server members.txt) != $(grep consul-server members.txt | grep -c alive) ] + then + echo "Failed because not all consul servers are available" + exit 1 + fi + + restartPolicy: Never +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/tls-init-cleanup-job.yaml b/charts/hashicorp/consul/0.49.0/templates/tls-init-cleanup-job.yaml new file mode 100644 index 000000000..9a8898cc1 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/tls-init-cleanup-job.yaml @@ -0,0 +1,67 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} +{{- if not .Values.global.secretsBackend.vault.enabled }} +# tls-init-cleanup job deletes Kubernetes secrets created by tls-init +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "consul.fullname" . }}-tls-init-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: tls-init-cleanup + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-delete-policy": hook-succeeded + {{- /* Hook weight needs to be 1 so that the service account is provisioned first */}} + "helm.sh/hook-weight": "1" +spec: + template: + metadata: + name: {{ template "consul.fullname" . }}-tls-init-cleanup + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: tls-init-cleanup + annotations: + "consul.hashicorp.com/connect-inject": "false" + spec: + restartPolicy: Never + serviceAccountName: {{ template "consul.fullname" . }}-tls-init-cleanup + containers: + - name: tls-init-cleanup + image: "{{ .Values.global.image }}" + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + command: + - "/bin/sh" + - "-ec" + - | + {{- if (not (and .Values.global.tls.caCert.secretName .Values.global.tls.caKey.secretName)) }} + curl -s -X DELETE --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \ + https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}/api/v1/namespaces/${NAMESPACE}/secrets/{{ template "consul.fullname" . }}-ca-cert \ + -H "Authorization: Bearer $( cat /var/run/secrets/kubernetes.io/serviceaccount/token )" + curl -s -X DELETE --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \ + https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}/api/v1/namespaces/${NAMESPACE}/secrets/{{ template "consul.fullname" . }}-ca-key \ + -H "Authorization: Bearer $( cat /var/run/secrets/kubernetes.io/serviceaccount/token )" + {{- end }} + curl -s -X DELETE --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \ + https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}/api/v1/namespaces/${NAMESPACE}/secrets/{{ template "consul.fullname" . }}-server-cert \ + -H "Authorization: Bearer $( cat /var/run/secrets/kubernetes.io/serviceaccount/token )" + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/tls-init-cleanup-podsecuritypolicy.yaml b/charts/hashicorp/consul/0.49.0/templates/tls-init-cleanup-podsecuritypolicy.yaml new file mode 100644 index 000000000..ed99d5f29 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/tls-init-cleanup-podsecuritypolicy.yaml @@ -0,0 +1,43 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and (and .Values.global.tls.enabled .Values.global.enablePodSecurityPolicies) (not .Values.server.serverCert.secretName)) }} +{{- if not .Values.global.secretsBackend.vault.enabled }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-tls-init-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: tls-init-cleanup + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-delete-policy": hook-succeeded +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'secret' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/tls-init-cleanup-role.yaml b/charts/hashicorp/consul/0.49.0/templates/tls-init-cleanup-role.yaml new file mode 100644 index 000000000..aa66e3edc --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/tls-init-cleanup-role.yaml @@ -0,0 +1,41 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} +{{- if not .Values.global.secretsBackend.vault.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-tls-init-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: tls-init-cleanup + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-delete-policy": hook-succeeded +rules: +- apiGroups: [""] + resources: + - secrets + resourceNames: + {{- if (not (and .Values.global.tls.caCert.secretName .Values.global.tls.caKey.secretName)) }} + - {{ template "consul.fullname" . }}-ca-cert + - {{ template "consul.fullname" . }}-ca-key + {{- end }} + - {{ template "consul.fullname" . }}-server-cert + verbs: + - delete +{{- if .Values.global.enablePodSecurityPolicies }} +- apiGroups: ["policy"] + resources: + - podsecuritypolicies + verbs: + - use + resourceNames: + - {{ template "consul.fullname" . }}-tls-init-cleanup +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/tls-init-cleanup-rolebinding.yaml b/charts/hashicorp/consul/0.49.0/templates/tls-init-cleanup-rolebinding.yaml new file mode 100644 index 000000000..0d3bfe38e --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/tls-init-cleanup-rolebinding.yaml @@ -0,0 +1,27 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} +{{- if not .Values.global.secretsBackend.vault.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-tls-init-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: tls-init-cleanup + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-delete-policy": hook-succeeded +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-tls-init-cleanup +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-tls-init-cleanup +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/tls-init-cleanup-serviceaccount.yaml b/charts/hashicorp/consul/0.49.0/templates/tls-init-cleanup-serviceaccount.yaml new file mode 100644 index 000000000..57e40dd3a --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/tls-init-cleanup-serviceaccount.yaml @@ -0,0 +1,26 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} +{{- if not .Values.global.secretsBackend.vault.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-tls-init-cleanup + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: tls-init-cleanup + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-delete-policy": hook-succeeded +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/tls-init-job.yaml b/charts/hashicorp/consul/0.49.0/templates/tls-init-job.yaml new file mode 100644 index 000000000..47dd6462b --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/tls-init-job.yaml @@ -0,0 +1,109 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} +{{- if not .Values.global.secretsBackend.vault.enabled }} +# tls-init job generate Consul cluster CA and certificates for the Consul servers +# and creates Kubernetes secrets for them. +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "consul.fullname" . }}-tls-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: tls-init + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation +spec: + template: + metadata: + name: {{ template "consul.fullname" . }}-tls-init + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: tls-init + annotations: + "consul.hashicorp.com/connect-inject": "false" + spec: + restartPolicy: Never + serviceAccountName: {{ template "consul.fullname" . }}-tls-init + {{- if (and .Values.global.tls.caCert.secretName .Values.global.tls.caKey.secretName) }} + volumes: + - name: consul-ca-cert + secret: + secretName: {{ .Values.global.tls.caCert.secretName }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + - name: consul-ca-key + secret: + secretName: {{ .Values.global.tls.caKey.secretName }} + items: + - key: {{ default "tls.key" .Values.global.tls.caKey.secretKey }} + path: tls.key + {{- end }} + containers: + - name: tls-init + image: "{{ .Values.global.imageK8S }}" + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + workingDir: /tmp + command: + - "/bin/sh" + - "-ec" + - | + # Suppress globbing so we can interpolate the $NAMESPACE environment variable + # and use * at the start of the dns name when setting -additional-dnsname. + set -o noglob + consul-k8s-control-plane tls-init \ + -log-level={{ .Values.global.logLevel }} \ + -log-json={{ .Values.global.logJSON }} \ + -domain={{ .Values.global.domain }} \ + -days=730 \ + -name-prefix={{ template "consul.fullname" . }} \ + -k8s-namespace=${NAMESPACE} \ + {{- if (and .Values.global.tls.caCert.secretName .Values.global.tls.caKey.secretName) }} + -ca=/consul/tls/ca/cert/tls.crt \ + -key=/consul/tls/ca/key/tls.key \ + {{- end }} + -additional-dnsname="{{ template "consul.fullname" . }}-server" \ + -additional-dnsname="*.{{ template "consul.fullname" . }}-server" \ + -additional-dnsname="*.{{ template "consul.fullname" . }}-server.${NAMESPACE}" \ + -additional-dnsname="{{ template "consul.fullname" . }}-server.${NAMESPACE}" \ + -additional-dnsname="*.{{ template "consul.fullname" . }}-server.${NAMESPACE}.svc" \ + -additional-dnsname="{{ template "consul.fullname" . }}-server.${NAMESPACE}.svc" \ + -additional-dnsname="*.server.{{ .Values.global.datacenter }}.{{ .Values.global.domain }}" \ + {{- range .Values.global.tls.serverAdditionalIPSANs }} + -additional-ipaddress={{ . }} \ + {{- end }} + {{- range .Values.global.tls.serverAdditionalDNSSANs }} + -additional-dnsname={{ . }} \ + {{- end }} + -dc={{ .Values.global.datacenter }} + {{- if (and .Values.global.tls.caCert.secretName .Values.global.tls.caKey.secretName) }} + volumeMounts: + - name: consul-ca-cert + mountPath: /consul/tls/ca/cert + readOnly: true + - name: consul-ca-key + mountPath: /consul/tls/ca/key + readOnly: true + {{- end }} + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/tls-init-podsecuritypolicy.yaml b/charts/hashicorp/consul/0.49.0/templates/tls-init-podsecuritypolicy.yaml new file mode 100644 index 000000000..5d2a39395 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/tls-init-podsecuritypolicy.yaml @@ -0,0 +1,43 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and (and .Values.global.tls.enabled .Values.global.enablePodSecurityPolicies) (not .Values.server.serverCert.secretName)) }} +{{- if not .Values.global.secretsBackend.vault.enabled }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-tls-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: tls-init + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'secret' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/tls-init-role.yaml b/charts/hashicorp/consul/0.49.0/templates/tls-init-role.yaml new file mode 100644 index 000000000..216602ee9 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/tls-init-role.yaml @@ -0,0 +1,38 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} +{{- if not .Values.global.secretsBackend.vault.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-tls-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: tls-init + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +rules: +- apiGroups: [""] + resources: + - secrets + verbs: + - create + - update + - get + - list +{{- if .Values.global.enablePodSecurityPolicies }} +- apiGroups: ["policy"] + resources: + - podsecuritypolicies + verbs: + - use + resourceNames: + - {{ template "consul.fullname" . }}-tls-init +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/tls-init-rolebinding.yaml b/charts/hashicorp/consul/0.49.0/templates/tls-init-rolebinding.yaml new file mode 100644 index 000000000..9b68d97d8 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/tls-init-rolebinding.yaml @@ -0,0 +1,27 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} +{{- if not .Values.global.secretsBackend.vault.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-tls-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: tls-init + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-tls-init +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-tls-init +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/tls-init-serviceaccount.yaml b/charts/hashicorp/consul/0.49.0/templates/tls-init-serviceaccount.yaml new file mode 100644 index 000000000..f8504da94 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/tls-init-serviceaccount.yaml @@ -0,0 +1,26 @@ +{{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if (and .Values.global.tls.enabled (not .Values.server.serverCert.secretName)) }} +{{- if not .Values.global.secretsBackend.vault.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-tls-init + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: tls-init + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/ui-ingress.yaml b/charts/hashicorp/consul/0.49.0/templates/ui-ingress.yaml new file mode 100644 index 000000000..0414a7cc2 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/ui-ingress.yaml @@ -0,0 +1,83 @@ +{{- if (and (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) (or (and (ne (.Values.ui.enabled | toString) "-") .Values.ui.enabled) (and (eq (.Values.ui.enabled | toString) "-") .Values.global.enabled)) (or (and (ne (.Values.ui.service.enabled | toString) "-") .Values.ui.service.enabled) (and (eq (.Values.ui.service.enabled | toString) "-") .Values.global.enabled))) }} +{{- if (and (ne (.Values.ui.ingress.enabled | toString) "-") .Values.ui.ingress.enabled) }} +{{- $serviceName := printf "%s-%s" (include "consul.fullname" .) "ui" -}} +{{- /* We use the kube version to determine if the apiVersion of networking.k8s.io should be v1 or v1beta1. +This is because while networks.k8s.io/v1 was introduced in Kubernetes v1.15+, the Ingress resource was +promoted to v1 only in Kubernetes v1.19+. This ensures the correct API version is chosen that supports +the Ingress resource. */}} +{{- if or ( gt .Capabilities.KubeVersion.Major "1" ) ( ge .Capabilities.KubeVersion.Minor "19" ) }} +apiVersion: networking.k8s.io/v1 +{{- else }} +apiVersion: networking.k8s.io/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ template "consul.fullname" . }}-ui + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: ui + {{- if .Values.ui.ingress.annotations }} + annotations: + {{ tpl .Values.ui.ingress.annotations . | nindent 4 | trim }} + {{- end }} +spec: + ingressClassName: {{ .Values.ui.ingress.ingressClassName }} + rules: + {{ $global := .Values.global }} + {{- if or ( gt .Capabilities.KubeVersion.Major "1" ) ( ge .Capabilities.KubeVersion.Minor "19" ) }} + {{- range .Values.ui.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range (.paths | default (list "/")) }} + {{- if (or (not $global.tls.enabled) (not $global.tls.httpsOnly)) }} + - backend: + service: + name: {{ $serviceName }} + port: + number: 80 + path: {{ . }} + pathType: {{ $.Values.ui.ingress.pathType }} + {{- end }} + {{- if $global.tls.enabled }} + - backend: + service: + name: {{ $serviceName }} + port: + number: 443 + path: {{ . }} + pathType: {{ $.Values.ui.ingress.pathType }} + {{- end }} + {{- end }} + {{- end }} + {{- else }} + {{- range .Values.ui.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range (.paths | default (list "/")) }} + {{- if (or (not $global.tls.enabled) (not $global.tls.httpsOnly)) }} + - backend: + serviceName: {{ $serviceName }} + servicePort: 80 + path: {{ . }} + {{- end }} + {{- if $global.tls.enabled }} + - backend: + serviceName: {{ $serviceName }} + servicePort: 443 + path: {{ . }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.ui.ingress.tls }} + tls: + {{- toYaml .Values.ui.ingress.tls | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/ui-service.yaml b/charts/hashicorp/consul/0.49.0/templates/ui-service.yaml new file mode 100644 index 000000000..dc2abf4fc --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/ui-service.yaml @@ -0,0 +1,46 @@ +{{- if (and (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) (or (and (ne (.Values.ui.enabled | toString) "-") .Values.ui.enabled) (and (eq (.Values.ui.enabled | toString) "-") .Values.global.enabled)) (or (and (ne (.Values.ui.service.enabled | toString) "-") .Values.ui.service.enabled) (and (eq (.Values.ui.service.enabled | toString) "-") .Values.global.enabled))) }} +# UI Service for Consul Server +apiVersion: v1 +kind: Service +metadata: + name: {{ template "consul.fullname" . }}-ui + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: ui + {{- if .Values.ui.service.annotations }} + annotations: + {{ tpl .Values.ui.service.annotations . | nindent 4 | trim }} + {{- end }} +spec: + selector: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + component: server + ports: + {{- if (or (not .Values.global.tls.enabled) (not .Values.global.tls.httpsOnly)) }} + - name: http + port: {{ .Values.ui.service.port.http }} + targetPort: 8500 + {{- if .Values.ui.service.type }}{{ if (and (eq .Values.ui.service.type "NodePort") .Values.ui.service.nodePort.http) }} + nodePort: {{ .Values.ui.service.nodePort.http }} + {{- end }}{{ end }} + {{- end }} + {{- if .Values.global.tls.enabled }} + - name: https + port: {{ .Values.ui.service.port.https }} + targetPort: 8501 + {{- if .Values.ui.service.type }}{{ if (and (eq .Values.ui.service.type "NodePort") .Values.ui.service.nodePort.https) }} + nodePort: {{ .Values.ui.service.nodePort.https }} + {{- end }}{{ end }} + {{- end }} + {{- if .Values.ui.service.type }} + type: {{ .Values.ui.service.type }} + {{- end }} + {{- if .Values.ui.service.additionalSpec }} + {{ tpl .Values.ui.service.additionalSpec . | nindent 2 | trim }} + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-clusterrole.yaml b/charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-clusterrole.yaml new file mode 100644 index 000000000..ce8dfb846 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-clusterrole.yaml @@ -0,0 +1,53 @@ +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName .Values.global.secretsBackend.vault.controllerRole .Values.global.secretsBackend.vault.controller.tlsCert.secretName .Values.global.secretsBackend.vault.controller.caCert.secretName) -}} +{{- if (and (or .Values.connectInject.enabled .Values.controller.enabled) (not $hasConfiguredWebhookCertsUsingVault)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "consul.fullname" . }}-webhook-cert-manager + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: webhook-cert-manager +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + verbs: + - get + - list + - watch + - patch +- apiGroups: + - apps + resources: + - deployments + resourceNames: + - {{ template "consul.fullname" . }}-webhook-cert-manager + verbs: + - get +{{- if .Values.global.enablePodSecurityPolicies }} +- apiGroups: + - policy + resources: + - podsecuritypolicies + resourceNames: + - {{ template "consul.fullname" . }}-webhook-cert-manager + verbs: + - use +{{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-clusterrolebinding.yaml b/charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-clusterrolebinding.yaml new file mode 100644 index 000000000..90192d596 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-clusterrolebinding.yaml @@ -0,0 +1,21 @@ +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName .Values.global.secretsBackend.vault.controllerRole .Values.global.secretsBackend.vault.controller.tlsCert.secretName .Values.global.secretsBackend.vault.controller.caCert.secretName) -}} +{{- if (and (or .Values.connectInject.enabled .Values.controller.enabled) (not $hasConfiguredWebhookCertsUsingVault)) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "consul.fullname" . }}-webhook-cert-manager + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: webhook-cert-manager +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "consul.fullname" . }}-webhook-cert-manager +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-webhook-cert-manager + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-configmap.yaml b/charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-configmap.yaml new file mode 100644 index 000000000..61520fe23 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-configmap.yaml @@ -0,0 +1,43 @@ +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName .Values.global.secretsBackend.vault.controllerRole .Values.global.secretsBackend.vault.controller.tlsCert.secretName .Values.global.secretsBackend.vault.controller.caCert.secretName) -}} +{{- if (and (or .Values.connectInject.enabled .Values.controller.enabled) (not $hasConfiguredWebhookCertsUsingVault)) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "consul.fullname" . }}-webhook-cert-manager-config + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: webhook-cert-manager +data: + webhook-config.json: |- + [ + {{- if .Values.connectInject.enabled }} + { + "name": "{{ template "consul.fullname" . }}-connect-injector", + "tlsAutoHosts": [ + "{{ template "consul.fullname" . }}-connect-injector", + "{{ template "consul.fullname" . }}-connect-injector.{{ .Release.Namespace }}", + "{{ template "consul.fullname" . }}-connect-injector.{{ .Release.Namespace }}.svc", + "{{ template "consul.fullname" . }}-connect-injector.{{ .Release.Namespace }}.svc.cluster.local" + ], + "secretName": "{{ template "consul.fullname" . }}-connect-inject-webhook-cert", + "secretNamespace": "{{ .Release.Namespace }}" + }{{- if and .Values.controller.enabled }},{{- end }}{{- end }} + {{- if and .Values.controller.enabled }} + { + "name": "{{ template "consul.fullname" . }}-controller", + "tlsAutoHosts": [ + "{{ template "consul.fullname" . }}-controller-webhook", + "{{ template "consul.fullname" . }}-controller-webhook.{{ .Release.Namespace }}", + "{{ template "consul.fullname" . }}-controller-webhook.{{ .Release.Namespace }}.svc", + "{{ template "consul.fullname" . }}-controller-webhook.{{ .Release.Namespace }}.svc.cluster.local" + ], + "secretName": "{{ template "consul.fullname" . }}-controller-webhook-cert", + "secretNamespace": "{{ .Release.Namespace }}" + } + {{- end }} + ] + {{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-deployment.yaml b/charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-deployment.yaml new file mode 100644 index 000000000..609f3314b --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-deployment.yaml @@ -0,0 +1,69 @@ +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName .Values.global.secretsBackend.vault.controllerRole .Values.global.secretsBackend.vault.controller.tlsCert.secretName .Values.global.secretsBackend.vault.controller.caCert.secretName) -}} +{{- if (and (or .Values.connectInject.enabled .Values.controller.enabled) (not $hasConfiguredWebhookCertsUsingVault)) }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "consul.fullname" . }}-webhook-cert-manager + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: webhook-cert-manager +spec: + replicas: 1 + selector: + matchLabels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: webhook-cert-manager + template: + metadata: + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: webhook-cert-manager + annotations: + "consul.hashicorp.com/connect-inject": "false" + "consul.hashicorp.com/config-checksum": {{ include (print $.Template.BasePath "/webhook-cert-manager-configmap.yaml") . | sha256sum }} + spec: + containers: + - command: + - "/bin/sh" + - "-ec" + - | + consul-k8s-control-plane webhook-cert-manager \ + -log-level={{ .Values.global.logLevel }} \ + -log-json={{ .Values.global.logJSON }} \ + -config-file=/bootstrap/config/webhook-config.json \ + -deployment-name={{ template "consul.fullname" . }}-webhook-cert-manager \ + -deployment-namespace={{ .Release.Namespace }} + image: {{ .Values.global.imageK8S }} + name: webhook-cert-manager + resources: + limits: + cpu: 100m + memory: 50Mi + requests: + cpu: 100m + memory: 50Mi + volumeMounts: + - name: config + mountPath: /bootstrap/config + terminationGracePeriodSeconds: 10 + serviceAccountName: {{ template "consul.fullname" . }}-webhook-cert-manager + volumes: + - name: config + configMap: + name: {{ template "consul.fullname" . }}-webhook-cert-manager-config + {{- if .Values.webhookCertManager.tolerations }} + tolerations: + {{ tpl .Values.webhookCertManager.tolerations . | indent 8 | trim }} + {{- end}} + +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-podsecuritypolicy.yaml b/charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-podsecuritypolicy.yaml new file mode 100644 index 000000000..833d90234 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-podsecuritypolicy.yaml @@ -0,0 +1,41 @@ +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName .Values.global.secretsBackend.vault.controllerRole .Values.global.secretsBackend.vault.controller.tlsCert.secretName .Values.global.secretsBackend.vault.controller.caCert.secretName) -}} +{{- if (and (or .Values.connectInject.enabled .Values.controller.enabled) .Values.global.enablePodSecurityPolicies (not $hasConfiguredWebhookCertsUsingVault)) }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-webhook-cert-manager + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: webhook-cert-manager +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-serviceaccount.yaml b/charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-serviceaccount.yaml new file mode 100644 index 000000000..e1680d6e5 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/templates/webhook-cert-manager-serviceaccount.yaml @@ -0,0 +1,20 @@ +{{ $hasConfiguredWebhookCertsUsingVault := (and .Values.global.secretsBackend.vault.enabled .Values.global.secretsBackend.vault.connectInjectRole .Values.global.secretsBackend.vault.connectInject.tlsCert.secretName .Values.global.secretsBackend.vault.connectInject.caCert.secretName .Values.global.secretsBackend.vault.controllerRole .Values.global.secretsBackend.vault.controller.tlsCert.secretName .Values.global.secretsBackend.vault.controller.caCert.secretName) -}} +{{- if (and (or .Values.connectInject.enabled .Values.controller.enabled) (not $hasConfiguredWebhookCertsUsingVault)) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-webhook-cert-manager + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: webhook-cert-manager + {{- with .Values.global.imagePullSecrets }} +imagePullSecrets: + {{- range . }} +- name: {{ .name }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/hashicorp/consul/0.49.0/values.yaml b/charts/hashicorp/consul/0.49.0/values.yaml new file mode 100644 index 000000000..1489faac7 --- /dev/null +++ b/charts/hashicorp/consul/0.49.0/values.yaml @@ -0,0 +1,3078 @@ +# Available parameters and their default values for the Consul chart. + +# Holds values that affect multiple components of the chart. +global: + # The main enabled/disabled setting. If true, servers, + # clients, Consul DNS and the Consul UI will be enabled. Each component can override + # this default via its component-specific "enabled" config. If false, no components + # will be installed by default and per-component opt-in is required, such as by + # setting `server.enabled` to true. + enabled: true + + # The default log level to apply to all components which do not otherwise override this setting. + # It is recommended to generally not set this below "info" unless actively debugging due to logging verbosity. + # One of "debug", "info", "warn", or "error". + # @type: string + logLevel: "info" + + # Enable all component logs to be output in JSON format. + # @type: boolean + logJSON: false + + # Set the prefix used for all resources in the Helm chart. If not set, + # the prefix will be `-consul`. + # @type: string + name: null + + # The domain Consul will answer DNS queries for + # (see `-domain` (https://www.consul.io/docs/agent/config/cli-flags#_domain)) and the domain services synced from + # Consul into Kubernetes will have, e.g. `service-name.service.consul`. + domain: consul + + # [Experimental] Configures the Cluster Peering feature. Requires Consul v1.13+ and Consul-K8s v0.45+. + peering: + # If true, the Helm chart enables Cluster Peering for the cluster. This option enables peering controllers and + # allows use of the PeeringAcceptor and PeeringDialer CRDs for establishing service mesh peerings. + enabled: false + tokenGeneration: + serverAddresses: + # Source can be set to "","consul" or "static". + # + # "" is the default source. If servers are enabled, it will check if `server.exposeService` is enabled, and read + # the addresses from that service to use as the peering token server addresses. If using admin partitions and + # only Consul client agents are enabled, the addresses in `externalServers.hosts` and `externalServers.grpcPort` + # will be used. + # + # "consul" will use the Consul advertise addresses in the peering token. + # + # "static" will use the addresses specified in `global.peering.tokenGeneration.serverAddresses.static`. + source: "" + # Static addresses must be formatted "hostname|ip:port" where the port is the Consul server(s)' grpc port. + # @type: array + static: [] + + # [Enterprise Only] Enabling `adminPartitions` allows creation of Admin Partitions in Kubernetes clusters. + # It additionally indicates that you are running Consul Enterprise v1.11+ with a valid Consul Enterprise + # license. Admin partitions enables deploying services across partitions, while sharing + # a set of Consul servers. + adminPartitions: + # If true, the Helm chart will enable Admin Partitions for the cluster. The clients in the server cluster + # must be installed in the default partition. Creation of Admin Partitions is only supported during installation. + # Admin Partitions cannot be installed via a Helm upgrade operation. Only Helm installs are supported. + enabled: false + + # The name of the Admin Partition. The partition name cannot be modified once the partition has been installed. + # Changing the partition name would require an un-install and a re-install with the updated name. + # Must be "default" in the server cluster ie the Kubernetes cluster that the Consul server pods are deployed onto. + name: "default" + + # Partition service properties. + service: + type: LoadBalancer + # Optionally set the nodePort value of the partition service if using a NodePort service. + # If not set and using a NodePort service, Kubernetes will automatically assign + # a port. + nodePort: + + # RPC node port + # @type: integer + rpc: null + + # Serf node port + # @type: integer + serf: null + + # HTTPS node port + # @type: integer + https: null + + # Annotations to apply to the partition service. + # + # ```yaml + # annotations: | + # "annotation-key": "annotation-value" + # ``` + # + # @type: string + annotations: null + + # The name (and tag) of the Consul Docker image for clients and servers. + # This can be overridden per component. This should be pinned to a specific + # version tag, otherwise you may inadvertently upgrade your Consul version. + # + # Examples: + # + # ```yaml + # # Consul 1.10.0 + # image: "consul:1.10.0" + # # Consul Enterprise 1.10.0 + # image: "hashicorp/consul-enterprise:1.10.0-ent" + # ``` + # @default: hashicorp/consul: + image: "hashicorp/consul:1.13.2" + + # Array of objects containing image pull secret names that will be applied to each service account. + # This can be used to reference image pull secrets if using a custom consul or consul-k8s-control-plane Docker image. + # See https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry for reference. + # + # Example: + # + # ```yaml + # imagePullSecrets: + # - name: pull-secret-name + # - name: pull-secret-name-2 + # ``` + # @type: array + imagePullSecrets: [] + + # The name (and tag) of the consul-k8s-control-plane Docker + # image that is used for functionality such as catalog sync. + # This can be overridden per component. + # @default: hashicorp/consul-k8s-control-plane: + imageK8S: hashicorp/consul-k8s-control-plane:0.49.0 + + # The name of the datacenter that the agents should + # register as. This can't be changed once the Consul cluster is up and running + # since Consul doesn't support an automatic way to change this value currently: + # https://github.com/hashicorp/consul/issues/1858. + datacenter: dc1 + + # Controls whether pod security policies are created for the Consul components + # created by this chart. See https://kubernetes.io/docs/concepts/policy/pod-security-policy/. + enablePodSecurityPolicies: false + + # secretsBackend is used to configure Vault as the secrets backend for the Consul on Kubernetes installation. + # The Vault cluster needs to have the Kubernetes Auth Method, KV2 and PKI secrets engines enabled + # and have necessary secrets, policies and roles created prior to installing Consul. + # See https://www.consul.io/docs/k8s/installation/vault for full instructions. + # + # The Vault cluster _must_ not have the Consul cluster installed by this Helm chart as its storage backend + # as that would cause a circular dependency. + # Vault can have Consul as its storage backend as long as that Consul cluster is not running on this Kubernetes cluster + # and is being managed separately from this Helm installation. + # + # Note: When using Vault KV2 secrets engines the "data" field is implicitly required for Vault API calls, + # secretName should be in the form of "vault-kv2-mount-path/data/secret-name". + # secretKey should be in the form of "key". + secretsBackend: + vault: + # Enabling the Vault secrets backend will replace Kubernetes secrets with referenced Vault secrets. + enabled: false + + # The Vault role for the Consul server. + # The role must be connected to the Consul server's service account. + # The role must also have a policy with read capabilities for the following secrets: + # - gossip encryption key defined by the `global.gossipEncryption.secretName` value + # - certificate issue path defined by the `server.serverCert.secretName` value + # - CA certificate defined by the `global.tls.caCert.secretName` value + # - replication token defined by the `global.acls.replicationToken.secretName` value if `global.federation.enabled` is `true` + # To discover the service account name of the Consul server, run + # ```shell-session + # $ helm template --show-only templates/server-serviceaccount.yaml hashicorp/consul + # ``` + # and check the name of `metadata.name`. + consulServerRole: "" + + # The Vault role for the Consul client. + # The role must be connected to the Consul client's service account. + # The role must also have a policy with read capabilities for the gossip encryption + # key defined by the `global.gossipEncryption.secretName` value. + # To discover the service account name of the Consul client, run + # ```shell-session + # $ helm template --show-only templates/client-serviceaccount.yaml hashicorp/consul + # ``` + # and check the name of `metadata.name`. + consulClientRole: "" + + # [Enterprise Only] The Vault role for the Consul client snapshot agent. + # The role must be connected to the Consul client snapshot agent's service account. + # The role must also have a policy with read capabilities for the snapshot agent config + # defined by the `client.snapshotAgent.configSecret.secretName` value. + # To discover the service account name of the Consul client, run + # ```shell-session + # $ helm template --show-only templates/client-snapshot-agent-serviceaccount.yaml --set client.snapshotAgent.enabled=true hashicorp/consul + # ``` + # and check the name of `metadata.name`. + consulSnapshotAgentRole: "" + + # A Vault role for the Consul `server-acl-init` job, which manages setting ACLs so that clients and components can obtain ACL tokens. + # The role must be connected to the `server-acl-init` job's service account. + # The role must also have a policy with read and write capabilities for the bootstrap, replication or partition tokens + # To discover the service account name of the `server-acl-init` job, run + # ```shell-session + # $ helm template --show-only templates/server-acl-init-serviceaccount.yaml \ + # --set global.acls.manageSystemACLs=true hashicorp/consul + # ``` + # and check the name of `metadata.name`. + manageSystemACLsRole: "" + + # [Enterprise Only] A Vault role that allows the Consul `partition-init` job to read a Vault secret for the partition ACL token. + # The `partition-init` job bootstraps Admin Partitions on Consul servers. + # . + # This role must be bound the `partition-init` job's service account. + # To discover the service account name of the `partition-init` job, run with Helm values for the client cluster: + # ```shell-session + # $ helm template --show-only templates/partition-init-serviceaccount.yaml -f client-cluster-values.yaml hashicorp/consul + # ``` + # and check the name of `metadata.name`. + adminPartitionsRole: "" + + # The Vault role to read Consul controller's webhook's + # CA and issue a certificate and private key. + # A Vault policy must be created which grants issue capabilities to + # `global.secretsBackend.vault.controller.tlsCert.secretName`. + controllerRole: "" + + # The Vault role to read Consul connect-injector webhook's CA + # and issue a certificate and private key. + # A Vault policy must be created which grants issue capabilities to + # `global.secretsBackend.vault.connectInject.tlsCert.secretName`. + connectInjectRole: "" + + # The Vault role for all Consul components to read the Consul's server's CA Certificate (unauthenticated). + # The role should be connected to the service accounts of all Consul components, or alternatively `*` since it + # will be used only against the `pki/cert/ca` endpoint which is unauthenticated. A policy must be created which grants + # read capabilities to `global.tls.caCert.secretName`, which is usually `pki/cert/ca`. + consulCARole: "" + + # This value defines additional annotations for + # Vault agent on any pods where it'll be running. + # This should be formatted as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + agentAnnotations: null + + # Configuration for Vault server CA certificate. This certificate will be mounted + # to any pod where Vault agent needs to run. + ca: + # The name of the Kubernetes or Vault secret that holds the Vault CA certificate. + # A Kubernetes secret must be in the same namespace that Consul is installed into. + secretName: "" + # The key within the Kubernetes or Vault secret that holds the Vault CA certificate. + secretKey: "" + + # Configuration for the Vault Connect CA provider. + # The provider will be configured to use the Vault Kubernetes auth method + # and therefore requires the role provided by `global.secretsBackend.vault.consulServerRole` + # to have permissions to the root and intermediate PKI paths. + # Please see https://www.consul.io/docs/connect/ca/vault#vault-acl-policies + # for information on how to configure the Vault policies. + connectCA: + # The address of the Vault server. + address: "" + + # The mount path of the Kubernetes auth method in Vault. + authMethodPath: "kubernetes" + + # The path to a PKI secrets engine for the root certificate. + # For more details, please refer to [Vault Connect CA configuration](https://www.consul.io/docs/connect/ca/vault#rootpkipath). + rootPKIPath: "" + + # The path to a PKI secrets engine for the generated intermediate certificate. + # For more details, please refer to [Vault Connect CA configuration](https://www.consul.io/docs/connect/ca/vault#intermediatepkipath). + intermediatePKIPath: "" + + # Additional Connect CA configuration in JSON format. + # Please refer to [Vault Connect CA configuration](https://www.consul.io/docs/connect/ca/vault#configuration) + # for all configuration options available for that provider. + # + # Example: + # + # ```yaml + # additionalConfig: | + # { + # "connect": [{ + # "ca_config": [{ + # "namespace": "my-vault-ns", + # "leaf_cert_ttl": "36h" + # }] + # }] + # } + # ``` + additionalConfig: | + {} + + controller: + # Configuration to the Vault Secret that Kubernetes will use on + # Kubernetes CRD creation, deletion, and update, to get TLS certificates + # used issued from vault to send webhooks to the controller. + tlsCert: + # The Vault secret path that issues TLS certificates for controller + # webhooks. + # @type: string + secretName: null + + # Configuration to the Vault Secret that Kubernetes will use on + # Kubernetes CRD creation, deletion, and update, to get CA certificates + # used issued from vault to send webhooks to the controller. + caCert: + # The Vault secret path that contains the CA certificate for controller + # webhooks. + # @type: string + secretName: null + + connectInject: + # Configuration to the Vault Secret that Kubernetes will use on + # Kubernetes pod creation, deletion, and update, to get CA certificates + # used issued from vault to send webhooks to the ConnectInject. + caCert: + # The Vault secret path that contains the CA certificate for + # Connect Inject webhooks. + # @type: string + secretName: null + + # Configuration to the Vault Secret that Kubernetes will use on + # Kubernetes pod creation, deletion, and update, to get TLS certificates + # used issued from vault to send webhooks to the ConnectInject. + tlsCert: + # The Vault secret path that issues TLS certificates for connect + # inject webhooks. + # @type: string + secretName: null + + # Configures Consul's gossip encryption key. + # (see `-encrypt` (https://www.consul.io/docs/agent/config/cli-flags#_encrypt)). + # By default, gossip encryption is not enabled. The gossip encryption key may be set automatically or manually. + # The recommended method is to automatically generate the key. + # To automatically generate and set a gossip encryption key, set autoGenerate to true. + # Values for secretName and secretKey should not be set if autoGenerate is true. + # To manually generate a gossip encryption key, set secretName and secretKey and use Consul to generate + # a key, saving this as a Kubernetes secret or Vault secret path and key. + # If `global.secretsBackend.vault.enabled=true`, be sure to add the "data" component of the secretName path as required by + # the Vault KV-2 secrets engine [see example]. + # + # ```shell-session + # $ kubectl create secret generic consul-gossip-encryption-key --from-literal=key=$(consul keygen) + # ``` + # + # Vault CLI Example: + # ```shell-session + # $ vault kv put consul/secrets/gossip key=$(consul keygen) + # ``` + # `gossipEncryption.secretName="consul/data/secrets/gossip"` + # `gossipEncryption.secretKey="key"` + + gossipEncryption: + # Automatically generate a gossip encryption key and save it to a Kubernetes or Vault secret. + autoGenerate: false + # The name of the Kubernetes secret or Vault secret path that holds the gossip + # encryption key. A Kubernetes secret must be in the same namespace that Consul is installed into. + secretName: "" + # The key within the Kubernetes secret or Vault secret key that holds the gossip + # encryption key. + secretKey: "" + + # A list of addresses of upstream DNS servers that are used to recursively resolve DNS queries. + # These values are given as `-recursor` flags to Consul servers and clients. + # See https://www.consul.io/docs/agent/config/cli-flags#_recursor for more details. + # If this is an empty array (the default), then Consul DNS will only resolve queries for the Consul top level domain (by default `.consul`). + # @type: array + recursors: [] + + # Enables TLS (https://learn.hashicorp.com/tutorials/consul/tls-encryption-secure) + # across the cluster to verify authenticity of the Consul servers and clients. + # Requires Consul v1.4.1+. + tls: + # If true, the Helm chart will enable TLS for Consul + # servers and clients and all consul-k8s-control-plane components, as well as generate certificate + # authority (optional) and server and client certificates. + enabled: false + + # If true, turns on the auto-encrypt feature on clients and servers. + # It also switches consul-k8s-control-plane components to retrieve the CA from the servers + # via the API. Requires Consul 1.7.1+. + enableAutoEncrypt: false + + # A list of additional DNS names to set as Subject Alternative Names (SANs) + # in the server certificate. This is useful when you need to access the + # Consul server(s) externally, for example, if you're using the UI. + # @type: array + serverAdditionalDNSSANs: [] + + # A list of additional IP addresses to set as Subject Alternative Names (SANs) + # in the server certificate. This is useful when you need to access the + # Consul server(s) externally, for example, if you're using the UI. + # @type: array + serverAdditionalIPSANs: [] + + # If true, `verify_outgoing`, `verify_server_hostname`, + # and `verify_incoming` for internal RPC communication will be set to `true` for Consul servers and clients. + # Set this to false to incrementally roll out TLS on an existing Consul cluster. + # Please see https://consul.io/docs/k8s/operations/tls-on-existing-cluster + # for more details. + verify: true + + # If true, the Helm chart will configure Consul to disable the HTTP port on + # both clients and servers and to only accept HTTPS connections. + httpsOnly: true + + # A secret containing the certificate of the CA to use for TLS communication within the Consul cluster. + # If you have generated the CA yourself with the consul CLI, you could use the following command to create the secret + # in Kubernetes: + # + # ```shell-session + # $ kubectl create secret generic consul-ca-cert \ + # --from-file='tls.crt=./consul-agent-ca.pem' + # ``` + # If you are using Vault as a secrets backend with TLS, `caCert.secretName` must be provided and should reference + # the CA path for your PKI secrets engine. This should be of the form `pki/cert/ca` where `pki` is the mount point of your PKI secrets engine. + # A read policy must be created and associated with the CA cert path for `global.tls.caCert.secretName`. + # This will be consumed by the `global.secretsBackend.vault.consulCARole` role by all Consul components. + # When using Vault the secretKey is not used. + caCert: + # The name of the Kubernetes or Vault secret that holds the CA certificate. + # @type: string + secretName: null + # The key within the Kubernetes or Vault secret that holds the CA certificate. + # @type: string + secretKey: null + + # A Kubernetes or Vault secret containing the private key of the CA to use for + # TLS communication within the Consul cluster. If you have generated the CA yourself + # with the consul CLI, you could use the following command to create the secret + # in Kubernetes: + # + # ```shell-session + # $ kubectl create secret generic consul-ca-key \ + # --from-file='tls.key=./consul-agent-ca-key.pem' + # ``` + # + # Note that we need the CA key so that we can generate server and client certificates. + # It is particularly important for the client certificates since they need to have host IPs + # as Subject Alternative Names. In the future, we may support bringing your own server + # certificates. + caKey: + # The name of the Kubernetes or Vault secret that holds the CA key. + # @type: string + secretName: null + # The key within the Kubernetes or Vault secret that holds the CA key. + # @type: string + secretKey: null + + # [Enterprise Only] `enableConsulNamespaces` indicates that you are running + # Consul Enterprise v1.7+ with a valid Consul Enterprise license and would + # like to make use of configuration beyond registering everything into + # the `default` Consul namespace. Additional configuration + # options are found in the `consulNamespaces` section of both the catalog sync + # and connect injector. + enableConsulNamespaces: false + + # Configure ACLs. + acls: + + # If true, the Helm chart will automatically manage ACL tokens and policies + # for all Consul and consul-k8s-control-plane components. + # This requires Consul >= 1.4. + manageSystemACLs: false + + # A Kubernetes or Vault secret containing the bootstrap token to use for + # creating policies and tokens for all Consul and consul-k8s-control-plane components. + # If set, we will skip ACL bootstrapping of the servers and will only + # initialize ACLs for the Consul clients and consul-k8s-control-plane system components. + bootstrapToken: + # The name of the Kubernetes or Vault secret that holds the bootstrap token. + secretName: null + # The key within the Kubernetes or Vault secret that holds the bootstrap token. + secretKey: null + + # If true, an ACL token will be created that can be used in secondary + # datacenters for replication. This should only be set to true in the + # primary datacenter since the replication token must be created from that + # datacenter. + # In secondary datacenters, the secret needs to be imported from the primary + # datacenter and referenced via `global.acls.replicationToken`. + createReplicationToken: false + + # replicationToken references a secret containing the replication ACL token. + # This token will be used by secondary datacenters to perform ACL replication + # and create ACL tokens and policies. + # This value is ignored if `bootstrapToken` is also set. + replicationToken: + # The name of the Kubernetes or Vault secret that holds the replication token. + # @type: string + secretName: null + # The key within the Kubernetes or Vault secret that holds the replication token. + # @type: string + secretKey: null + + # partitionToken references a Vault secret containing the ACL token to be used in non-default partitions. + # This value should only be provided in the default partition and only when setting + # the `global.secretsBackend.vault.enabled` value to true. + # Consul will use the value of the secret stored in Vault to create an ACL token in Consul with the value of the + # secret as the secretID for the token. + # In non-default, partitions set this secret as the `bootstrapToken`. + partitionToken: + # The name of the Vault secret that holds the partition token. + # @type: string + secretName: null + # The key within the Vault secret that holds the parition token. + # @type: string + secretKey: null + + + # [Enterprise Only] This value refers to a Kubernetes or Vault secret that you have created + # that contains your enterprise license. It is required if you are using an + # enterprise binary. Defining it here applies it to your cluster once a leader + # has been elected. If you are not using an enterprise image or if you plan to + # introduce the license key via another route, then set these fields to null. + # Note: the job to apply license runs on both Helm installs and upgrades. + enterpriseLicense: + # The name of the Kubernetes or Vault secret that holds the enterprise license. + # A Kubernetes secret must be in the same namespace that Consul is installed into. + # @type: string + secretName: null + # The key within the Kubernetes or Vault secret that holds the enterprise license. + # @type: string + secretKey: null + # Manages license autoload. Required in Consul 1.10.0+, 1.9.7+ and 1.8.12+. + enableLicenseAutoload: true + + # Configure federation. + federation: + # If enabled, this datacenter will be federation-capable. Only federation + # via mesh gateways is supported. + # Mesh gateways and servers will be configured to allow federation. + # Requires `global.tls.enabled`, `meshGateway.enabled` and `connectInject.enabled` + # to be true. Requires Consul 1.8+. + enabled: false + + # If true, the chart will create a Kubernetes secret that can be imported + # into secondary datacenters so they can federate with this datacenter. The + # secret contains all the information secondary datacenters need to contact + # and authenticate with this datacenter. This should only be set to true + # in your primary datacenter. The secret name is + # `-federation` (if setting `global.name`), otherwise + # `-consul-federation`. + createFederationSecret: false + + # The name of the primary datacenter. + # @type: string + primaryDatacenter: null + + # A list of addresses of the primary mesh gateways in the form `:`. + # (e.g. ["1.1.1.1:443", "2.3.4.5:443"] + # @type: array + primaryGateways: [] + + # If you are setting `global.federation.enabled` to true and are in a secondary datacenter, + # set `k8sAuthMethodHost` to the address of the Kubernetes API server of the secondary datacenter. + # This address must be reachable from the Consul servers in the primary datacenter. + # This auth method will be used to provision ACL tokens for Consul components and is different + # from the one used by the Consul Service Mesh. + # Please see the [Kubernetes Auth Method documentation](https://consul.io/docs/acl/auth-methods/kubernetes). + # + # You can retrieve this value from your `kubeconfig` by running: + # + # ```shell-session + # $ kubectl config view \ + # -o jsonpath="{.clusters[?(@.name=='')].cluster.server}" + # ``` + # + # @type: string + k8sAuthMethodHost: null + + # Configures metrics for Consul service mesh + metrics: + # Configures the Helm chart’s components + # to expose Prometheus metrics for the Consul service mesh. By default + # this includes gateway metrics and sidecar metrics. + # @type: boolean + enabled: false + + # Configures consul agent metrics. Only applicable if + # `global.metrics.enabled` is true. + # @type: boolean + enableAgentMetrics: false + + # Configures the retention time for metrics in Consul clients and + # servers. This must be greater than 0 for Consul clients and servers + # to expose any metrics at all. + # Only applicable if `global.metrics.enabled` is true. + # @type: string + agentMetricsRetentionTime: 1m + + # If true, mesh, terminating, and ingress gateways will expose their + # Envoy metrics on port `20200` at the `/metrics` path and all gateway pods + # will have Prometheus scrape annotations. Only applicable if `global.metrics.enabled` is true. + # @type: boolean + enableGatewayMetrics: true + + # For connect-injected pods, the consul sidecar is responsible for metrics merging. For ingress/mesh/terminating + # gateways, it additionally ensures the Consul services are always registered with their local Consul client. + # @type: map + consulSidecarContainer: + # Set default resources for consul sidecar. If null, that resource won't + # be set. + # These settings can be overridden on a per-pod basis via these annotations: + # + # - `consul.hashicorp.com/consul-sidecar-cpu-limit` + # - `consul.hashicorp.com/consul-sidecar-cpu-request` + # - `consul.hashicorp.com/consul-sidecar-memory-limit` + # - `consul.hashicorp.com/consul-sidecar-memory-request` + # @recurse: false + # @type: map + resources: + requests: + memory: "25Mi" + cpu: "20m" + limits: + memory: "50Mi" + cpu: "20m" + + # The name (and tag) of the Envoy Docker image used for the + # connect-injected sidecar proxies and mesh, terminating, and ingress gateways. + # See https://www.consul.io/docs/connect/proxies/envoy for full compatibility matrix between Consul and Envoy. + # @default: envoyproxy/envoy-alpine: + imageEnvoy: "envoyproxy/envoy:v1.23.1" + + # Configuration for running this Helm chart on the Red Hat OpenShift platform. + # This Helm chart currently supports OpenShift v4.x+. + openshift: + # If true, the Helm chart will create necessary configuration for running + # its components on OpenShift. + enabled: false + + # The time in seconds that the consul API client will wait for a response from + # the API before cancelling the request. + consulAPITimeout: 5s + +# Server, when enabled, configures a server cluster to run. This should +# be disabled if you plan on connecting to a Consul cluster external to +# the Kube cluster. +server: + + # If true, the chart will install all the resources necessary for a + # Consul server cluster. If you're running Consul externally and want agents + # within Kubernetes to join that cluster, this should probably be false. + # @default: global.enabled + # @type: boolean + enabled: "-" + + # The name of the Docker image (including any tag) for the containers running + # Consul server agents. + # @type: string + image: null + + # The number of server agents to run. This determines the fault tolerance of + # the cluster. Please see the deployment table (https://consul.io/docs/internals/consensus#deployment-table) + # for more information. + replicas: 3 + + # The number of servers that are expected to be running. + # It defaults to server.replicas. + # In most cases the default should be used, however if there are more + # servers in this datacenter than server.replicas it might make sense + # to override the default. This would be the case if two kube clusters + # were joined into the same datacenter and each cluster ran a certain number + # of servers. + # @type: int + bootstrapExpect: null + + # A secret containing a certificate & key for the server agents to use + # for TLS communication within the Consul cluster. Cert needs to be provided with + # additional DNS name SANs so that it will work within the Kubernetes cluster: + # + # Kubernetes Secrets backend: + # ```bash + # consul tls cert create -server -days=730 -domain=consul -ca=consul-agent-ca.pem \ + # -key=consul-agent-ca-key.pem -dc={{datacenter}} \ + # -additional-dnsname="{{fullname}}-server" \ + # -additional-dnsname="*.{{fullname}}-server" \ + # -additional-dnsname="*.{{fullname}}-server.{{namespace}}" \ + # -additional-dnsname="*.{{fullname}}-server.{{namespace}}.svc" \ + # -additional-dnsname="*.server.{{datacenter}}.{{domain}}" \ + # -additional-dnsname="server.{{datacenter}}.{{domain}}" + # ``` + # + # If you have generated the server-cert yourself with the consul CLI, you could use the following command + # to create the secret in Kubernetes: + # + # ```bash + # kubectl create secret generic consul-server-cert \ + # --from-file='tls.crt=./dc1-server-consul-0.pem' + # --from-file='tls.key=./dc1-server-consul-0-key.pem' + # ``` + # + # Vault Secrets backend: + # If you are using Vault as a secrets backend, a Vault Policy must be created which allows `["create", "update"]` + # capabilities on the PKI issuing endpoint, which is usually of the form `pki/issue/consul-server`. + # Please see the following guide for steps to generate a compatible certificate: + # https://learn.hashicorp.com/tutorials/consul/vault-pki-consul-secure-tls + # Note: when using TLS, both the `server.serverCert` and `global.tls.caCert` which points to the CA endpoint of this PKI engine + # must be provided. + serverCert: + # The name of the Vault secret that holds the PEM encoded server certificate. + # @type: string + secretName: null + + # Exposes the servers' gossip and RPC ports as hostPorts. To enable a client + # agent outside of the k8s cluster to join the datacenter, you would need to + # enable `server.exposeGossipAndRPCPorts`, `client.exposeGossipPorts`, and + # set `server.ports.serflan.port` to a port not being used on the host. Since + # `client.exposeGossipPorts` uses the hostPort 8301, + # `server.ports.serflan.port` must be set to something other than 8301. + exposeGossipAndRPCPorts: false + + # Configures ports for the consul servers. + ports: + # Configures the LAN gossip port for the consul servers. If you choose to + # enable `server.exposeGossipAndRPCPorts` and `client.exposeGossipPorts`, + # that will configure the LAN gossip ports on the servers and clients to be + # hostPorts, so if you are running clients and servers on the same node the + # ports will conflict if they are both 8301. When you enable + # `server.exposeGossipAndRPCPorts` and `client.exposeGossipPorts`, you must + # change this from the default to an unused port on the host, e.g. 9301. By + # default the LAN gossip port is 8301 and configured as a containerPort on + # the consul server Pods. + serflan: + port: 8301 + + # This defines the disk size for configuring the + # servers' StatefulSet storage. For dynamically provisioned storage classes, this is the + # desired size. For manually defined persistent volumes, this should be set to + # the disk size of the attached volume. + storage: 10Gi + + # The StorageClass to use for the servers' StatefulSet storage. It must be + # able to be dynamically provisioned if you want the storage + # to be automatically created. For example, to use + # local(https://kubernetes.io/docs/concepts/storage/storage-classes/#local) + # storage classes, the PersistentVolumeClaims would need to be manually created. + # A `null` value will use the Kubernetes cluster's default StorageClass. If a default + # StorageClass does not exist, you will need to create one. + # Refer to the [Read/Write Tuning](https://www.consul.io/docs/install/performance#read-write-tuning) + # section of the Server Performance Requirements documentation for considerations + # around choosing a performant storage class. + # + # ~> **Note:** The [Reference Architecture](https://learn.hashicorp.com/tutorials/consul/reference-architecture#hardware-sizing-for-consul-servers) + # contains best practices and recommendations for selecting suitable + # hardware sizes for your Consul servers. + # @type: string + storageClass: null + + # This will enable/disable Connect (https://consul.io/docs/connect). Setting this to true + # _will not_ automatically secure pod communication, this + # setting will only enable usage of the feature. Consul will automatically initialize + # a new CA and set of certificates. Additional Connect settings can be configured + # by setting the `server.extraConfig` value. + connect: true + + serviceAccount: + # This value defines additional annotations for the server service account. This should be formatted as a multi-line + # string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # The resource requests (CPU, memory, etc.) + # for each of the server agents. This should be a YAML map corresponding to a Kubernetes + # ResourceRequirements (https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#resourcerequirements-v1-core) + # object. NOTE: The use of a YAML string is deprecated. + # + # Example: + # + # ```yaml + # resources: + # requests: + # memory: '100Mi' + # cpu: '100m' + # limits: + # memory: '100Mi' + # cpu: '100m' + # ``` + # + # @recurse: false + # @type: map + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "100Mi" + cpu: "100m" + + # The security context for the server pods. This should be a YAML map corresponding to a + # Kubernetes [SecurityContext](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) object. + # By default, servers will run as non-root, with user ID `100` and group ID `1000`, + # which correspond to the consul user and group created by the Consul docker image. + # Note: if running on OpenShift, this setting is ignored because the user and group are set automatically + # by the OpenShift platform. + # @type: map + # @recurse: false + securityContext: + runAsNonRoot: true + runAsGroup: 1000 + runAsUser: 100 + fsGroup: 1000 + + # The container securityContext for each container in the server pods. In + # addition to the Pod's SecurityContext this can + # set the capabilities of processes running in the container and ensure the + # root file systems in the container is read-only. + # @type: map + # @recurse: true + containerSecurityContext: + # The consul server agent container + # @type: map + # @recurse: false + server: null + + # This value is used to carefully + # control a rolling update of Consul server agents. This value specifies the + # partition (https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions) + # for performing a rolling update. Please read the linked Kubernetes documentation + # and https://www.consul.io/docs/k8s/upgrade#upgrading-consul-servers for more information. + updatePartition: 0 + + # This configures the PodDisruptionBudget (https://kubernetes.io/docs/tasks/run-application/configure-pdb/) + # for the server cluster. + disruptionBudget: + # This will enable/disable registering a PodDisruptionBudget for the server + # cluster. If this is enabled, it will only register the budget so long as + # the server cluster is enabled. + enabled: true + + # The maximum number of unavailable pods. By default, this will be + # automatically computed based on the `server.replicas` value to be `(n/2)-1`. + # If you need to set this to `0`, you will need to add a + # --set 'server.disruptionBudget.maxUnavailable=0'` flag to the helm chart installation + # command because of a limitation in the Helm templating language. + # @type: integer + maxUnavailable: null + + # A raw string of extra JSON configuration (https://consul.io/docs/agent/options) for Consul + # servers. This will be saved as-is into a ConfigMap that is read by the Consul + # server agents. This can be used to add additional configuration that + # isn't directly exposed by the chart. + # + # Example: + # + # ```yaml + # extraConfig: | + # { + # "log_level": "DEBUG" + # } + # ``` + # + # This can also be set using Helm's `--set` flag using the following syntax: + # + # ```shell-session + # --set 'server.extraConfig="{"log_level": "DEBUG"}"' + # ``` + extraConfig: | + {} + + # A list of extra volumes to mount for server agents. This + # is useful for bringing in extra data that can be referenced by other configurations + # at a well known path, such as TLS certificates or Gossip encryption keys. The + # value of this should be a list of objects. + # + # Example: + # + # ```yaml + # extraVolumes: + # - type: secret + # name: consul-certs + # load: false + # ``` + # + # Each object supports the following keys: + # + # - `type` - Type of the volume, must be one of "configMap" or "secret". Case sensitive. + # + # - `name` - Name of the configMap or secret to be mounted. This also controls + # the path that it is mounted to. The volume will be mounted to `/consul/userconfig/`. + # + # - `load` - If true, then the agent will be + # configured to automatically load HCL/JSON configuration files from this volume + # with `-config-dir`. This defaults to false. + # + # @type: array + extraVolumes: [] + + # A list of sidecar containers. + # Example: + # + # ```yaml + # extraContainers: + # - name: extra-container + # image: example-image:latest + # command: + # - ... + # ``` + # @type: array + extraContainers: [] + + # This value defines the affinity (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) + # for server pods. It defaults to allowing only a single server pod on each node, which + # minimizes risk of the cluster becoming unusable if a node is lost. If you need + # to run more pods per node (for example, testing on Minikube), set this value + # to `null`. + # + # Example: + # + # ```yaml + # affinity: | + # podAntiAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # - labelSelector: + # matchLabels: + # app: {{ template "consul.name" . }} + # release: "{{ .Release.Name }}" + # component: server + # topologyKey: kubernetes.io/hostname + # ``` + affinity: | + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + component: server + topologyKey: kubernetes.io/hostname + + # Toleration settings for server pods. This + # should be a multi-line string matching the Tolerations + # (https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) array in a Pod spec. + tolerations: "" + + # Pod topology spread constraints for server pods. + # This should be a multi-line YAML string matching the `topologySpreadConstraints` array + # (https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) in a Pod Spec. + # + # This requires K8S >= 1.18 (beta) or 1.19 (stable). + # + # Example: + # + # ```yaml + # topologySpreadConstraints: | + # - maxSkew: 1 + # topologyKey: topology.kubernetes.io/zone + # whenUnsatisfiable: DoNotSchedule + # labelSelector: + # matchLabels: + # app: {{ template "consul.name" . }} + # release: "{{ .Release.Name }}" + # component: server + # ``` + topologySpreadConstraints: "" + + # This value defines `nodeSelector` (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # labels for server pod assignment, formatted as a multi-line string. + # + # Example: + # + # ```yaml + # nodeSelector: | + # beta.kubernetes.io/arch: amd64 + # ``` + # + # @type: string + nodeSelector: null + + # This value references an existing + # Kubernetes `priorityClassName` (https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#pod-priority) + # that can be assigned to server pods. + priorityClassName: "" + + # Extra labels to attach to the server pods. This should be a YAML map. + # + # Example: + # + # ```yaml + # extraLabels: + # labelKey: label-value + # anotherLabelKey: another-label-value + # ``` + # + # @type: map + extraLabels: null + + # This value defines additional annotations for + # server pods. This should be formatted as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # Configures a service to expose ports on the Consul servers over a Kubernetes Service. + exposeService: + # When enabled, deploys a Kubernetes Service to reach the Consul servers. + # @type: boolean + enabled: "-" + # Type of service, supports LoadBalancer or NodePort. + # @type: string + type: LoadBalancer + # If service is of type NodePort, configures the nodePorts. + nodePort: + # Configures the nodePort to expose the Consul server http port. + # @type: integer + http: null + # Configures the nodePort to expose the Consul server https port. + # @type: integer + https: null + # Configures the nodePort to expose the Consul server serf port. + # @type: integer + serf: null + # Configures the nodePort to expose the Consul server rpc port. + # @type: integer + rpc: null + # Configures the nodePort to expose the Consul server grpc port. + # @type: integer + grpc: null + # This value defines additional annotations for + # server pods. This should be formatted as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # Server service properties. + service: + # Annotations to apply to the server service. + # + # ```yaml + # annotations: | + # "annotation-key": "annotation-value" + # ``` + # + # @type: string + annotations: null + + # A list of extra environment variables to set within the stateful set. + # These could be used to include proxy settings required for cloud auto-join + # feature, in case kubernetes cluster is behind egress http proxies. Additionally, + # it could be used to configure custom consul parameters. + # @type: map + extraEnvironmentVars: {} + +# Configuration for Consul servers when the servers are running outside of Kubernetes. +# When running external servers, configuring these values is recommended +# if setting `global.tls.enableAutoEncrypt` to true +# or `global.acls.manageSystemACLs` to true. +externalServers: + # If true, the Helm chart will be configured to talk to the external servers. + # If setting this to true, you must also set `server.enabled` to false. + enabled: false + + # An array of external Consul server hosts that are used to make + # HTTPS connections from the components in this Helm chart. + # Valid values include IPs, DNS names, or Cloud auto-join string. + # The port must be provided separately below. + # Note: `client.join` must also be set to the hosts that should be + # used to join the cluster. In most cases, the `client.join` values + # should be the same, however, they may be different if you + # wish to use separate hosts for the HTTPS connections. + # @type: array + hosts: [] + + # The HTTPS port of the Consul servers. + httpsPort: 8501 + + # The GRPC port of the Consul servers. + grpcPort: 8503 + + # The server name to use as the SNI host header when connecting with HTTPS. + # @type: string + tlsServerName: null + + # If true, consul-k8s-control-plane components will ignore the CA set in + # `global.tls.caCert` when making HTTPS calls to Consul servers and + # will instead use the consul-k8s-control-plane image's system CAs for TLS verification. + # If false, consul-k8s-control-plane components will use `global.tls.caCert` when + # making HTTPS calls to Consul servers. + # **NOTE:** This does not affect Consul's internal RPC communication which will + # always use `global.tls.caCert`. + useSystemRoots: false + + # If you are setting `global.acls.manageSystemACLs` and + # `connectInject.enabled` to true, set `k8sAuthMethodHost` to the address of the Kubernetes API server. + # This address must be reachable from the Consul servers. + # Please see the Kubernetes Auth Method documentation (https://consul.io/docs/acl/auth-methods/kubernetes). + # + # You could retrieve this value from your `kubeconfig` by running: + # + # ```shell-session + # $ kubectl config view \ + # -o jsonpath="{.clusters[?(@.name=='')].cluster.server}" + # ``` + # + # @type: string + k8sAuthMethodHost: null + +# Values that configure running a Consul client on Kubernetes nodes. +client: + # If true, the chart will install all + # the resources necessary for a Consul client on every Kubernetes node. This _does not_ require + # `server.enabled`, since the agents can be configured to join an external cluster. + # @default: global.enabled + # @type: boolean + enabled: "-" + + # The name of the Docker image (including any tag) for the containers + # running Consul client agents. + # @type: string + image: null + + # A list of valid `-retry-join` values (https://www.consul.io/docs/agent/config/cli-flags#_retry_join). + # If this is `null` (default), then the clients will attempt to automatically + # join the server cluster running within Kubernetes. + # This means that with `server.enabled` set to true, clients will automatically + # join that cluster. If `server.enabled` is not true, then a value must be + # specified so the clients can join a valid cluster. + # @type: array + join: null + + # An absolute path to a directory on the host machine to use as the Consul + # client data directory. If set to the empty string or null, the Consul agent + # will store its data in the Pod's local filesystem (which will + # be lost if the Pod is deleted). Security Warning: If setting this, Pod Security + # Policies _must_ be enabled on your cluster and in this Helm chart (via the + # `global.enablePodSecurityPolicies` setting) to prevent other pods from + # mounting the same host path and gaining access to all of Consul's data. + # Consul's data is not encrypted at rest. + # @type: string + dataDirectoryHostPath: null + + # If true, agents will enable their GRPC listener on + # port 8502 and expose it to the host. This will use slightly more resources, but is + # required for Connect. + grpc: true + + # nodeMeta specifies an arbitrary metadata key/value pair to associate with the node + # (see https://www.consul.io/docs/agent/config/cli-flags#_node_meta) + nodeMeta: + pod-name: ${HOSTNAME} + host-ip: ${HOST_IP} + + # If true, the Helm chart will expose the clients' gossip ports as hostPorts. + # This is only necessary if pod IPs in the k8s cluster are not directly routable + # and the Consul servers are outside of the k8s cluster. + # This also changes the clients' advertised IP to the `hostIP` rather than `podIP`. + exposeGossipPorts: false + + serviceAccount: + # This value defines additional annotations for the client service account. This should be formatted as a multi-line + # string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # The resource settings for Client agents. + # NOTE: The use of a YAML string is deprecated. Instead, set directly as a + # YAML map. + # @recurse: false + # @type: map + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "100Mi" + cpu: "100m" + + # The security context for the client pods. This should be a YAML map corresponding to a + # Kubernetes [SecurityContext](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) object. + # By default, servers will run as non-root, with user ID `100` and group ID `1000`, + # which correspond to the consul user and group created by the Consul docker image. + # Note: if running on OpenShift, this setting is ignored because the user and group are set automatically + # by the OpenShift platform. + # @type: map + # @recurse: false + securityContext: + runAsNonRoot: true + runAsGroup: 1000 + runAsUser: 100 + fsGroup: 1000 + + # The container securityContext for each container in the client pods. In + # addition to the Pod's SecurityContext this can + # set the capabilities of processes running in the container and ensure the + # root file systems in the container is read-only. + # @type: map + # @recurse: true + containerSecurityContext: + # The consul client agent container + # @type: map + # @recurse: false + client: null + # The acl-init initContainer + # @type: map + # @recurse: false + aclInit: null + # The tls-init initContainer + # @type: map + # @recurse: false + tlsInit: null + + # A raw string of extra JSON configuration (https://consul.io/docs/agent/options) for Consul + # clients. This will be saved as-is into a ConfigMap that is read by the Consul + # client agents. This can be used to add additional configuration that + # isn't directly exposed by the chart. + # + # Example: + # + # ```yaml + # extraConfig: | + # { + # "log_level": "DEBUG" + # } + # ``` + # + # This can also be set using Helm's `--set` flag using the following syntax: + # + # ```shell-session + # --set 'client.extraConfig="{"log_level": "DEBUG"}"' + # ``` + extraConfig: | + {} + + # A list of extra volumes to mount for client agents. This + # is useful for bringing in extra data that can be referenced by other configurations + # at a well known path, such as TLS certificates or Gossip encryption keys. The + # value of this should be a list of objects. + # + # Example: + # + # ```yaml + # extraVolumes: + # - type: secret + # name: consul-certs + # load: false + # ``` + # + # Each object supports the following keys: + # + # - `type` - Type of the volume, must be one of "configMap" or "secret". Case sensitive. + # + # - `name` - Name of the configMap or secret to be mounted. This also controls + # the path that it is mounted to. The volume will be mounted to `/consul/userconfig/`. + # + # - `load` - If true, then the agent will be + # configured to automatically load HCL/JSON configuration files from this volume + # with `-config-dir`. This defaults to false. + # + # @type: array + extraVolumes: [] + + # A list of sidecar containers. + # Example: + # + # ```yaml + # extraContainers: + # - name: extra-container + # image: example-image:latest + # command: + # - ... + # ``` + # @type: array + extraContainers: [] + + # Toleration Settings for Client pods + # This should be a multi-line string matching the Toleration array + # in a PodSpec. + # The example below will allow Client pods to run on every node + # regardless of taints + # + # ```yaml + # tolerations: | + # - operator: Exists + # ``` + tolerations: "" + + # nodeSelector labels for client pod assignment, formatted as a multi-line string. + # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + # + # Example: + # + # ```yaml + # nodeSelector: | + # beta.kubernetes.io/arch: amd64 + # ``` + # @type: string + nodeSelector: null + + # Affinity Settings for Client pods, formatted as a multi-line YAML string. + # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + # + # Example: + # + # ```yaml + # affinity: | + # nodeAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # nodeSelectorTerms: + # - matchExpressions: + # - key: node-role.kubernetes.io/master + # operator: DoesNotExist + # ``` + # @type: string + affinity: null + + # This value references an existing + # Kubernetes `priorityClassName` (https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#pod-priority) + # that can be assigned to client pods. + priorityClassName: "" + + # This value defines additional annotations for + # client pods. This should be formatted as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # Extra labels to attach to the client pods. This should be a regular YAML map. + # + # Example: + # + # ```yaml + # extraLabels: + # labelKey: label-value + # anotherLabelKey: another-label-value + # ``` + # + # @type: map + extraLabels: null + + # A list of extra environment variables to set within the stateful set. + # These could be used to include proxy settings required for cloud auto-join + # feature, in case kubernetes cluster is behind egress http proxies. Additionally, + # it could be used to configure custom consul parameters. + # @type: map + extraEnvironmentVars: {} + + # This value defines the Pod DNS policy (https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy) + # for client pods to use. + # @type: string + dnsPolicy: null + + # hostNetwork defines whether or not we use host networking instead of hostPort in the event + # that a CNI plugin doesn't support `hostPort`. This has security implications and is not recommended + # as doing so gives the consul client unnecessary access to all network traffic on the host. + # In most cases, pod network and host network are on different networks so this should be + # combined with `dnsPolicy: ClusterFirstWithHostNet` + hostNetwork: false + + # updateStrategy for the DaemonSet. + # See https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#daemonset-update-strategy. + # This should be a multi-line string mapping directly to the updateStrategy + # + # Example: + # + # ```yaml + # updateStrategy: | + # rollingUpdate: + # maxUnavailable: 5 + # type: RollingUpdate + # ``` + # + # @type: string + updateStrategy: null + + # [Enterprise Only] Values for setting up and running snapshot agents + # (https://consul.io/commands/snapshot/agent) + # within the Consul clusters. They are required to be co-located with Consul clients, + # so will inherit the clients' nodeSelector, tolerations and affinity. + snapshotAgent: + # If true, the chart will install resources necessary to run the snapshot agent. + enabled: false + + # The number of snapshot agents to run. + replicas: 2 + + # Interval at which to perform snapshots. + # See https://www.consul.io/commands/snapshot/agent#interval + # @type: string + interval: 1h + + # A Kubernetes or Vault secret that should be manually created to contain the entire + # config to be used on the snapshot agent. + # This is the preferred method of configuration since there are usually storage + # credentials present. Please see Snapshot agent config (https://consul.io/commands/snapshot/agent#config-file-options) + # for details. + configSecret: + # The name of the Kubernetes secret or Vault secret path that holds the snapshot agent config. + # @type: string + secretName: null + # The key within the Kubernetes secret or Vault secret key that holds the snapshot agent config. + # @type: string + secretKey: null + + serviceAccount: + # This value defines additional annotations for the snapshot agent service account. This should be formatted as a + # multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # The resource settings for snapshot agent pods. + # @recurse: false + # @type: map + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + + # Optional PEM-encoded CA certificate that will be added to the trusted system CAs. + # Useful if using an S3-compatible storage exposing a self-signed certificate. + # + # Example: + # + # ```yaml + # caCert: | + # -----BEGIN CERTIFICATE----- + # MIIC7jCCApSgAwIBAgIRAIq2zQEVexqxvtxP6J0bXAwwCgYIKoZIzj0EAwIwgbkx + # ... + # ``` + # @type: string + caCert: null + +# Configuration for DNS configuration within the Kubernetes cluster. +# This creates a service that routes to all agents (client or server) +# for serving DNS requests. This DOES NOT automatically configure kube-dns +# today, so you must still manually configure a `stubDomain` with kube-dns +# for this to have any effect: +# https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/#configure-stub-domain-and-upstream-dns-servers +dns: + # @type: boolean + enabled: "-" + + # If true, services using Consul Connect will use Consul DNS + # for default DNS resolution. The DNS lookups fall back to the nameserver IPs + # listed in /etc/resolv.conf if not found in Consul. + # @type: boolean + enableRedirection: false + + # Used to control the type of service created. For + # example, setting this to "LoadBalancer" will create an external load + # balancer (for supported K8S installations) + type: ClusterIP + + # Set a predefined cluster IP for the DNS service. + # Useful if you need to reference the DNS service's IP + # address in CoreDNS config. + # @type: string + clusterIP: null + + # Extra annotations to attach to the dns service + # This should be a multi-line string of + # annotations to apply to the dns Service + # @type: string + annotations: null + + # Additional ServiceSpec values + # This should be a multi-line string mapping directly to a Kubernetes + # ServiceSpec object. + # @type: string + additionalSpec: null + +# Values that configure the Consul UI. +ui: + # If true, the UI will be enabled. This will + # only _enable_ the UI, it doesn't automatically register any service for external + # access. The UI will only be enabled on server agents. If `server.enabled` is + # false, then this setting has no effect. To expose the UI in some way, you must + # configure `ui.service`. + # @default: global.enabled + # @type: boolean + enabled: "-" + + # Configure the service for the Consul UI. + service: + # This will enable/disable registering a + # Kubernetes Service for the Consul UI. This value only takes effect if `ui.enabled` is + # true and taking effect. + enabled: true + + # The service type to register. + # @type: string + type: null + + # Set the port value of the UI service. + port: + + # HTTP port. + http: 80 + + # HTTPS port. + https: 443 + + # Optionally set the nodePort value of the ui service if using a NodePort service. + # If not set and using a NodePort service, Kubernetes will automatically assign + # a port. + nodePort: + + # HTTP node port + # @type: integer + http: null + + # HTTPS node port + # @type: integer + https: null + + # Annotations to apply to the UI service. + # + # Example: + # + # ```yaml + # annotations: | + # 'annotation-key': annotation-value + # ``` + # @type: string + annotations: null + + # Additional ServiceSpec values + # This should be a multi-line string mapping directly to a Kubernetes + # ServiceSpec object. + # @type: string + additionalSpec: null + + # Configure Ingress for the Consul UI. + # If `global.tls.enabled` is set to `true`, the Ingress will expose + # the port 443 on the UI service. Please ensure the Ingress Controller + # supports SSL pass-through and it is enabled to ensure traffic forwarded + # to port 443 has not been TLS terminated. + ingress: + # This will create an Ingress resource for the Consul UI. + # @type: boolean + enabled: false + + # Optionally set the ingressClassName. + ingressClassName: "" + + # pathType override - see: https://kubernetes.io/docs/concepts/services-networking/ingress/#path-types + pathType: Prefix + + # hosts is a list of host name to create Ingress rules. + # + # ```yaml + # hosts: + # - host: foo.bar + # paths: + # - /example + # - /test + # ``` + # + # @type: array + hosts: [] + + # tls is a list of hosts and secret name in an Ingress + # which tells the Ingress controller to secure the channel. + # + # ```yaml + # tls: + # - hosts: + # - chart-example.local + # secretName: testsecret-tls + # ``` + # @type: array + tls: [] + + # Annotations to apply to the UI ingress. + # + # Example: + # + # ```yaml + # annotations: | + # 'annotation-key': annotation-value + # ``` + # @type: string + annotations: null + + # Configurations for displaying metrics in the UI. + metrics: + # Enable displaying metrics in the UI. The default value of "-" + # will inherit from `global.metrics.enabled` value. + # @type: boolean + # @default: global.metrics.enabled + enabled: "-" + # Provider for metrics. See + # https://www.consul.io/docs/agent/options#ui_config_metrics_provider + # This value is only used if `ui.enabled` is set to true. + # @type: string + provider: "prometheus" + + # baseURL is the URL of the prometheus server, usually the service URL. + # This value is only used if `ui.enabled` is set to true. + # @type: string + baseURL: http://prometheus-server + + # Corresponds to https://www.consul.io/docs/agent/options#ui_config_dashboard_url_templates configuration. + dashboardURLTemplates: + # Sets https://www.consul.io/docs/agent/options#ui_config_dashboard_url_templates_service. + service: "" + +# Configure the catalog sync process to sync K8S with Consul +# services. This can run bidirectional (default) or unidirectionally (Consul +# to K8S or K8S to Consul only). +# +# This process assumes that a Consul agent is available on the host IP. +# This is done automatically if clients are enabled. If clients are not +# enabled then set the node selection so that it chooses a node with a +# Consul agent. +syncCatalog: + # True if you want to enable the catalog sync. Set to "-" to inherit from + # global.enabled. + enabled: false + + # The name of the Docker image (including any tag) for consul-k8s-control-plane + # to run the sync program. + # @type: string + image: null + + # If true, all valid services in K8S are + # synced by default. If false, the service must be annotated + # (https://consul.io/docs/k8s/service-sync#sync-enable-disable) properly to sync. + # In either case an annotation can override the default. + default: true + + # Optional priorityClassName. + priorityClassName: "" + + # If true, will sync Kubernetes services to Consul. This can be disabled to + # have a one-way sync. + toConsul: true + + # If true, will sync Consul services to Kubernetes. This can be disabled to + # have a one-way sync. + toK8S: true + + # Service prefix to prepend to services before registering + # with Kubernetes. For example "consul-" will register all services + # prepended with "consul-". (Consul -> Kubernetes sync) + # @type: string + k8sPrefix: null + + # List of k8s namespaces to sync the k8s services from. + # If a k8s namespace is not included in this list or is listed in `k8sDenyNamespaces`, + # services in that k8s namespace will not be synced even if they are explicitly + # annotated. Use `["*"]` to automatically allow all k8s namespaces. + # + # For example, `["namespace1", "namespace2"]` will only allow services in the k8s + # namespaces `namespace1` and `namespace2` to be synced and registered + # with Consul. All other k8s namespaces will be ignored. + # + # To deny all namespaces, set this to `[]`. + # + # Note: `k8sDenyNamespaces` takes precedence over values defined here. + # @type: array + k8sAllowNamespaces: ["*"] + + # List of k8s namespaces that should not have their + # services synced. This list takes precedence over `k8sAllowNamespaces`. + # `*` is not supported because then nothing would be allowed to sync. + # + # For example, if `k8sAllowNamespaces` is `["*"]` and `k8sDenyNamespaces` is + # `["namespace1", "namespace2"]`, then all k8s namespaces besides `namespace1` + # and `namespace2` will be synced. + # @type: array + k8sDenyNamespaces: ["kube-system", "kube-public"] + + # [DEPRECATED] Use k8sAllowNamespaces and k8sDenyNamespaces instead. For + # backwards compatibility, if both this and the allow/deny lists are set, + # the allow/deny lists will be ignored. + # k8sSourceNamespace is the Kubernetes namespace to watch for service + # changes and sync to Consul. If this is not set then it will default + # to all namespaces. + # @type: string + k8sSourceNamespace: null + + # [Enterprise Only] These settings manage the catalog sync's interaction with + # Consul namespaces (requires consul-ent v1.7+). + # Also, `global.enableConsulNamespaces` must be true. + consulNamespaces: + # Name of the Consul namespace to register all + # k8s services into. If the Consul namespace does not already exist, + # it will be created. This will be ignored if `mirroringK8S` is true. + consulDestinationNamespace: "default" + + # If true, k8s services will be registered into a Consul namespace + # of the same name as their k8s namespace, optionally prefixed if + # `mirroringK8SPrefix` is set below. If the Consul namespace does not + # already exist, it will be created. Turning this on overrides the + # `consulDestinationNamespace` setting. + # `addK8SNamespaceSuffix` may no longer be needed if enabling this option. + # If mirroring is enabled, avoid creating any Consul resources in the following + # Kubernetes namespaces, as Consul currently reserves these namespaces for + # system use: "system", "universal", "operator", "root". + mirroringK8S: false + + # If `mirroringK8S` is set to true, `mirroringK8SPrefix` allows each Consul namespace + # to be given a prefix. For example, if `mirroringK8SPrefix` is set to "k8s-", a + # service in the k8s `staging` namespace will be registered into the + # `k8s-staging` Consul namespace. + mirroringK8SPrefix: "" + + # Appends Kubernetes namespace suffix to + # each service name synced to Consul, separated by a dash. + # For example, for a service 'foo' in the default namespace, + # the sync process will create a Consul service named 'foo-default'. + # Set this flag to true to avoid registering services with the same name + # but in different namespaces as instances for the same Consul service. + # Namespace suffix is not added if 'annotationServiceName' is provided. + addK8SNamespaceSuffix: true + + # Service prefix which prepends itself + # to Kubernetes services registered within Consul + # For example, "k8s-" will register all services prepended with "k8s-". + # (Kubernetes -> Consul sync) + # consulPrefix is ignored when 'annotationServiceName' is provided. + # NOTE: Updating this property to a non-null value for an existing installation will result in deregistering + # of existing services in Consul and registering them with a new name. + # @type: string + consulPrefix: null + + # Optional tag that is applied to all of the Kubernetes services + # that are synced into Consul. If nothing is set, defaults to "k8s". + # (Kubernetes -> Consul sync) + # @type: string + k8sTag: null + + # Defines the Consul synthetic node that all services + # will be registered to. + # NOTE: Changing the node name and upgrading the Helm chart will leave + # all of the previously sync'd services registered with Consul and + # register them again under the new Consul node name. The out-of-date + # registrations will need to be explicitly removed. + consulNodeName: "k8s-sync" + + # Syncs services of the ClusterIP type, which may + # or may not be broadly accessible depending on your Kubernetes cluster. + # Set this to false to skip syncing ClusterIP services. + syncClusterIPServices: true + + # Configures the type of syncing that happens for NodePort + # services. The valid options are: ExternalOnly, InternalOnly, ExternalFirst. + # + # - ExternalOnly will only use a node's ExternalIP address for the sync + # - InternalOnly use's the node's InternalIP address + # - ExternalFirst will preferentially use the node's ExternalIP address, but + # if it doesn't exist, it will use the node's InternalIP address instead. + nodePortSyncType: ExternalFirst + + # Refers to a Kubernetes secret that you have created that contains + # an ACL token for your Consul cluster which allows the sync process the correct + # permissions. This is only needed if ACLs are managed manually within the Consul cluster, i.e. `global.acls.manageSystemACLs` is `false`. + aclSyncToken: + # The name of the Kubernetes secret that holds the acl sync token. + # @type: string + secretName: null + # The key within the Kubernetes secret that holds the acl sync token. + # @type: string + secretKey: null + + # This value defines `nodeSelector` (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # labels for catalog sync pod assignment, formatted as a multi-line string. + # + # Example: + # + # ```yaml + # nodeSelector: | + # beta.kubernetes.io/arch: amd64 + # ``` + # + # @type: string + nodeSelector: null + + # Affinity Settings + # This should be a multi-line string matching the affinity object + # @type: string + affinity: null + + # Toleration Settings + # This should be a multi-line string matching the Toleration array + # in a PodSpec. + # @type: string + tolerations: null + + serviceAccount: + # This value defines additional annotations for the mesh gateways' service account. This should be formatted as a + # multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # The resource settings for sync catalog pods. + # @recurse: false + # @type: map + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + + # Override global log verbosity level. One of "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + + # Override the default interval to perform syncing operations creating Consul services. + # @type: string + consulWriteInterval: null + + # Extra labels to attach to the sync catalog pods. This should be a YAML map. + # + # Example: + # + # ```yaml + # extraLabels: + # labelKey: label-value + # anotherLabelKey: another-label-value + # ``` + # + # @type: map + extraLabels: null + + # This value defines additional annotations for + # the catalog sync pods. This should be formatted as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + +# Configures the automatic Connect sidecar injector. +connectInject: + # True if you want to enable connect injection. Set to "-" to inherit from + # global.enabled. + enabled: false + + # The number of deployment replicas. + replicas: 2 + + # Image for consul-k8s-control-plane that contains the injector. + # @type: string + image: null + + # If true, the injector will inject the + # Connect sidecar into all pods by default. Otherwise, pods must specify the + # injection annotation (https://consul.io/docs/k8s/connect#consul-hashicorp-com-connect-inject) + # to opt-in to Connect injection. If this is true, pods can use the same annotation + # to explicitly opt-out of injection. + default: false + + # Configures Transparent Proxy for Consul Service mesh services. + # Using this feature requires Consul 1.10.0-beta1+. + transparentProxy: + # If true, then all Consul Service mesh will run with transparent proxy enabled by default, + # i.e. we enforce that all traffic within the pod will go through the proxy. + # This value is overridable via the "consul.hashicorp.com/transparent-proxy" pod annotation. + defaultEnabled: true + + # If true, we will overwrite Kubernetes HTTP probes of the pod to point to the Envoy proxy instead. + # This setting is recommended because with traffic being enforced to go through the Envoy proxy, + # the probes on the pod will fail because kube-proxy doesn't have the right certificates + # to talk to Envoy. + # This value is also overridable via the "consul.hashicorp.com/transparent-proxy-overwrite-probes" annotation. + # Note: This value has no effect if transparent proxy is disabled on the pod. + defaultOverwriteProbes: true + + # This configures the PodDisruptionBudget (https://kubernetes.io/docs/tasks/run-application/configure-pdb/) + # for the service mesh sidecar injector. + disruptionBudget: + # This will enable/disable registering a PodDisruptionBudget for the + # service mesh sidecar injector. If this is enabled, it will only register the budget so long as + # the service mesh is enabled. + enabled: true + + # The maximum number of unavailable pods. By default, this will be + # automatically computed based on the `connectInject.replicas` value to be `(n/2)-1`. + # If you need to set this to `0`, you will need to add a + # --set 'connectInject.disruptionBudget.maxUnavailable=0'` flag to the helm chart installation + # command because of a limitation in the Helm templating language. + # @type: integer + maxUnavailable: null + + # Configures consul-cni plugin for Consul Service mesh services + cni: + # If true, then all traffic redirection setup will use the consul-cni plugin. + # Requires connectInject.enabled to also be true. + # @type: boolean + enabled: false + + # Log level for the installer and plugin. Overrides global.logLevel + # @type: string + logLevel: null + + # Location on the kubernetes node where the CNI plugin is installed. Shoud be the absolute path and start with a '/' + # Example on GKE: + # + # ```yaml + # cniBinDir: "/home/kubernetes/bin" + # ``` + # @type: string + cniBinDir: "/opt/cni/bin" + + # Location on the kubernetes node of all CNI configuration. Should be the absolute path and start with a '/' + # @type: string + cniNetDir: "/etc/cni/net.d" + + # If multus CNI plugin is enabled with consul-cni. When enabled, consul-cni will not be installed as a chained + # CNI plugin. Instead, a NetworkAttachementDefinition CustomResourceDefinition (CRD) will be created in the helm + # release namespace. Following multus plugin standards, an annotation is required in order for the consul-cni plugin + # to be executed and for your service to be added to the Consul Service Mesh. + # + # Add the annotation `'k8s.v1.cni.cncf.io/networks': '[{ "name":"consul-cni","namespace": "consul" }]'` to your pod + # to use the default installed NetworkAttachementDefinition CRD. + # + # Please refer to the [Multus Quickstart Guide](https://github.com/k8snetworkplumbingwg/multus-cni/blob/master/docs/quickstart.md) + # for more information about using multus. + # @type: string + multus: false + + # The resource settings for CNI installer daemonset. + # @recurse: false + # @type: map + resources: + requests: + memory: "75Mi" + cpu: "75m" + limits: + memory: "100Mi" + cpu: "100m" + + # Resource quotas for running the daemonset as system critical pods + resourceQuota: + pods: 5000 + + # The security context for the CNI installer daemonset. This should be a YAML map corresponding to a + # Kubernetes [SecurityContext](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) object. + # By default, servers will run as root, with user ID `0` and group ID `0`. + # Note: if running on OpenShift, this setting is ignored because the user and group are set automatically + # by the OpenShift platform. + # @type: map + # @recurse: false + securityContext: + runAsNonRoot: false + runAsGroup: 0 + runAsUser: 0 + + # updateStrategy for the CNI installer DaemonSet. + # See https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#daemonset-update-strategy. + # This should be a multi-line string mapping directly to the updateStrategy + # + # Example: + # + # ```yaml + # updateStrategy: | + # rollingUpdate: + # maxUnavailable: 5 + # type: RollingUpdate + # ``` + # + # @type: string + updateStrategy: null + + + # Configures metrics for Consul Connect services. All values are overridable + # via annotations on a per-pod basis. + metrics: + # If true, the connect-injector will automatically + # add prometheus annotations to connect-injected pods. It will also + # add a listener on the Envoy sidecar to expose metrics. The exposed + # metrics will depend on whether metrics merging is enabled: + # - If metrics merging is enabled: + # the Consul sidecar will run a merged metrics server + # combining Envoy sidecar and Connect service metrics, + # i.e. if your service exposes its own Prometheus metrics. + # - If metrics merging is disabled: + # the listener will just expose Envoy sidecar metrics. + # This will inherit from `global.metrics.enabled`. + defaultEnabled: "-" + # Configures the Consul sidecar to run a merged metrics server + # to combine and serve both Envoy and Connect service metrics. + # This feature is available only in Consul v1.10.0 or greater. + defaultEnableMerging: false + # Configures the port at which the Consul sidecar will listen on to return + # combined metrics. This port only needs to be changed if it conflicts with + # the application's ports. + defaultMergedMetricsPort: 20100 + # Configures the port Prometheus will scrape metrics from, by configuring + # the Pod annotation `prometheus.io/port` and the corresponding listener in + # the Envoy sidecar. + # NOTE: This is *not* the port that your application exposes metrics on. + # That can be configured with the + # `consul.hashicorp.com/service-metrics-port` annotation. + defaultPrometheusScrapePort: 20200 + # Configures the path Prometheus will scrape metrics from, by configuring the pod + # annotation `prometheus.io/path` and the corresponding handler in the Envoy + # sidecar. + # NOTE: This is *not* the path that your application exposes metrics on. + # That can be configured with the + # `consul.hashicorp.com/service-metrics-path` annotation. + defaultPrometheusScrapePath: "/metrics" + + # Used to pass arguments to the injected envoy sidecar. + # Valid arguments to pass to envoy can be found here: https://www.envoyproxy.io/docs/envoy/latest/operations/cli + # e.g "--log-level debug --disable-hot-restart" + # @type: string + envoyExtraArgs: null + + # Optional priorityClassName. + priorityClassName: "" + + # This value defines additional annotations for + # connect inject pods. This should be formatted as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # The Docker image for Consul to use when performing Connect injection. + # Defaults to global.image. + # @type: string + imageConsul: null + + # Override global log verbosity level. One of "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + + serviceAccount: + # This value defines additional annotations for the injector service account. This should be formatted as a + # multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # The resource settings for connect inject pods. + # @recurse: false + # @type: map + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + + # Sets the failurePolicy for the mutating webhook. By default this will cause pods not part of the consul installation to fail scheduling while the webhook + # is offline. This prevents a pod from skipping mutation if the webhook were to be momentarily offline. + # Once the webhook is back online the pod will be scheduled. + # In some environments such as Kind this may have an undesirable effect as it may prevent volume provisioner pods from running + # which can lead to hangs. In these environments it is recommend to use "Ignore" instead. + # This setting can be safely disabled by setting to "Ignore". + failurePolicy: "Fail" + + # Selector for restricting the webhook to only specific namespaces. + # Use with `connectInject.default: true` to automatically inject all pods in namespaces that match the selector. This should be set to a multiline string. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector + # for more details. + # + # By default, we exclude the kube-system namespace since usually users won't + # want those pods injected and also the local-path-storage namespace so that + # Kind (Kubernetes In Docker) can provision Pods used to create PVCs. + # Note that this exclusion is only supported in Kubernetes v1.21.1+. + # + # Example: + # + # ```yaml + # namespaceSelector: | + # matchLabels: + # namespace-label: label-value + # ``` + # @type: string + namespaceSelector: | + matchExpressions: + - key: "kubernetes.io/metadata.name" + operator: "NotIn" + values: ["kube-system","local-path-storage"] + + # List of k8s namespaces to allow Connect sidecar + # injection in. If a k8s namespace is not included or is listed in `k8sDenyNamespaces`, + # pods in that k8s namespace will not be injected even if they are explicitly + # annotated. Use `["*"]` to automatically allow all k8s namespaces. + # + # For example, `["namespace1", "namespace2"]` will only allow pods in the k8s + # namespaces `namespace1` and `namespace2` to have Connect sidecars injected + # and registered with Consul. All other k8s namespaces will be ignored. + # + # To deny all namespaces, set this to `[]`. + # + # Note: `k8sDenyNamespaces` takes precedence over values defined here and + # `namespaceSelector` takes precedence over both since it is applied first. + # `kube-system` and `kube-public` are never injected, even if included here. + # @type: array + k8sAllowNamespaces: ["*"] + + # List of k8s namespaces that should not allow Connect + # sidecar injection. This list takes precedence over `k8sAllowNamespaces`. + # `*` is not supported because then nothing would be allowed to be injected. + # + # For example, if `k8sAllowNamespaces` is `["*"]` and k8sDenyNamespaces is + # `["namespace1", "namespace2"]`, then all k8s namespaces besides "namespace1" + # and "namespace2" will be available for injection. + # + # Note: `namespaceSelector` takes precedence over this since it is applied first. + # `kube-system` and `kube-public` are never injected. + # @type: array + k8sDenyNamespaces: [] + + # [Enterprise Only] These settings manage the connect injector's interaction with + # Consul namespaces (requires consul-ent v1.7+). + # Also, `global.enableConsulNamespaces` must be true. + consulNamespaces: + # Name of the Consul namespace to register all + # k8s pods into. If the Consul namespace does not already exist, + # it will be created. This will be ignored if `mirroringK8S` is true. + consulDestinationNamespace: "default" + + # Causes k8s pods to be registered into a Consul namespace + # of the same name as their k8s namespace, optionally prefixed if + # `mirroringK8SPrefix` is set below. If the Consul namespace does not + # already exist, it will be created. Turning this on overrides the + # `consulDestinationNamespace` setting. If mirroring is enabled, avoid creating any Consul + # resources in the following Kubernetes namespaces, as Consul currently reserves these + # namespaces for system use: "system", "universal", "operator", "root". + mirroringK8S: false + + # If `mirroringK8S` is set to true, `mirroringK8SPrefix` allows each Consul namespace + # to be given a prefix. For example, if `mirroringK8SPrefix` is set to "k8s-", a + # pod in the k8s `staging` namespace will be registered into the + # `k8s-staging` Consul namespace. + mirroringK8SPrefix: "" + + # Selector labels for connectInject pod assignment, formatted as a multi-line string. + # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + # + # Example: + # + # ```yaml + # nodeSelector: | + # beta.kubernetes.io/arch: amd64 + # ``` + # @type: string + nodeSelector: null + + # Affinity Settings + # This should be a multi-line string matching the affinity object + # @type: string + affinity: null + + # Toleration Settings + # This should be a multi-line string matching the Toleration array + # in a PodSpec. + # @type: string + tolerations: null + + # Query that defines which Service Accounts + # can authenticate to Consul and receive an ACL token during Connect injection. + # The default setting, i.e. serviceaccount.name!=default, prevents the + # 'default' Service Account from logging in. + # If set to an empty string all service accounts can log in. + # This only has effect if ACLs are enabled. + # + # See https://www.consul.io/docs/acl/acl-auth-methods.html#binding-rules + # and https://www.consul.io/docs/acl/auth-methods/kubernetes.html#trusted-identity-attributes + # for more details. + # Requires Consul >= v1.5. + aclBindingRuleSelector: "serviceaccount.name!=default" + + # If you are not using global.acls.manageSystemACLs and instead manually setting up an + # auth method for Connect inject, set this to the name of your auth method. + overrideAuthMethodName: "" + + # Refers to a Kubernetes secret that you have created that contains + # an ACL token for your Consul cluster which allows the Connect injector the correct + # permissions. This is only needed if Consul namespaces [Enterprise Only] and ACLs + # are enabled on the Consul cluster and you are not setting + # `global.acls.manageSystemACLs` to `true`. + # This token needs to have `operator = "write"` privileges to be able to + # create Consul namespaces. + aclInjectToken: + # The name of the Vault secret that holds the ACL inject token. + # @type: string + secretName: null + # The key within the Vault secret that holds the ACL inject token. + # @type: string + secretKey: null + + sidecarProxy: + # The number of worker threads to be used by the Envoy proxy. + # By default the threading model of Envoy will use one thread per CPU core per envoy proxy. This + # leads to unnecessary thread and memory usage and leaves unnecessary idle connections open. It is + # advised to keep this number low for sidecars and high for edge proxies. + # This will control the `--concurrency` flag to Envoy. + # For additional information see also: https://blog.envoyproxy.io/envoy-threading-model-a8d44b922310 + # + # This setting can be overridden on a per-pod basis via this annotation: + # - `consul.hashicorp.com/consul-envoy-proxy-concurrency` + # @type: string + concurrency: 2 + + # Set default resources for sidecar proxy. If null, that resource won't + # be set. + # These settings can be overridden on a per-pod basis via these annotations: + # + # - `consul.hashicorp.com/sidecar-proxy-cpu-limit` + # - `consul.hashicorp.com/sidecar-proxy-cpu-request` + # - `consul.hashicorp.com/sidecar-proxy-memory-limit` + # - `consul.hashicorp.com/sidecar-proxy-memory-request` + # @type: map + resources: + requests: + # Recommended default: 100Mi + # @type: string + memory: null + # Recommended default: 100m + # @type: string + cpu: null + limits: + # Recommended default: 100Mi + # @type: string + memory: null + # Recommended default: 100m + # @type: string + cpu: null + + # The resource settings for the Connect injected init container. + # @recurse: false + # @type: map + initContainer: + resources: + requests: + memory: "25Mi" + cpu: "50m" + limits: + memory: "150Mi" + cpu: "50m" + +# Controller handles config entry custom resources. +# Requires consul >= 1.8.4. +# ServiceIntentions require consul 1.9+. +controller: + # Enables the controller for managing custom resources. + enabled: false + + # The number of deployment replicas. + replicas: 1 + + # Log verbosity level. One of "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + + serviceAccount: + # This value defines additional annotations for the controller service account. This should be formatted as a + # multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # The resource settings for controller pods. + # @recurse: false + # @type: map + resources: + limits: + cpu: 100m + memory: 50Mi + requests: + cpu: 100m + memory: 50Mi + + # Optional YAML string to specify a nodeSelector config. + # @type: string + nodeSelector: null + + # Optional YAML string to specify tolerations. + # @type: string + tolerations: null + + # Affinity Settings + # This should be a multi-line string matching the affinity object + # @type: string + affinity: null + + # Optional priorityClassName. + priorityClassName: "" + + # Refers to a Kubernetes secret that you have created that contains + # an ACL token for your Consul cluster which grants the controller process the correct + # permissions. This is only needed if you are managing ACLs yourself (i.e. not using + # `global.acls.manageSystemACLs`). + # + # If running Consul OSS, requires permissions: + # ```hcl + # operator = "write" + # service_prefix "" { + # policy = "write" + # intentions = "write" + # } + # ``` + # If running Consul Enterprise, talk to your account manager for assistance. + aclToken: + # The name of the Vault secret that holds the ACL token. + # @type: string + secretName: null + # The key within the Vault secret that holds the ACL token. + # @type: string + secretKey: null + +# Mesh Gateways enable Consul Connect to work across Consul datacenters. +meshGateway: + # If mesh gateways are enabled, a Deployment will be created that runs + # gateways and Consul Connect will be configured to use gateways. + # See https://www.consul.io/docs/connect/mesh_gateway.html + # Requirements: consul 1.6.0+ if using + # global.acls.manageSystemACLs. + enabled: false + + # Number of replicas for the Deployment. + replicas: 2 + + # What gets registered as WAN address for the gateway. + wanAddress: + # source configures where to retrieve the WAN address (and possibly port) + # for the mesh gateway from. + # Can be set to either: `Service`, `NodeIP`, `NodeName` or `Static`. + # + # - `Service` - Determine the address based on the service type. + # + # - If `service.type=LoadBalancer` use the external IP or hostname of + # the service. Use the port set by `service.port`. + # + # - If `service.type=NodePort` use the Node IP. The port will be set to + # `service.nodePort` so `service.nodePort` cannot be null. + # + # - If `service.type=ClusterIP` use the `ClusterIP`. The port will be set to + # `service.port`. + # + # - `service.type=ExternalName` is not supported. + # + # - `NodeIP` - The node IP as provided by the Kubernetes downward API. + # + # - `NodeName` - The name of the node as provided by the Kubernetes downward + # API. This is useful if the node names are DNS entries that + # are routable from other datacenters. + # + # - `Static` - Use the address hardcoded in `meshGateway.wanAddress.static`. + source: "Service" + + # Port that gets registered for WAN traffic. + # If source is set to "Service" then this setting will have no effect. + # See the documentation for source as to which port will be used in that + # case. + port: 443 + + # If source is set to "Static" then this value will be used as the WAN + # address of the mesh gateways. This is useful if you've configured a + # DNS entry to point to your mesh gateways. + static: "" + + # The service option configures the Service that fronts the Gateway Deployment. + service: + # Whether to create a Service or not. + enabled: true + + # Type of service, ex. LoadBalancer, ClusterIP. + type: LoadBalancer + + # Port that the service will be exposed on. + # The targetPort will be set to meshGateway.containerPort. + port: 443 + + # Optionally set the nodePort value of the service if using a NodePort service. + # If not set and using a NodePort service, Kubernetes will automatically assign + # a port. + # @type: integer + nodePort: null + + # Annotations to apply to the mesh gateway service. + # + # Example: + # + # ```yaml + # annotations: | + # 'annotation-key': annotation-value + # ``` + # @type: string + annotations: null + + # Optional YAML string that will be appended to the Service spec. + # @type: string + additionalSpec: null + + # If set to true, gateway Pods will run on the host network. + hostNetwork: false + + # dnsPolicy to use. + # @type: string + dnsPolicy: null + + # Consul service name for the mesh gateways. + # Cannot be set to anything other than "mesh-gateway" if + # global.acls.manageSystemACLs is true since the ACL token + # generated is only for the name 'mesh-gateway'. + consulServiceName: "mesh-gateway" + + # Port that the gateway will run on inside the container. + containerPort: 8443 + + # Optional hostPort for the gateway to be exposed on. + # This can be used with wanAddress.port and wanAddress.useNodeIP + # to expose the gateways directly from the node. + # If hostNetwork is true, this must be null or set to the same port as + # containerPort. + # NOTE: Cannot set to 8500 or 8502 because those are reserved for the Consul + # agent. + # @type: integer + hostPort: null + + serviceAccount: + # This value defines additional annotations for the mesh gateways' service account. This should be formatted as a + # multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # The resource settings for mesh gateway pods. + # NOTE: The use of a YAML string is deprecated. Instead, set directly as a + # YAML map. + # @recurse: false + # @type: map + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "100Mi" + cpu: "100m" + + # The resource settings for the `copy-consul-bin` init container. + # @recurse: false + # @type: map + initCopyConsulContainer: + resources: + requests: + memory: "25Mi" + cpu: "50m" + limits: + memory: "150Mi" + cpu: "50m" + + # The resource settings for the `service-init` init container. + # @recurse: false + # @type: map + initServiceInitContainer: + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + + # By default, we set an anti-affinity so that two gateway pods won't be + # on the same node. NOTE: Gateways require that Consul client agents are + # also running on the nodes alongside each gateway pod. + affinity: | + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + component: mesh-gateway + topologyKey: kubernetes.io/hostname + + # Optional YAML string to specify tolerations. + # @type: string + tolerations: null + + # Pod topology spread constraints for mesh gateway pods. + # This should be a multi-line YAML string matching the `topologySpreadConstraints` array + # (https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) in a Pod Spec. + # + # This requires K8S >= 1.18 (beta) or 1.19 (stable). + # + # Example: + # + # ```yaml + # topologySpreadConstraints: | + # - maxSkew: 1 + # topologyKey: topology.kubernetes.io/zone + # whenUnsatisfiable: DoNotSchedule + # labelSelector: + # matchLabels: + # app: {{ template "consul.name" . }} + # release: "{{ .Release.Name }}" + # component: mesh-gateway + # ``` + topologySpreadConstraints: "" + + # Optional YAML string to specify a nodeSelector config. + # @type: string + nodeSelector: null + + # Optional priorityClassName. + priorityClassName: "" + + # Annotations to apply to the mesh gateway deployment. + # + # Example: + # + # ```yaml + # annotations: | + # 'annotation-key': annotation-value + # ``` + # @type: string + annotations: null + +# Configuration options for ingress gateways. Default values for all +# ingress gateways are defined in `ingressGateways.defaults`. Any of +# these values may be overridden in `ingressGateways.gateways` for a +# specific gateway with the exception of annotations. Annotations will +# include both the default annotations and any additional ones defined +# for a specific gateway. +# Requirements: consul >= 1.8.0 +ingressGateways: + # Enable ingress gateway deployment. Requires `connectInject.enabled=true` + # and `client.enabled=true`. + enabled: false + + # Defaults sets default values for all gateway fields. With the exception + # of annotations, defining any of these values in the `gateways` list + # will override the default values provided here. Annotations will + # include both the default annotations and any additional ones defined + # for a specific gateway. + defaults: + # Number of replicas for each ingress gateway defined. + replicas: 2 + + # The service options configure the Service that fronts the gateway Deployment. + service: + # Type of service: LoadBalancer, ClusterIP or NodePort. If using NodePort service + # type, you must set the desired nodePorts in the `ports` setting below. + type: ClusterIP + + # Ports that will be exposed on the service and gateway container. Any + # ports defined as ingress listeners on the gateway's Consul configuration + # entry should be included here. The first port will be used as part of + # the Consul service registration for the gateway and be listed in its + # SRV record. If using a NodePort service type, you must specify the + # desired nodePort for each exposed port. + # @type: array + # @default: [{port: 8080, port: 8443}] + # @recurse: false + ports: + - port: 8080 + nodePort: null + - port: 8443 + nodePort: null + + # Annotations to apply to the ingress gateway service. Annotations defined + # here will be applied to all ingress gateway services in addition to any + # service annotations defined for a specific gateway in `ingressGateways.gateways`. + # + # Example: + # + # ```yaml + # annotations: | + # 'annotation-key': annotation-value + # ``` + # @type: string + annotations: null + + # Optional YAML string that will be appended to the Service spec. + # @type: string + additionalSpec: null + + serviceAccount: + # This value defines additional annotations for the ingress gateways' service account. This should be formatted + # as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # Resource limits for all ingress gateway pods + # @recurse: false + # @type: map + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "100Mi" + cpu: "100m" + + # The resource settings for the `copy-consul-bin` init container. + # @recurse: false + # @type: map + initCopyConsulContainer: + resources: + requests: + memory: "25Mi" + cpu: "50m" + limits: + memory: "150Mi" + cpu: "50m" + + # By default, we set an anti-affinity so that two of the same gateway pods + # won't be on the same node. NOTE: Gateways require that Consul client agents are + # also running on the nodes alongside each gateway pod. + affinity: | + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + component: ingress-gateway + topologyKey: kubernetes.io/hostname + + # Optional YAML string to specify tolerations. + # @type: string + tolerations: null + + # Pod topology spread constraints for ingress gateway pods. + # This should be a multi-line YAML string matching the `topologySpreadConstraints` array + # (https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) in a Pod Spec. + # + # This requires K8S >= 1.18 (beta) or 1.19 (stable). + # + # Example: + # + # ```yaml + # topologySpreadConstraints: | + # - maxSkew: 1 + # topologyKey: topology.kubernetes.io/zone + # whenUnsatisfiable: DoNotSchedule + # labelSelector: + # matchLabels: + # app: {{ template "consul.name" . }} + # release: "{{ .Release.Name }}" + # component: ingress-gateway + # ``` + topologySpreadConstraints: "" + + # Optional YAML string to specify a nodeSelector config. + # @type: string + nodeSelector: null + + # Optional priorityClassName. + priorityClassName: "" + + # Amount of seconds to wait for graceful termination before killing the pod. + terminationGracePeriodSeconds: 10 + + # Annotations to apply to the ingress gateway deployment. Annotations defined + # here will be applied to all ingress gateway deployments in addition to any + # annotations defined for a specific gateway in `ingressGateways.gateways`. + # + # Example: + # + # ```yaml + # annotations: | + # "annotation-key": 'annotation-value' + # ``` + # @type: string + annotations: null + + # [Enterprise Only] `consulNamespace` defines the Consul namespace to register + # the gateway into. Requires `global.enableConsulNamespaces` to be true and + # Consul Enterprise v1.7+ with a valid Consul Enterprise license. + # Note: The Consul namespace MUST exist before the gateway is deployed. + consulNamespace: "default" + + # Gateways is a list of gateway objects. The only required field for + # each is `name`, though they can also contain any of the fields in + # `defaults`. Values defined here override the defaults except in the + # case of annotations where both will be applied. + # @type: array + gateways: + - name: ingress-gateway + +# Configuration options for terminating gateways. Default values for all +# terminating gateways are defined in `terminatingGateways.defaults`. Any of +# these values may be overridden in `terminatingGateways.gateways` for a +# specific gateway with the exception of annotations. Annotations will +# include both the default annotations and any additional ones defined +# for a specific gateway. +# Requirements: consul >= 1.8.0 +terminatingGateways: + # Enable terminating gateway deployment. Requires `connectInject.enabled=true` + # and `client.enabled=true`. + enabled: false + + # Defaults sets default values for all gateway fields. With the exception + # of annotations, defining any of these values in the `gateways` list + # will override the default values provided here. Annotations will + # include both the default annotations and any additional ones defined + # for a specific gateway. + defaults: + # Number of replicas for each terminating gateway defined. + replicas: 2 + + # A list of extra volumes to mount. These will be exposed to Consul in the path `/consul/userconfig//`. + # + # Example: + # + # ```yaml + # extraVolumes: + # - type: secret + # name: my-secret + # items: # optional items array + # - key: key + # path: path # secret will now mount to /consul/userconfig/my-secret/path + # ``` + # @type: array + extraVolumes: [] + + # Resource limits for all terminating gateway pods + # @recurse: false + # @type: map + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "100Mi" + cpu: "100m" + + # The resource settings for the `copy-consul-bin` init container. + # @recurse: false + # @type: map + initCopyConsulContainer: + resources: + requests: + memory: "25Mi" + cpu: "50m" + limits: + memory: "150Mi" + cpu: "50m" + + # By default, we set an anti-affinity so that two of the same gateway pods + # won't be on the same node. NOTE: Gateways require that Consul client agents are + # also running on the nodes alongside each gateway pod. + affinity: | + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app: {{ template "consul.name" . }} + release: "{{ .Release.Name }}" + component: terminating-gateway + topologyKey: kubernetes.io/hostname + + # Optional YAML string to specify tolerations. + # @type: string + tolerations: null + + # Pod topology spread constraints for terminating gateway pods. + # This should be a multi-line YAML string matching the `topologySpreadConstraints` array + # (https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) in a Pod Spec. + # + # This requires K8S >= 1.18 (beta) or 1.19 (stable). + # + # Example: + # + # ```yaml + # topologySpreadConstraints: | + # - maxSkew: 1 + # topologyKey: topology.kubernetes.io/zone + # whenUnsatisfiable: DoNotSchedule + # labelSelector: + # matchLabels: + # app: {{ template "consul.name" . }} + # release: "{{ .Release.Name }}" + # component: terminating-gateway + # ``` + topologySpreadConstraints: "" + + # Optional YAML string to specify a nodeSelector config. + # @type: string + nodeSelector: null + + # Optional priorityClassName. + # @type: string + priorityClassName: "" + + # Annotations to apply to the terminating gateway deployment. Annotations defined + # here will be applied to all terminating gateway deployments in addition to any + # annotations defined for a specific gateway in `terminatingGateways.gateways`. + # + # Example: + # + # ```yaml + # annotations: | + # 'annotation-key': annotation-value + # ``` + # @type: string + annotations: null + + serviceAccount: + # This value defines additional annotations for the terminating gateways' service account. This should be + # formatted as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # [Enterprise Only] `consulNamespace` defines the Consul namespace to register + # the gateway into. Requires `global.enableConsulNamespaces` to be true and + # Consul Enterprise v1.7+ with a valid Consul Enterprise license. + # Note: The Consul namespace MUST exist before the gateway is deployed. + consulNamespace: "default" + + # Gateways is a list of gateway objects. The only required field for + # each is `name`, though they can also contain any of the fields in + # `defaults`. Values defined here override the defaults except in the + # case of annotations where both will be applied. + # @type: array + gateways: + - name: terminating-gateway + +# Configuration settings for the Consul API Gateway integration +apiGateway: + # When true the helm chart will install the Consul API Gateway controller + enabled: false + + # Image to use for the api-gateway-controller pods and gateway instances + # @type: string + image: null + + # Override global log verbosity level for api-gateway-controller pods. One of "debug", "info", "warn", or "error". + # @type: string + logLevel: info + + # Configuration settings for the optional GatewayClass installed by consul-k8s (enabled by default) + managedGatewayClass: + # When true a GatewayClass is configured to automatically work with Consul as installed by helm. + enabled: true + + # This value defines `nodeSelector` (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # labels for gateway pod assignment, formatted as a multi-line string. + # + # Example: + # + # ```yaml + # nodeSelector: | + # beta.kubernetes.io/arch: amd64 + # ``` + # + # @type: string + nodeSelector: null + + # This value defines the type of service created for gateways (e.g. LoadBalancer, ClusterIP) + serviceType: LoadBalancer + + # This value toggles if the gateway ports should be mapped to host ports + useHostPorts: false + + # Configuration settings for annotations to be copied from the Gateway to other child resources. + copyAnnotations: + # This value defines a list of annotations to be copied from the Gateway to the Service created, formatted as a multi-line string. + # + # Example: + # + # ```yaml + # service: | + # - external-dns.alpha.kubernetes.io/hostname + # ``` + # + # @type: string + service: null + + # This value defines the number of pods to deploy for each Gateway as well as a min and max number of pods for all Gateways + # + # Example: + # + # ```yaml + # deployment: + # defaultInstances: 3 + # maxInstances: 8 + # minInstances: 1 + # ``` + # + # @type: map + deployment: null + + # Configuration for the ServiceAccount created for the api-gateway component + serviceAccount: + # This value defines additional annotations for the client service account. This should be formatted as a multi-line + # string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + # Configuration for the api-gateway controller component + controller: + # This value sets the number of controller replicas to deploy. + replicas: 1 + + # Annotations to apply to the api-gateway-controller pods. + # + # ```yaml + # annotations: | + # "annotation-key": "annotation-value" + # ``` + # + # @type: string + annotations: null + + # This value references an existing + # Kubernetes `priorityClassName` (https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#pod-priority) + # that can be assigned to api-gateway-controller pods. + priorityClassName: "" + + # This value defines `nodeSelector` (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # labels for api-gateway-controller pod assignment, formatted as a multi-line string. + # + # Example: + # + # ```yaml + # nodeSelector: | + # beta.kubernetes.io/arch: amd64 + # ``` + # + # @type: string + nodeSelector: null + + # Configuration for the Service created for the api-gateway-controller + service: + # Annotations to apply to the api-gateway-controller service. + # + # ```yaml + # annotations: | + # "annotation-key": "annotation-value" + # ``` + # + # @type: string + annotations: null + + # The resource settings for api gateway pods. + # @recurse: false + # @type: map + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "100Mi" + cpu: "100m" + + # The resource settings for the `copy-consul-bin` init container. + # @recurse: false + # @type: map + initCopyConsulContainer: + resources: + requests: + memory: "25Mi" + cpu: "50m" + limits: + memory: "150Mi" + cpu: "50m" + +# Configuration settings for the webhook-cert-manager +# `webhook-cert-manager` ensures that cert bundles are up to date for the mutating webhook. +webhookCertManager: + + # Toleration Settings + # This should be a multi-line string matching the Toleration array + # in a PodSpec. + # @type: string + tolerations: null + +# Configures a demo Prometheus installation. +prometheus: + # When true, the Helm chart will install a demo Prometheus server instance + # alongside Consul. + enabled: false + +# Control whether a test Pod manifest is generated when running helm template. +# When using helm install, the test Pod is not submitted to the cluster so this +# is only useful when running helm template. +tests: + enabled: true diff --git a/charts/hashicorp/vault/0.22.0/.helmignore b/charts/hashicorp/vault/0.22.0/.helmignore new file mode 100644 index 000000000..4007e2435 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/.helmignore @@ -0,0 +1,28 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.terraform/ +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj + +# CI and test +.circleci/ +.github/ +.gitlab-ci.yml +test/ diff --git a/charts/hashicorp/vault/0.22.0/CHANGELOG.md b/charts/hashicorp/vault/0.22.0/CHANGELOG.md new file mode 100644 index 000000000..bdc728be4 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/CHANGELOG.md @@ -0,0 +1,427 @@ +## Unreleased + +## 0.22.0 (September 8th, 2022) + +Features: +* Add PrometheusOperator support for collecting Vault server metrics. [GH-772](https://github.com/hashicorp/vault-helm/pull/772) + +Changes: +* `vault-k8s` to 1.0.0 [GH-784](https://github.com/hashicorp/vault-helm/pull/784) +* Test against Kubernetes 1.25 [GH-784](https://github.com/hashicorp/vault-helm/pull/784) +* `vault` updated to 1.11.3 [GH-785](https://github.com/hashicorp/vault-helm/pull/785) + +## 0.21.0 (August 10th, 2022) + +CHANGES: +* `vault-k8s` updated to 0.17.0. [GH-771](https://github.com/hashicorp/vault-helm/pull/771) +* `vault-csi-provider` updated to 1.2.0 [GH-771](https://github.com/hashicorp/vault-helm/pull/771) +* `vault` updated to 1.11.2 [GH-771](https://github.com/hashicorp/vault-helm/pull/771) +* Start testing against Kubernetes 1.24. [GH-744](https://github.com/hashicorp/vault-helm/pull/744) +* Deprecated `injector.externalVaultAddr`. Added `global.externalVaultAddr`, which applies to both the Injector and the CSI Provider. [GH-745](https://github.com/hashicorp/vault-helm/pull/745) +* CSI Provider pods now set the `VAULT_ADDR` environment variable to either the internal Vault service or the configured external address. [GH-745](https://github.com/hashicorp/vault-helm/pull/745) + +Features: +* server: Add `server.statefulSet.securityContext` to override pod and container `securityContext`. [GH-767](https://github.com/hashicorp/vault-helm/pull/767) +* csi: Add `csi.daemonSet.securityContext` to override pod and container `securityContext`. [GH-767](https://github.com/hashicorp/vault-helm/pull/767) +* injector: Add `injector.securityContext` to override pod and container `securityContext`. [GH-750](https://github.com/hashicorp/vault-helm/pull/750) and [GH-767](https://github.com/hashicorp/vault-helm/pull/767) +* Add `server.service.activeNodePort` and `server.service.standbyNodePort` to specify the `nodePort` for active and standby services. [GH-610](https://github.com/hashicorp/vault-helm/pull/610) +* Support for setting annotations on the injector's serviceAccount [GH-753](https://github.com/hashicorp/vault-helm/pull/753) + +## 0.20.1 (May 25th, 2022) +CHANGES: +* `vault-k8s` updated to 0.16.1 [GH-739](https://github.com/hashicorp/vault-helm/pull/739) + +Improvements: +* Mutating webhook will no longer target the agent injector pod [GH-736](https://github.com/hashicorp/vault-helm/pull/736) + +Bugs: +* `vault` service account is now created even if the server is set to disabled, as per before 0.20.0 [GH-737](https://github.com/hashicorp/vault-helm/pull/737) + +## 0.20.0 (May 16th, 2022) + +CHANGES: +* `global.enabled` now works as documented, that is, setting `global.enabled` to false will disable everything, with individual components able to be turned on individually [GH-703](https://github.com/hashicorp/vault-helm/pull/703) +* Default value of `-` used for injector and server to indicate that they follow `global.enabled`. [GH-703](https://github.com/hashicorp/vault-helm/pull/703) +* Vault default image to 1.10.3 +* CSI provider default image to 1.1.0 +* Vault K8s default image to 0.16.0 +* Earliest Kubernetes version tested is now 1.16 +* Helm 3.6+ now required + +Features: +* Support topologySpreadConstraints in server and injector. [GH-652](https://github.com/hashicorp/vault-helm/pull/652) + +Improvements: +* CSI: Set `extraLabels` for daemonset, pods, and service account [GH-690](https://github.com/hashicorp/vault-helm/pull/690) +* Add namespace to injector-leader-elector role, rolebinding and secret [GH-683](https://github.com/hashicorp/vault-helm/pull/683) +* Support policy/v1 PodDisruptionBudget in Kubernetes 1.21+ for server and injector [GH-710](https://github.com/hashicorp/vault-helm/pull/710) +* Make the Cluster Address (CLUSTER_ADDR) configurable [GH-629](https://github.com/hashicorp/vault-helm/pull/709) +* server: Make `publishNotReadyAddresses` configurable for services [GH-694](https://github.com/hashicorp/vault-helm/pull/694) +* server: Allow config to be defined as a YAML object in the values file [GH-684](https://github.com/hashicorp/vault-helm/pull/684) +* Maintain default MutatingWebhookConfiguration values from `v1beta1` [GH-692](https://github.com/hashicorp/vault-helm/pull/692) + +## 0.19.0 (January 20th, 2022) + +CHANGES: +* Vault image default 1.9.2 +* Vault K8s image default 0.14.2 + +Features: +* Added configurable podDisruptionBudget for injector [GH-653](https://github.com/hashicorp/vault-helm/pull/653) +* Make terminationGracePeriodSeconds configurable for server [GH-659](https://github.com/hashicorp/vault-helm/pull/659) +* Added configurable update strategy for injector [GH-661](https://github.com/hashicorp/vault-helm/pull/661) +* csi: ability to set priorityClassName for CSI daemonset pods [GH-670](https://github.com/hashicorp/vault-helm/pull/670) + +Improvements: +* Set the namespace on the OpenShift Route [GH-679](https://github.com/hashicorp/vault-helm/pull/679) +* Add volumes and env vars to helm hook test pod [GH-673](https://github.com/hashicorp/vault-helm/pull/673) +* Make TLS configurable for OpenShift routes [GH-686](https://github.com/hashicorp/vault-helm/pull/686) + +## 0.18.0 (November 17th, 2021) + +CHANGES: +* Removed support for deploying a leader-elector container with the [vault-k8s injector](https://github.com/hashicorp/vault-k8s) injector since vault-k8s now uses an internal mechanism to determine leadership [GH-649](https://github.com/hashicorp/vault-helm/pull/649) +* Vault image default 1.9.0 +* Vault K8s image default 0.14.1 + +Improvements: +* Added templateConfig.staticSecretRenderInterval chart option for the injector [GH-621](https://github.com/hashicorp/vault-helm/pull/621) + +## 0.17.1 (October 25th, 2021) + +Improvements: + * Add option for Ingress PathType [GH-634](https://github.com/hashicorp/vault-helm/pull/634) + +## 0.17.0 (October 21st, 2021) + +KNOWN ISSUES: +* The chart will fail to deploy on Kubernetes 1.19+ with `server.ingress.enabled=true` because no `pathType` is set + +CHANGES: +* Vault image default 1.8.4 +* Vault K8s image default 0.14.0 + +Improvements: +* Support Ingress stable networking API [GH-590](https://github.com/hashicorp/vault-helm/pull/590) +* Support setting the `externalTrafficPolicy` for `LoadBalancer` and `NodePort` service types [GH-626](https://github.com/hashicorp/vault-helm/pull/626) +* Support setting ingressClassName on server Ingress [GH-630](https://github.com/hashicorp/vault-helm/pull/630) + +Bugs: +* Ensure `kubeletRootDir` volume path and mounts are the same when `csi.daemonSet.kubeletRootDir` is overridden [GH-628](https://github.com/hashicorp/vault-helm/pull/628) + +## 0.16.1 (September 29th, 2021) + +CHANGES: +* Vault image default 1.8.3 +* Vault K8s image default 0.13.1 + +## 0.16.0 (September 16th, 2021) + +CHANGES: +* Support for deploying a leader-elector container with the [vault-k8s injector](https://github.com/hashicorp/vault-k8s) injector will be removed in version 0.18.0 of this chart since vault-k8s now uses an internal mechanism to determine leadership. To enable the deployment of the leader-elector container for use with vault-k8s 0.12.0 and earlier, set `useContainer=true`. + +Improvements: + * Make CSI provider `hostPaths` configurable via `csi.daemonSet.providersDir` and `csi.daemonSet.kubeletRootDir` [GH-603](https://github.com/hashicorp/vault-helm/pull/603) + * Support vault-k8s internal leader election [GH-568](https://github.com/hashicorp/vault-helm/pull/568) [GH-607](https://github.com/hashicorp/vault-helm/pull/607) + +## 0.15.0 (August 23rd, 2021) + +Improvements: +* Add imagePullSecrets on server test [GH-572](https://github.com/hashicorp/vault-helm/pull/572) +* Add injector.webhookAnnotations chart option [GH-584](https://github.com/hashicorp/vault-helm/pull/584) + +## 0.14.0 (July 28th, 2021) + +Features: +* Added templateConfig.exitOnRetryFailure chart option for the injector [GH-560](https://github.com/hashicorp/vault-helm/pull/560) + +Improvements: +* Support configuring pod tolerations, pod affinity, and node selectors as YAML [GH-565](https://github.com/hashicorp/vault-helm/pull/565) +* Set the default vault image to come from the hashicorp organization [GH-567](https://github.com/hashicorp/vault-helm/pull/567) +* Add support for running the acceptance tests against a local `kind` cluster [GH-567](https://github.com/hashicorp/vault-helm/pull/567) +* Add `server.ingress.activeService` to configure if the ingress should use the active service [GH-570](https://github.com/hashicorp/vault-helm/pull/570) +* Add `server.route.activeService` to configure if the route should use the active service [GH-570](https://github.com/hashicorp/vault-helm/pull/570) +* Support configuring `global.imagePullSecrets` from a string array [GH-576](https://github.com/hashicorp/vault-helm/pull/576) + + +## 0.13.0 (June 17th, 2021) + +Improvements: +* Added a helm test for vault server [GH-531](https://github.com/hashicorp/vault-helm/pull/531) +* Added server.enterpriseLicense option [GH-547](https://github.com/hashicorp/vault-helm/pull/547) +* Added OpenShift overrides [GH-549](https://github.com/hashicorp/vault-helm/pull/549) + +Bugs: +* Fix ui.serviceNodePort schema [GH-537](https://github.com/hashicorp/vault-helm/pull/537) +* Fix server.ha.disruptionBudget.maxUnavailable schema [GH-535](https://github.com/hashicorp/vault-helm/pull/535) +* Added webhook-certs volume mount to sidecar injector [GH-545](https://github.com/hashicorp/vault-helm/pull/545) + +## 0.12.0 (May 25th, 2021) + +Features: +* Pass additional arguments to `vault-csi-provider` using `csi.extraArgs` [GH-526](https://github.com/hashicorp/vault-helm/pull/526) + +Improvements: +* Set chart kubeVersion and added chart-verifier tests [GH-510](https://github.com/hashicorp/vault-helm/pull/510) +* Added values json schema [GH-513](https://github.com/hashicorp/vault-helm/pull/513) +* Ability to set tolerations for CSI daemonset pods [GH-521](https://github.com/hashicorp/vault-helm/pull/521) +* UI target port is now configurable [GH-437](https://github.com/hashicorp/vault-helm/pull/437) + +Bugs: +* CSI: `global.imagePullSecrets` are now also used for CSI daemonset [GH-519](https://github.com/hashicorp/vault-helm/pull/519) + +## 0.11.0 (April 14th, 2021) + +Features: +* Added `server.enabled` to explicitly skip installing a Vault server [GH-486](https://github.com/hashicorp/vault-helm/pull/486) +* Injector now supports enabling host network [GH-471](https://github.com/hashicorp/vault-helm/pull/471) +* Injector port is now configurable [GH-489](https://github.com/hashicorp/vault-helm/pull/489) +* Injector Vault Agent resource defaults are now configurable [GH-493](https://github.com/hashicorp/vault-helm/pull/493) +* Extra paths can now be added to the Vault ingress service [GH-460](https://github.com/hashicorp/vault-helm/pull/460) +* Log level and format can now be set directly using `server.logFormat` and `server.logLevel` [GH-488](https://github.com/hashicorp/vault-helm/pull/488) + +Improvements: +* Added `https` name to injector service port [GH-495](https://github.com/hashicorp/vault-helm/pull/495) + +Bugs: +* CSI: Fix ClusterRole name and DaemonSet's service account to properly match deployment name [GH-486](https://github.com/hashicorp/vault-helm/pull/486) + +## 0.10.0 (March 25th, 2021) + +Features: +* Add support for [Vault CSI provider](https://github.com/hashicorp/vault-csi-provider) [GH-461](https://github.com/hashicorp/vault-helm/pull/461) + +Improvements: +* `objectSelector` can now be set on the mutating admission webhook [GH-456](https://github.com/hashicorp/vault-helm/pull/456) + +## 0.9.1 (February 2nd, 2021) + +Bugs: +* Injector: fix labels for default anti-affinity rule [GH-441](https://github.com/hashicorp/vault-helm/pull/441), [GH-442](https://github.com/hashicorp/vault-helm/pull/442) +* Set VAULT_DEV_LISTEN_ADDRESS in dev mode [GH-446](https://github.com/hashicorp/vault-helm/pull/446) + +## 0.9.0 (January 5th, 2021) + +Features: +* Injector now supports configurable number of replicas [GH-436](https://github.com/hashicorp/vault-helm/pull/436) +* Injector now supports auto TLS for multiple replicas using leader elections [GH-436](https://github.com/hashicorp/vault-helm/pull/436) + +Improvements: +* Dev mode now supports `server.extraArgs` [GH-421](https://github.com/hashicorp/vault-helm/pull/421) +* Dev mode root token is now configurable with `server.dev.devRootToken` [GH-415](https://github.com/hashicorp/vault-helm/pull/415) +* ClusterRoleBinding updated to `v1` [GH-395](https://github.com/hashicorp/vault-helm/pull/395) +* MutatingWebhook updated to `v1` [GH-408](https://github.com/hashicorp/vault-helm/pull/408) +* Injector service now supports `injector.service.annotations` [425](https://github.com/hashicorp/vault-helm/pull/425) +* Injector now supports `injector.extraLabels` [428](https://github.com/hashicorp/vault-helm/pull/428) +* Added `allowPrivilegeEscalation: false` to Vault and Injector containers [429](https://github.com/hashicorp/vault-helm/pull/429) +* Network Policy now supports `server.networkPolicy.egress` [389](https://github.com/hashicorp/vault-helm/pull/389) + +## 0.8.0 (October 20th, 2020) + +Improvements: +* Make server NetworkPolicy independent of OpenShift [GH-381](https://github.com/hashicorp/vault-helm/pull/381) +* Added configurables for all probe values [GH-387](https://github.com/hashicorp/vault-helm/pull/387) +* MountPath for audit and data storage is now configurable [GH-393](https://github.com/hashicorp/vault-helm/pull/393) +* Annotations can now be added to the Injector pods [GH-394](https://github.com/hashicorp/vault-helm/pull/394) +* The injector can now be configured with a failurePolicy [GH-400](https://github.com/hashicorp/vault-helm/pull/400) +* Added additional environment variables for rendering within Vault config [GH-398](https://github.com/hashicorp/vault-helm/pull/398) +* Service account for Vault K8s auth is automatically created when `injector.externalVaultAddr` is set [GH-392](https://github.com/hashicorp/vault-helm/pull/392) + +Bugs: +* Fixed install output using Helm V2 command [GH-378](https://github.com/hashicorp/vault-helm/pull/378) + +## 0.7.0 (August 24th, 2020) + +Features: +* Added `volumes` and `volumeMounts` for mounting _any_ type of volume [GH-314](https://github.com/hashicorp/vault-helm/pull/314). +* Added configurable to enable prometheus telemetery exporter for Vault Agent Injector [GH-372](https://github.com/hashicorp/vault-helm/pull/372) + +Improvements: +* Added `defaultMode` configurable to `extraVolumes`[GH-321](https://github.com/hashicorp/vault-helm/pull/321) +* Option to install and use PodSecurityPolicy's for vault server and injector [GH-177](https://github.com/hashicorp/vault-helm/pull/177) +* `VAULT_API_ADDR` is now configurable [GH-290](https://github.com/hashicorp/vault-helm/pull/290) +* Removed deprecated tolerate unready endpoint annotations [GH-363](https://github.com/hashicorp/vault-helm/pull/363) +* Add an option to set annotations on the StatefulSet [GH-199](https://github.com/hashicorp/vault-helm/pull/199) +* Make the vault server serviceAccount name a configuration option [GH-367](https://github.com/hashicorp/vault-helm/pull/367) +* Removed annotation striction from `dev` mode [GH-371](https://github.com/hashicorp/vault-helm/pull/371) +* Add an option to set annotations on PVCs [GH-364](https://github.com/hashicorp/vault-helm/pull/364) +* Added service configurables for UI [GH-285](https://github.com/hashicorp/vault-helm/pull/285) + +Bugs: +* Fix python dependency in test image [GH-337](https://github.com/hashicorp/vault-helm/pull/337) +* Fix caBundle not being quoted causing validation issues with Helm 3 [GH-352](https://github.com/hashicorp/vault-helm/pull/352) +* Fix injector network policy being rendered when injector is not enabled [GH-358](https://github.com/hashicorp/vault-helm/pull/358) + +## 0.6.0 (June 3rd, 2020) + +Features: +* Added `extraInitContainers` to define init containers for the Vault cluster [GH-258](https://github.com/hashicorp/vault-helm/pull/258) +* Added `postStart` lifecycle hook allowing users to configure commands to run on the Vault pods after they're ready [GH-315](https://github.com/hashicorp/vault-helm/pull/315) +* Beta: Added OpenShift support [GH-319](https://github.com/hashicorp/vault-helm/pull/319) + +Improvements: +* Server configs can now be defined in YAML. Multi-line string configs are still compatible [GH-213](https://github.com/hashicorp/vault-helm/pull/213) +* Removed IPC_LOCK privileges since swap is disabled on containers [[GH-198](https://github.com/hashicorp/vault-helm/pull/198)] +* Use port names that map to vault.scheme [[GH-223](https://github.com/hashicorp/vault-helm/pull/223)] +* Allow both yaml and multi-line string annotations [[GH-272](https://github.com/hashicorp/vault-helm/pull/272)] +* Added configurable to set the Raft node name to hostname [[GH-269](https://github.com/hashicorp/vault-helm/pull/269)] +* Support setting priorityClassName on pods [[GH-282](https://github.com/hashicorp/vault-helm/pull/282)] +* Added support for ingress apiVersion `networking.k8s.io/v1beta1` [[GH-310](https://github.com/hashicorp/vault-helm/pull/310)] +* Added configurable to change service type for the HA active service [GH-317](https://github.com/hashicorp/vault-helm/pull/317) + +Bugs: +* Fixed default ingress path [[GH-224](https://github.com/hashicorp/vault-helm/pull/224)] +* Fixed annotations for HA standby/active services [[GH-268](https://github.com/hashicorp/vault-helm/pull/268)] +* Updated some value defaults to match their use in templates [[GH-309](https://github.com/hashicorp/vault-helm/pull/309)] +* Use active service on ingress when ha [[GH-270](https://github.com/hashicorp/vault-helm/pull/270)] +* Fixed bug where pull secrets weren't being used for injector image [GH-298](https://github.com/hashicorp/vault-helm/pull/298) + +## 0.5.0 (April 9th, 2020) + +Features: + +* Added Raft support for HA mode [[GH-228](https://github.com/hashicorp/vault-helm/pull/229)] +* Now supports Vault Enterprise [[GH-250](https://github.com/hashicorp/vault-helm/pull/250)] +* Added K8s Service Registration for HA modes [[GH-250](https://github.com/hashicorp/vault-helm/pull/250)] + +* Option to set `AGENT_INJECT_VAULT_AUTH_PATH` for the injector [[GH-185](https://github.com/hashicorp/vault-helm/pull/185)] +* Added environment variables for logging and revocation on Vault Agent Injector [[GH-219](https://github.com/hashicorp/vault-helm/pull/219)] +* Option to set environment variables for the injector deployment [[GH-232](https://github.com/hashicorp/vault-helm/pull/232)] +* Added affinity, tolerations, and nodeSelector options for the injector deployment [[GH-234](https://github.com/hashicorp/vault-helm/pull/234)] +* Made all annotations multi-line strings [[GH-227](https://github.com/hashicorp/vault-helm/pull/227)] + +## 0.4.0 (February 21st, 2020) + +Improvements: + +* Allow process namespace sharing between Vault and sidecar containers [[GH-174](https://github.com/hashicorp/vault-helm/pull/174)] +* Added configurable to change updateStrategy [[GH-172](https://github.com/hashicorp/vault-helm/pull/172)] +* Added sleep in the preStop lifecycle step [[GH-188](https://github.com/hashicorp/vault-helm/pull/188)] +* Updated chart and tests to Helm 3 [[GH-195](https://github.com/hashicorp/vault-helm/pull/195)] +* Adds Values.injector.externalVaultAddr to use the injector with an external vault [[GH-207](https://github.com/hashicorp/vault-helm/pull/207)] + +Bugs: + +* Fix bug where Vault lifecycle was appended after extra containers. [[GH-179](https://github.com/hashicorp/vault-helm/pull/179)] + +## 0.3.3 (January 14th, 2020) + +Security: + +* Added `server.extraArgs` to allow loading of additional Vault configurations containing sensitive settings [GH-175](https://github.com/hashicorp/vault-helm/issues/175) + +Bugs: + +* Fixed injection bug where wrong environment variables were being used for manually mounted TLS files + +## 0.3.2 (January 8th, 2020) + +Bugs: + +* Fixed injection bug where TLS Skip Verify was true by default [VK8S-35] + +## 0.3.1 (January 2nd, 2020) + +Bugs: + +* Fixed injection bug causing kube-system pods to be rejected [VK8S-14] + +## 0.3.0 (December 19th, 2019) + +Features: + +* Extra containers can now be added to the Vault pods +* Added configurability of pod probes +* Added Vault Agent Injector + +Improvements: + +* Moved `global.image` to `server.image` +* Changed UI service template to route pods that aren't ready via `publishNotReadyAddresses: true` +* Added better HTTP/HTTPS scheme support to http probes +* Added configurable node port for Vault service +* `server.authDelegator` is now enabled by default + +Bugs: + +* Fixed upgrade bug by removing chart label which contained the version +* Fixed typo on `serviceAccount` (was `serviceaccount`) +* Fixed readiness/liveliness HTTP probe default to accept standbys + +## 0.2.1 (November 12th, 2019) + +Bugs: + +* Removed `readOnlyRootFilesystem` causing issues when validating deployments + +## 0.2.0 (October 29th, 2019) + +Features: + +* Added load balancer support +* Added ingress support +* Added configurable for service types (ClusterIP, NodePort, LoadBalancer, etc) +* Removed root requirements, now runs as Vault user + +Improvements: + +* Added namespace value to all rendered objects +* Made ports configurable in services +* Added the ability to add custom annotations to services +* Added docker image for running bats test in CircleCI +* Removed restrictions around `dev` mode such as annotations +* `readOnlyRootFilesystem` is now configurable +* Image Pull Policy is now configurable + +Bugs: + +* Fixed selector bugs related to Helm label updates (services, affinities, and pod disruption) +* Fixed bug where audit storage was not being mounted in HA mode +* Fixed bug where Vault pod wasn't receiving SIGTERM signals + + +## 0.1.2 (August 22nd, 2019) + +Features: + +* Added `extraSecretEnvironmentVars` to allow users to mount secrets as + environment variables +* Added `tlsDisable` configurable to change HTTP protocols from HTTP/HTTPS + depending on the value +* Added `serviceNodePort` to configure a NodePort value when setting `serviceType` + to "NodePort" + +Improvements: + +* Changed UI port to 8200 for better HTTP protocol support +* Added `path` to `extraVolumes` to define where the volume should be + mounted. Defaults to `/vault/userconfig` +* Upgraded Vault to 1.2.2 + +Bugs: + +* Fixed bug where upgrade would fail because immutable labels were being + changed (Helm Version label) +* Fixed bug where UI service used wrong selector after updating helm labels +* Added `VAULT_API_ADDR` env to Vault pod to fixed bug where Vault thinks + Consul is the active node +* Removed `step-down` preStop since it requires authentication. Shutdown signal + sent by Kube acts similar to `step-down` + + +## 0.1.1 (August 7th, 2019) + +Features: + +* Added `authDelegator` Cluster Role Binding to Vault service account for + bootstrapping Kube auth method + +Improvements: + +* Added `server.service.clusterIP` to `values.yml` so users can toggle + the Vault service to headless by using the value `None`. +* Upgraded Vault to 1.2.1 + +## 0.1.0 (August 6th, 2019) + +Initial release diff --git a/charts/hashicorp/vault/0.22.0/CONTRIBUTING.md b/charts/hashicorp/vault/0.22.0/CONTRIBUTING.md new file mode 100644 index 000000000..ad31ac92d --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/CONTRIBUTING.md @@ -0,0 +1,247 @@ +# Contributing to Vault Helm + +**Please note:** We take Vault's security and our users' trust very seriously. +If you believe you have found a security issue in Vault, please responsibly +disclose by contacting us at security@hashicorp.com. + +**First:** if you're unsure or afraid of _anything_, just ask or submit the +issue or pull request anyways. You won't be yelled at for giving it your best +effort. The worst that can happen is that you'll be politely asked to change +something. We appreciate any sort of contributions, and don't want a wall of +rules to get in the way of that. + +That said, if you want to ensure that a pull request is likely to be merged, +talk to us! You can find out our thoughts and ensure that your contribution +won't clash or be obviated by Vault's normal direction. A great way to do this +is via the [Vault Discussion Forum][1]. + +This document will cover what we're looking for in terms of reporting issues. +By addressing all the points we're looking for, it raises the chances we can +quickly merge or address your contributions. + +[1]: https://discuss.hashicorp.com/c/vault + +## Issues + +### Reporting an Issue + +* Make sure you test against the latest released version. It is possible + we already fixed the bug you're experiencing. Even better is if you can test + against `main`, as bugs are fixed regularly but new versions are only + released every few months. + +* Provide steps to reproduce the issue, and if possible include the expected + results as well as the actual results. Please provide text, not screen shots! + +* Respond as promptly as possible to any questions made by the Vault + team to your issue. Stale issues will be closed periodically. + +### Issue Lifecycle + +1. The issue is reported. + +2. The issue is verified and categorized by a Vault Helm collaborator. + Categorization is done via tags. For example, bugs are marked as "bugs". + +3. Unless it is critical, the issue may be left for a period of time (sometimes + many weeks), giving outside contributors -- maybe you!? -- a chance to + address the issue. + +4. The issue is addressed in a pull request or commit. The issue will be + referenced in the commit message so that the code that fixes it is clearly + linked. + +5. The issue is closed. Sometimes, valid issues will be closed to keep + the issue tracker clean. The issue is still indexed and available for + future viewers, or can be re-opened if necessary. + +## Testing + +The Helm chart ships with both unit and acceptance tests. + +The unit tests don't require any active Kubernetes cluster and complete +very quickly. These should be used for fast feedback during development. +The acceptance tests require a Kubernetes cluster with a configured `kubectl`. + +### Test Using Docker Container + +The following are the instructions for running bats tests using a Docker container. + +#### Prerequisites + +* Docker installed +* `vault-helm` checked out locally + +#### Test + +**Note:** the following commands should be run from the `vault-helm` directory. + +First, build the Docker image for running the tests: + +```shell +docker build -f ${PWD}/test/docker/Test.dockerfile ${PWD}/test/docker/ -t vault-helm-test +``` +Next, execute the tests with the following commands: +```shell +docker run -it --rm -v "${PWD}:/test" vault-helm-test bats /test/test/unit +``` +It's possible to only run specific bats tests using regular expressions. +For example, the following will run only tests with "injector" in the name: +```shell +docker run -it --rm -v "${PWD}:/test" vault-helm-test bats /test/test/unit -f "injector" +``` + +### Test Manually +The following are the instructions for running bats tests on your workstation. +#### Prerequisites +* [Bats](https://github.com/bats-core/bats-core) + ```bash + brew install bats-core + ``` +* [yq](https://pypi.org/project/yq/) + ```bash + brew install python-yq + ``` +* [helm](https://helm.sh) + ```bash + brew install kubernetes-helm + ``` + +#### Test + +To run the unit tests: + + bats ./test/unit + +To run the acceptance tests: + + bats ./test/acceptance + +If the acceptance tests fail, deployed resources in the Kubernetes cluster +may not be properly cleaned up. We recommend recycling the Kubernetes cluster to +start from a clean slate. + +**Note:** There is a Terraform configuration in the +[`test/terraform/`](https://github.com/hashicorp/vault-helm/tree/main/test/terraform) directory +that can be used to quickly bring up a GKE cluster and configure +`kubectl` and `helm` locally. This can be used to quickly spin up a test +cluster for acceptance tests. Unit tests _do not_ require a running Kubernetes +cluster. + +### Writing Unit Tests + +Changes to the Helm chart should be accompanied by appropriate unit tests. + +#### Formatting + +- Put tests in the test file in the same order as the variables appear in the `values.yaml`. +- Start tests for a chart value with a header that says what is being tested, like this: + ``` + #-------------------------------------------------------------------- + # annotations + ``` + +- Name the test based on what it's testing in the following format (this will be its first line): + ``` + @test "
: " { + ``` + + When adding tests to an existing file, the first section will be the same as the other tests in the file. + +#### Test Details + +[Bats](https://github.com/bats-core/bats-core) provides a way to run commands in a shell and inspect the output in an automated way. +In all of the tests in this repo, the base command being run is [helm template](https://docs.helm.sh/helm/#helm-template) which turns the templated files into straight yaml output. +In this way, we're able to test that the various conditionals in the templates render as we would expect. + +Each test defines the files that should be rendered using the `--show-only` flag, then it might adjust chart values by adding `--set` flags as well. +The output from this `helm template` command is then piped to [yq](https://pypi.org/project/yq/). +`yq` allows us to pull out just the information we're interested in, either by referencing its position in the yaml file directly or giving information about it (like its length). +The `-r` flag can be used with `yq` to return a raw string instead of a quoted one which is especially useful when looking for an exact match. + +The test passes or fails based on the conditional at the end that is in square brackets, which is a comparison of our expected value and the output of `helm template` piped to `yq`. + +The `| tee /dev/stderr ` pieces direct any terminal output of the `helm template` and `yq` commands to stderr so that it doesn't interfere with `bats`. + +#### Test Examples + +Here are some examples of common test patterns: + +- Check that a value is disabled by default + + ``` + @test "ui/Service: no type by default" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "null" ] + } + ``` + + In this example, nothing is changed from the default templates (no `--set` flags), then we use `yq` to retrieve the value we're checking, `.spec.type`. + This output is then compared against our expected value (`null` in this case) in the assertion `[ "${actual}" = "null" ]`. + + +- Check that a template value is rendered to a specific value + ``` + @test "ui/Service: specified type" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/ui-service.yaml \ + --set 'ui.serviceType=LoadBalancer' \ + . | tee /dev/stderr | + yq -r '.spec.type' | tee /dev/stderr) + [ "${actual}" = "LoadBalancer" ] + } + ``` + + This is very similar to the last example, except we've changed a default value with the `--set` flag and correspondingly changed the expected value. + +- Check that a template value contains several values + ``` + @test "server/standalone-StatefulSet: custom resources" { + cd `chart_dir` + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.resources.requests.memory=256Mi' \ + --set 'server.resources.requests.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.requests.memory' | tee /dev/stderr) + [ "${actual}" = "256Mi" ] + + local actual=$(helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'server.standalone.enabled=true' \ + --set 'server.resources.limits.memory=256Mi' \ + --set 'server.resources.limits.cpu=250m' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.limits.memory' | tee /dev/stderr) + [ "${actual}" = "256Mi" ] + ``` + + *Note:* If testing more than two conditions, it would be good to separate the `helm template` part of the command from the `yq` sections to reduce redundant work. + +- Check that an entire template file is not rendered + ``` + @test "syncCatalog/Deployment: disabled by default" { + cd `chart_dir` + local actual=$( (helm template \ + --show-only templates/server-statefulset.yaml \ + --set 'global.enabled=false' \ + . || echo "---") | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] + } + ``` + Here we are check the length of the command output to see if the anything is rendered. + This style can easily be switched to check that a file is rendered instead. + +## Contributor License Agreement + +We require that all contributors sign our Contributor License Agreement ("CLA") +before we can accept the contribution. + +[Learn more about why HashiCorp requires a CLA and what the CLA includes](https://www.hashicorp.com/cla) diff --git a/charts/hashicorp/vault/0.22.0/Chart.yaml b/charts/hashicorp/vault/0.22.0/Chart.yaml new file mode 100644 index 000000000..51eb8732d --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/Chart.yaml @@ -0,0 +1,26 @@ +annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Hashicorp Vault + catalog.cattle.io/kube-version: '>= 1.16.0-0' + catalog.cattle.io/release-name: vault +apiVersion: v2 +appVersion: 1.11.3 +description: Official HashiCorp Vault Chart +home: https://www.vaultproject.io +icon: https://github.com/hashicorp/vault/raw/f22d202cde2018f9455dec755118a9b84586e082/Vault_PrimaryLogo_Black.png +keywords: +- vault +- security +- encryption +- secrets +- management +- automation +- infrastructure +kubeVersion: '>= 1.16.0-0' +name: vault +sources: +- https://github.com/hashicorp/vault +- https://github.com/hashicorp/vault-helm +- https://github.com/hashicorp/vault-k8s +- https://github.com/hashicorp/vault-csi-provider +version: 0.22.0 diff --git a/charts/hashicorp/vault/0.22.0/LICENSE.md b/charts/hashicorp/vault/0.22.0/LICENSE.md new file mode 100644 index 000000000..82b4de97c --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/LICENSE.md @@ -0,0 +1,353 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. “Contributor” + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. “Contributor Version” + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution” + + means Covered Software of a particular Contributor. + +1.4. “Covered Software” + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. “Incompatible With Secondary Licenses” + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of version + 1.1 or earlier of the License, but not also under the terms of a + Secondary License. + +1.6. “Executable Form” + + means any form of the work other than Source Code Form. + +1.7. “Larger Work” + + means a work that combines Covered Software with other material, in a separate + file or files, that is not Covered Software. + +1.8. “License” + + means this document. + +1.9. “Licensable” + + means having the right to grant, to the maximum extent possible, whether at the + time of the initial grant or subsequently, any and all of the rights conveyed by + this License. + +1.10. “Modifications” + + means any of the following: + + a. any file in Source Code Form that results from an addition to, deletion + from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. “Patent Claims” of a Contributor + + means any patent claim(s), including without limitation, method, process, + and apparatus claims, in any patent Licensable by such Contributor that + would be infringed, but for the grant of the License, by the making, + using, selling, offering for sale, having made, import, or transfer of + either its Contributions or its Contributor Version. + +1.12. “Secondary License” + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form” + + means the form of the work preferred for making modifications. + +1.14. “You” (or “Your”) + + means an individual or a legal entity exercising rights under this + License. For legal entities, “You” includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, “control” means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or as + part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its Contributions + or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution become + effective for each Contribution on the date the Contributor first distributes + such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under this + License. No additional rights or licenses will be implied from the distribution + or licensing of Covered Software under this License. Notwithstanding Section + 2.1(b) above, no patent license is granted by a Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of its + Contributions. + + This License does not grant any rights in the trademarks, service marks, or + logos of any Contributor (except as may be necessary to comply with the + notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this License + (see Section 10.2) or under the terms of a Secondary License (if permitted + under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its Contributions + are its original creation(s) or it has sufficient rights to grant the + rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under applicable + copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under the + terms of this License. You must inform recipients that the Source Code Form + of the Covered Software is governed by the terms of this License, and how + they can obtain a copy of this License. You may not attempt to alter or + restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this License, + or sublicense it under different terms, provided that the license for + the Executable Form does not attempt to limit or alter the recipients’ + rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for the + Covered Software. If the Larger Work is a combination of Covered Software + with a work governed by one or more Secondary Licenses, and the Covered + Software is not Incompatible With Secondary Licenses, this License permits + You to additionally distribute such Covered Software under the terms of + such Secondary License(s), so that the recipient of the Larger Work may, at + their option, further distribute the Covered Software under the terms of + either this License or such Secondary License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices (including + copyright notices, patent notices, disclaimers of warranty, or limitations + of liability) contained within the Source Code Form of the Covered + Software, except that You may alter any license notices to the extent + required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on behalf + of any Contributor. You must make it absolutely clear that any such + warranty, support, indemnity, or liability obligation is offered by You + alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, judicial + order, or regulation then You must: (a) comply with the terms of this License + to the maximum extent possible; and (b) describe the limitations and the code + they affect. Such description must be placed in a text file included with all + distributions of the Covered Software under this License. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing basis, + if such Contributor fails to notify You of the non-compliance by some + reasonable means prior to 60 days after You have come back into compliance. + Moreover, Your grants from a particular Contributor are reinstated on an + ongoing basis if such Contributor notifies You of the non-compliance by + some reasonable means, this is the first time You have received notice of + non-compliance with this License from such Contributor, and You become + compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, counter-claims, + and cross-claims) alleging that a Contributor Version directly or + indirectly infringes any patent, then the rights granted to You by any and + all Contributors for the Covered Software under Section 2.1 of this License + shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an “as is” basis, without + warranty of any kind, either expressed, implied, or statutory, including, + without limitation, warranties that the Covered Software is free of defects, + merchantable, fit for a particular purpose or non-infringing. The entire + risk as to the quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, You (not any + Contributor) assume the cost of any necessary servicing, repair, or + correction. This disclaimer of warranty constitutes an essential part of this + License. No use of any Covered Software is authorized under this License + except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from such + party’s negligence to the extent applicable law prohibits such limitation. + Some jurisdictions do not allow the exclusion or limitation of incidental or + consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts of + a jurisdiction where the defendant maintains its principal place of business + and such litigation shall be governed by laws of that jurisdiction, without + reference to its conflict-of-law provisions. Nothing in this Section shall + prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. Any law or regulation which provides that the language of a + contract shall be construed against the drafter shall not be used to construe + this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version of + the License under which You originally received the Covered Software, or + under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a relevant +directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is “Incompatible + With Secondary Licenses”, as defined by + the Mozilla Public License, v. 2.0. diff --git a/charts/hashicorp/vault/0.22.0/Makefile b/charts/hashicorp/vault/0.22.0/Makefile new file mode 100644 index 000000000..e423f3529 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/Makefile @@ -0,0 +1,101 @@ +TEST_IMAGE?=vault-helm-test +GOOGLE_CREDENTIALS?=vault-helm-test.json +CLOUDSDK_CORE_PROJECT?=vault-helm-dev-246514 +# set to run a single test - e.g acceptance/server-ha-enterprise-dr.bats +ACCEPTANCE_TESTS?=acceptance + +# filter bats unit tests to run. +UNIT_TESTS_FILTER?='.*' + +# set to 'true' to run acceptance tests locally in a kind cluster +LOCAL_ACCEPTANCE_TESTS?=false + +# kind cluster name +KIND_CLUSTER_NAME?=vault-helm + +# kind k8s version +KIND_K8S_VERSION?=v1.25.0 + +# Generate json schema for chart values. See test/README.md for more details. +values-schema: + helm schema-gen values.yaml > values.schema.json + +test-image: + @docker build --rm -t $(TEST_IMAGE) -f $(CURDIR)/test/docker/Test.dockerfile $(CURDIR) + +test-unit: + @docker run --rm -it -v ${PWD}:/helm-test $(TEST_IMAGE) bats -f $(UNIT_TESTS_FILTER) /helm-test/test/unit + +test-bats: test-unit test-acceptance + +test: test-image test-bats + +# run acceptance tests on GKE +# set google project/credential vars above +test-acceptance: +ifeq ($(LOCAL_ACCEPTANCE_TESTS),true) + make setup-kind acceptance +else + @docker run -it -v ${PWD}:/helm-test \ + -e GOOGLE_CREDENTIALS=${GOOGLE_CREDENTIALS} \ + -e CLOUDSDK_CORE_PROJECT=${CLOUDSDK_CORE_PROJECT} \ + -e KUBECONFIG=/helm-test/.kube/config \ + -e VAULT_LICENSE_CI=${VAULT_LICENSE_CI} \ + -w /helm-test \ + $(TEST_IMAGE) \ + make acceptance +endif + +# destroy GKE cluster using terraform +test-destroy: + @docker run -it -v ${PWD}:/helm-test \ + -e GOOGLE_CREDENTIALS=${GOOGLE_CREDENTIALS} \ + -e CLOUDSDK_CORE_PROJECT=${CLOUDSDK_CORE_PROJECT} \ + -w /helm-test \ + $(TEST_IMAGE) \ + make destroy-cluster + +# provision GKE cluster using terraform +test-provision: + @docker run -it -v ${PWD}:/helm-test \ + -e GOOGLE_CREDENTIALS=${GOOGLE_CREDENTIALS} \ + -e CLOUDSDK_CORE_PROJECT=${CLOUDSDK_CORE_PROJECT} \ + -e KUBECONFIG=/helm-test/.kube/config \ + -w /helm-test \ + $(TEST_IMAGE) \ + make provision-cluster + +# this target is for running the acceptance tests +# it is run in the docker container above when the test-acceptance target is invoked +acceptance: +ifneq ($(LOCAL_ACCEPTANCE_TESTS),true) + gcloud auth activate-service-account --key-file=${GOOGLE_CREDENTIALS} +endif + bats --tap --timing test/${ACCEPTANCE_TESTS} + +# this target is for provisioning the GKE cluster +# it is run in the docker container above when the test-provision target is invoked +provision-cluster: + gcloud auth activate-service-account --key-file=${GOOGLE_CREDENTIALS} + terraform init test/terraform + terraform apply -var project=${CLOUDSDK_CORE_PROJECT} -var init_cli=true -auto-approve test/terraform + +# this target is for removing the GKE cluster +# it is run in the docker container above when the test-destroy target is invoked +destroy-cluster: + terraform destroy -auto-approve + +# create a kind cluster for running the acceptance tests locally +setup-kind: + kind get clusters | grep -q "^${KIND_CLUSTER_NAME}$$" || \ + kind create cluster \ + --image kindest/node:${KIND_K8S_VERSION} \ + --name ${KIND_CLUSTER_NAME} \ + --config $(CURDIR)/test/kind/config.yaml + kubectl config use-context kind-${KIND_CLUSTER_NAME} + +# delete the kind cluster +delete-kind: + kind delete cluster --name ${KIND_CLUSTER_NAME} || : + +.PHONY: values-schema test-image test-unit test-bats test test-acceptance test-destroy test-provision acceptance provision-cluster destroy-cluster diff --git a/charts/hashicorp/vault/0.22.0/README.md b/charts/hashicorp/vault/0.22.0/README.md new file mode 100644 index 000000000..c9971ff41 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/README.md @@ -0,0 +1,43 @@ +# Vault Helm Chart + +> :warning: **Please note**: We take Vault's security and our users' trust very seriously. If +you believe you have found a security issue in Vault Helm, _please responsibly disclose_ +by contacting us at [security@hashicorp.com](mailto:security@hashicorp.com). + +This repository contains the official HashiCorp Helm chart for installing +and configuring Vault on Kubernetes. This chart supports multiple use +cases of Vault on Kubernetes depending on the values provided. + +For full documentation on this Helm chart along with all the ways you can +use Vault with Kubernetes, please see the +[Vault and Kubernetes documentation](https://www.vaultproject.io/docs/platform/k8s/). + +## Prerequisites + +To use the charts here, [Helm](https://helm.sh/) must be configured for your +Kubernetes cluster. Setting up Kubernetes and Helm is outside the scope of +this README. Please refer to the Kubernetes and Helm documentation. + +The versions required are: + + * **Helm 3.6+** + * **Kubernetes 1.16+** - This is the earliest version of Kubernetes tested. + It is possible that this chart works with earlier versions but it is + untested. + +## Usage + +To install the latest version of this chart, add the Hashicorp helm repository +and run `helm install`: + +```console +$ helm repo add hashicorp https://helm.releases.hashicorp.com +"hashicorp" has been added to your repositories + +$ helm install vault hashicorp/vault +``` + +Please see the many options supported in the `values.yaml` file. These are also +fully documented directly on the [Vault +website](https://www.vaultproject.io/docs/platform/k8s/helm) along with more +detailed installation instructions. diff --git a/charts/hashicorp/vault/0.22.0/templates/NOTES.txt b/charts/hashicorp/vault/0.22.0/templates/NOTES.txt new file mode 100644 index 000000000..8e267121c --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/NOTES.txt @@ -0,0 +1,14 @@ + +Thank you for installing HashiCorp Vault! + +Now that you have deployed Vault, you should look over the docs on using +Vault with Kubernetes available here: + +https://www.vaultproject.io/docs/ + + +Your release is named {{ .Release.Name }}. To learn more about the release, try: + + $ helm status {{ .Release.Name }} + $ helm get manifest {{ .Release.Name }} + diff --git a/charts/hashicorp/vault/0.22.0/templates/_helpers.tpl b/charts/hashicorp/vault/0.22.0/templates/_helpers.tpl new file mode 100644 index 000000000..38973910a --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/_helpers.tpl @@ -0,0 +1,953 @@ +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to +this (by the DNS naming spec). If release name contains chart name it will +be used as a full name. +*/}} +{{- define "vault.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "vault.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Expand the name of the chart. +*/}} +{{- define "vault.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Compute if the csi driver is enabled. +*/}} +{{- define "vault.csiEnabled" -}} +{{- $_ := set . "csiEnabled" (or + (eq (.Values.csi.enabled | toString) "true") + (and (eq (.Values.csi.enabled | toString) "-") (eq (.Values.global.enabled | toString) "true"))) -}} +{{- end -}} + +{{/* +Compute if the injector is enabled. +*/}} +{{- define "vault.injectorEnabled" -}} +{{- $_ := set . "injectorEnabled" (or + (eq (.Values.injector.enabled | toString) "true") + (and (eq (.Values.injector.enabled | toString) "-") (eq (.Values.global.enabled | toString) "true"))) -}} +{{- end -}} + +{{/* +Compute if the server is enabled. +*/}} +{{- define "vault.serverEnabled" -}} +{{- $_ := set . "serverEnabled" (or + (eq (.Values.server.enabled | toString) "true") + (and (eq (.Values.server.enabled | toString) "-") (eq (.Values.global.enabled | toString) "true"))) -}} +{{- end -}} + +{{/* +Compute if the server auth delegator serviceaccount is enabled. +*/}} +{{- define "vault.serverServiceAccountEnabled" -}} +{{- $_ := set . "serverServiceAccountEnabled" + (and + (eq (.Values.server.serviceAccount.create | toString) "true" ) + (or + (eq (.Values.server.enabled | toString) "true") + (eq (.Values.global.enabled | toString) "true"))) -}} +{{- end -}} + +{{/* +Compute if the server auth delegator serviceaccount is enabled. +*/}} +{{- define "vault.serverAuthDelegator" -}} +{{- $_ := set . "serverAuthDelegator" + (and + (eq (.Values.server.authDelegator.enabled | toString) "true" ) + (or (eq (.Values.server.serviceAccount.create | toString) "true") + (not (eq .Values.server.serviceAccount.name ""))) + (or + (eq (.Values.server.enabled | toString) "true") + (eq (.Values.global.enabled | toString) "true"))) -}} +{{- end -}} + +{{/* +Compute if the server service is enabled. +*/}} +{{- define "vault.serverServiceEnabled" -}} +{{- template "vault.serverEnabled" . -}} +{{- $_ := set . "serverServiceEnabled" (and .serverEnabled (eq (.Values.server.service.enabled | toString) "true")) -}} +{{- end -}} + +{{/* +Compute if the ui is enabled. +*/}} +{{- define "vault.uiEnabled" -}} +{{- $_ := set . "uiEnabled" (or + (eq (.Values.ui.enabled | toString) "true") + (and (eq (.Values.ui.enabled | toString) "-") (eq (.Values.global.enabled | toString) "true"))) -}} +{{- end -}} + +{{/* +Compute the maximum number of unavailable replicas for the PodDisruptionBudget. +This defaults to (n/2)-1 where n is the number of members of the server cluster. +Add a special case for replicas=1, where it should default to 0 as well. +*/}} +{{- define "vault.pdb.maxUnavailable" -}} +{{- if eq (int .Values.server.ha.replicas) 1 -}} +{{ 0 }} +{{- else if .Values.server.ha.disruptionBudget.maxUnavailable -}} +{{ .Values.server.ha.disruptionBudget.maxUnavailable -}} +{{- else -}} +{{- div (sub (div (mul (int .Values.server.ha.replicas) 10) 2) 1) 10 -}} +{{- end -}} +{{- end -}} + +{{/* +Set the variable 'mode' to the server mode requested by the user to simplify +template logic. +*/}} +{{- define "vault.mode" -}} + {{- template "vault.serverEnabled" . -}} + {{- if or (.Values.injector.externalVaultAddr) (.Values.global.externalVaultAddr) -}} + {{- $_ := set . "mode" "external" -}} + {{- else if not .serverEnabled -}} + {{- $_ := set . "mode" "external" -}} + {{- else if eq (.Values.server.dev.enabled | toString) "true" -}} + {{- $_ := set . "mode" "dev" -}} + {{- else if eq (.Values.server.ha.enabled | toString) "true" -}} + {{- $_ := set . "mode" "ha" -}} + {{- else if or (eq (.Values.server.standalone.enabled | toString) "true") (eq (.Values.server.standalone.enabled | toString) "-") -}} + {{- $_ := set . "mode" "standalone" -}} + {{- else -}} + {{- $_ := set . "mode" "" -}} + {{- end -}} +{{- end -}} + +{{/* +Set's the replica count based on the different modes configured by user +*/}} +{{- define "vault.replicas" -}} + {{ if eq .mode "standalone" }} + {{- default 1 -}} + {{ else if eq .mode "ha" }} + {{- .Values.server.ha.replicas | default 3 -}} + {{ else }} + {{- default 1 -}} + {{ end }} +{{- end -}} + +{{/* +Set's up configmap mounts if this isn't a dev deployment and the user +defined a custom configuration. Additionally iterates over any +extra volumes the user may have specified (such as a secret with TLS). +*/}} +{{- define "vault.volumes" -}} + {{- if and (ne .mode "dev") (or (.Values.server.standalone.config) (.Values.server.ha.config)) }} + - name: config + configMap: + name: {{ template "vault.fullname" . }}-config + {{ end }} + {{- range .Values.server.extraVolumes }} + - name: userconfig-{{ .name }} + {{ .type }}: + {{- if (eq .type "configMap") }} + name: {{ .name }} + {{- else if (eq .type "secret") }} + secretName: {{ .name }} + {{- end }} + defaultMode: {{ .defaultMode | default 420 }} + {{- end }} + {{- if .Values.server.volumes }} + {{- toYaml .Values.server.volumes | nindent 8}} + {{- end }} + {{- if (and .Values.server.enterpriseLicense.secretName .Values.server.enterpriseLicense.secretKey) }} + - name: vault-license + secret: + secretName: {{ .Values.server.enterpriseLicense.secretName }} + defaultMode: 0440 + {{- end }} +{{- end -}} + +{{/* +Set's the args for custom command to render the Vault configuration +file with IP addresses to make the out of box experience easier +for users looking to use this chart with Consul Helm. +*/}} +{{- define "vault.args" -}} + {{ if or (eq .mode "standalone") (eq .mode "ha") }} + - | + cp /vault/config/extraconfig-from-values.hcl /tmp/storageconfig.hcl; + [ -n "${HOST_IP}" ] && sed -Ei "s|HOST_IP|${HOST_IP?}|g" /tmp/storageconfig.hcl; + [ -n "${POD_IP}" ] && sed -Ei "s|POD_IP|${POD_IP?}|g" /tmp/storageconfig.hcl; + [ -n "${HOSTNAME}" ] && sed -Ei "s|HOSTNAME|${HOSTNAME?}|g" /tmp/storageconfig.hcl; + [ -n "${API_ADDR}" ] && sed -Ei "s|API_ADDR|${API_ADDR?}|g" /tmp/storageconfig.hcl; + [ -n "${TRANSIT_ADDR}" ] && sed -Ei "s|TRANSIT_ADDR|${TRANSIT_ADDR?}|g" /tmp/storageconfig.hcl; + [ -n "${RAFT_ADDR}" ] && sed -Ei "s|RAFT_ADDR|${RAFT_ADDR?}|g" /tmp/storageconfig.hcl; + /usr/local/bin/docker-entrypoint.sh vault server -config=/tmp/storageconfig.hcl {{ .Values.server.extraArgs }} + {{ else if eq .mode "dev" }} + - | + /usr/local/bin/docker-entrypoint.sh vault server -dev {{ .Values.server.extraArgs }} + {{ end }} +{{- end -}} + +{{/* +Set's additional environment variables based on the mode. +*/}} +{{- define "vault.envs" -}} + {{ if eq .mode "dev" }} + - name: VAULT_DEV_ROOT_TOKEN_ID + value: {{ .Values.server.dev.devRootToken }} + - name: VAULT_DEV_LISTEN_ADDRESS + value: "[::]:8200" + {{ end }} +{{- end -}} + +{{/* +Set's which additional volumes should be mounted to the container +based on the mode configured. +*/}} +{{- define "vault.mounts" -}} + {{ if eq (.Values.server.auditStorage.enabled | toString) "true" }} + - name: audit + mountPath: {{ .Values.server.auditStorage.mountPath }} + {{ end }} + {{ if or (eq .mode "standalone") (and (eq .mode "ha") (eq (.Values.server.ha.raft.enabled | toString) "true")) }} + {{ if eq (.Values.server.dataStorage.enabled | toString) "true" }} + - name: data + mountPath: {{ .Values.server.dataStorage.mountPath }} + {{ end }} + {{ end }} + {{ if and (ne .mode "dev") (or (.Values.server.standalone.config) (.Values.server.ha.config)) }} + - name: config + mountPath: /vault/config + {{ end }} + {{- range .Values.server.extraVolumes }} + - name: userconfig-{{ .name }} + readOnly: true + mountPath: {{ .path | default "/vault/userconfig" }}/{{ .name }} + {{- end }} + {{- if .Values.server.volumeMounts }} + {{- toYaml .Values.server.volumeMounts | nindent 12}} + {{- end }} + {{- if (and .Values.server.enterpriseLicense.secretName .Values.server.enterpriseLicense.secretKey) }} + - name: vault-license + mountPath: /vault/license + readOnly: true + {{- end }} +{{- end -}} + +{{/* +Set's up the volumeClaimTemplates when data or audit storage is required. HA +might not use data storage since Consul is likely it's backend, however, audit +storage might be desired by the user. +*/}} +{{- define "vault.volumeclaims" -}} + {{- if and (ne .mode "dev") (or .Values.server.dataStorage.enabled .Values.server.auditStorage.enabled) }} + volumeClaimTemplates: + {{- if and (eq (.Values.server.dataStorage.enabled | toString) "true") (or (eq .mode "standalone") (eq (.Values.server.ha.raft.enabled | toString ) "true" )) }} + - metadata: + name: data + {{- include "vault.dataVolumeClaim.annotations" . | nindent 6 }} + spec: + accessModes: + - {{ .Values.server.dataStorage.accessMode | default "ReadWriteOnce" }} + resources: + requests: + storage: {{ .Values.server.dataStorage.size }} + {{- if .Values.server.dataStorage.storageClass }} + storageClassName: {{ .Values.server.dataStorage.storageClass }} + {{- end }} + {{ end }} + {{- if eq (.Values.server.auditStorage.enabled | toString) "true" }} + - metadata: + name: audit + {{- include "vault.auditVolumeClaim.annotations" . | nindent 6 }} + spec: + accessModes: + - {{ .Values.server.auditStorage.accessMode | default "ReadWriteOnce" }} + resources: + requests: + storage: {{ .Values.server.auditStorage.size }} + {{- if .Values.server.auditStorage.storageClass }} + storageClassName: {{ .Values.server.auditStorage.storageClass }} + {{- end }} + {{ end }} + {{ end }} +{{- end -}} + +{{/* +Set's the affinity for pod placement when running in standalone and HA modes. +*/}} +{{- define "vault.affinity" -}} + {{- if and (ne .mode "dev") .Values.server.affinity }} + affinity: + {{ $tp := typeOf .Values.server.affinity }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.affinity . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.server.affinity | nindent 8 }} + {{- end }} + {{ end }} +{{- end -}} + +{{/* +Sets the injector affinity for pod placement +*/}} +{{- define "injector.affinity" -}} + {{- if .Values.injector.affinity }} + affinity: + {{ $tp := typeOf .Values.injector.affinity }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.affinity . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.injector.affinity | nindent 8 }} + {{- end }} + {{ end }} +{{- end -}} + +{{/* +Sets the topologySpreadConstraints when running in standalone and HA modes. +*/}} +{{- define "vault.topologySpreadConstraints" -}} + {{- if and (ne .mode "dev") .Values.server.topologySpreadConstraints }} + topologySpreadConstraints: + {{ $tp := typeOf .Values.server.topologySpreadConstraints }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.topologySpreadConstraints . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.server.topologySpreadConstraints | nindent 8 }} + {{- end }} + {{ end }} +{{- end -}} + + +{{/* +Sets the injector topologySpreadConstraints for pod placement +*/}} +{{- define "injector.topologySpreadConstraints" -}} + {{- if .Values.injector.topologySpreadConstraints }} + topologySpreadConstraints: + {{ $tp := typeOf .Values.injector.topologySpreadConstraints }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.topologySpreadConstraints . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.injector.topologySpreadConstraints | nindent 8 }} + {{- end }} + {{ end }} +{{- end -}} + +{{/* +Sets the toleration for pod placement when running in standalone and HA modes. +*/}} +{{- define "vault.tolerations" -}} + {{- if and (ne .mode "dev") .Values.server.tolerations }} + tolerations: + {{- $tp := typeOf .Values.server.tolerations }} + {{- if eq $tp "string" }} + {{ tpl .Values.server.tolerations . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.server.tolerations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets the injector toleration for pod placement +*/}} +{{- define "injector.tolerations" -}} + {{- if .Values.injector.tolerations }} + tolerations: + {{- $tp := typeOf .Values.injector.tolerations }} + {{- if eq $tp "string" }} + {{ tpl .Values.injector.tolerations . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.injector.tolerations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Set's the node selector for pod placement when running in standalone and HA modes. +*/}} +{{- define "vault.nodeselector" -}} + {{- if and (ne .mode "dev") .Values.server.nodeSelector }} + nodeSelector: + {{- $tp := typeOf .Values.server.nodeSelector }} + {{- if eq $tp "string" }} + {{ tpl .Values.server.nodeSelector . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.server.nodeSelector | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets the injector node selector for pod placement +*/}} +{{- define "injector.nodeselector" -}} + {{- if .Values.injector.nodeSelector }} + nodeSelector: + {{- $tp := typeOf .Values.injector.nodeSelector }} + {{- if eq $tp "string" }} + {{ tpl .Values.injector.nodeSelector . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.injector.nodeSelector | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets the injector deployment update strategy +*/}} +{{- define "injector.strategy" -}} + {{- if .Values.injector.strategy }} + strategy: + {{- $tp := typeOf .Values.injector.strategy }} + {{- if eq $tp "string" }} + {{ tpl .Values.injector.strategy . | nindent 4 | trim }} + {{- else }} + {{- toYaml .Values.injector.strategy | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra pod annotations +*/}} +{{- define "vault.annotations" -}} + {{- if .Values.server.annotations }} + annotations: + {{- $tp := typeOf .Values.server.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.annotations . | nindent 8 }} + {{- else }} + {{- toYaml .Values.server.annotations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra injector pod annotations +*/}} +{{- define "injector.annotations" -}} + {{- if .Values.injector.annotations }} + annotations: + {{- $tp := typeOf .Values.injector.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.annotations . | nindent 8 }} + {{- else }} + {{- toYaml .Values.injector.annotations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra injector service annotations +*/}} +{{- define "injector.service.annotations" -}} + {{- if .Values.injector.service.annotations }} + annotations: + {{- $tp := typeOf .Values.injector.service.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.service.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.injector.service.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +securityContext for the injector pod level. +*/}} +{{- define "injector.securityContext.pod" -}} + {{- if .Values.injector.securityContext.pod }} + securityContext: + {{- $tp := typeOf .Values.injector.securityContext.pod }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.securityContext.pod . | nindent 8 }} + {{- else }} + {{- toYaml .Values.injector.securityContext.pod | nindent 8 }} + {{- end }} + {{- else if not .Values.global.openshift }} + securityContext: + runAsNonRoot: true + runAsGroup: {{ .Values.injector.gid | default 1000 }} + runAsUser: {{ .Values.injector.uid | default 100 }} + fsGroup: {{ .Values.injector.gid | default 1000 }} + {{- end }} +{{- end -}} + +{{/* +securityContext for the injector container level. +*/}} +{{- define "injector.securityContext.container" -}} + {{- if .Values.injector.securityContext.container}} + securityContext: + {{- $tp := typeOf .Values.injector.securityContext.container }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.securityContext.container . | nindent 12 }} + {{- else }} + {{- toYaml .Values.injector.securityContext.container | nindent 12 }} + {{- end }} + {{- else if not .Values.global.openshift }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + {{- end }} +{{- end -}} + +{{/* +securityContext for the statefulset pod template. +*/}} +{{- define "server.statefulSet.securityContext.pod" -}} + {{- if .Values.server.statefulSet.securityContext.pod }} + securityContext: + {{- $tp := typeOf .Values.server.statefulSet.securityContext.pod }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.statefulSet.securityContext.pod . | nindent 8 }} + {{- else }} + {{- toYaml .Values.server.statefulSet.securityContext.pod | nindent 8 }} + {{- end }} + {{- else if not .Values.global.openshift }} + securityContext: + runAsNonRoot: true + runAsGroup: {{ .Values.server.gid | default 1000 }} + runAsUser: {{ .Values.server.uid | default 100 }} + fsGroup: {{ .Values.server.gid | default 1000 }} + {{- end }} +{{- end -}} + +{{/* +securityContext for the statefulset vault container +*/}} +{{- define "server.statefulSet.securityContext.container" -}} + {{- if .Values.server.statefulSet.securityContext.container }} + securityContext: + {{- $tp := typeOf .Values.server.statefulSet.securityContext.container }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.statefulSet.securityContext.container . | nindent 12 }} + {{- else }} + {{- toYaml .Values.server.statefulSet.securityContext.container | nindent 12 }} + {{- end }} + {{- else if not .Values.global.openshift }} + securityContext: + allowPrivilegeEscalation: false + {{- end }} +{{- end -}} + + +{{/* +Sets extra injector service account annotations +*/}} +{{- define "injector.serviceAccount.annotations" -}} + {{- if and (ne .mode "dev") .Values.injector.serviceAccount.annotations }} + annotations: + {{- $tp := typeOf .Values.injector.serviceAccount.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.injector.serviceAccount.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.injector.serviceAccount.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra injector webhook annotations +*/}} +{{- define "injector.webhookAnnotations" -}} + {{- if or (((.Values.injector.webhook)).annotations) (.Values.injector.webhookAnnotations) }} + annotations: + {{- $tp := typeOf (or (((.Values.injector.webhook)).annotations) (.Values.injector.webhookAnnotations)) }} + {{- if eq $tp "string" }} + {{- tpl (((.Values.injector.webhook)).annotations | default .Values.injector.webhookAnnotations) . | nindent 4 }} + {{- else }} + {{- toYaml (((.Values.injector.webhook)).annotations | default .Values.injector.webhookAnnotations) | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Set's the injector webhook objectSelector +*/}} +{{- define "injector.objectSelector" -}} + {{- $v := or (((.Values.injector.webhook)).objectSelector) (.Values.injector.objectSelector) -}} + {{ if $v }} + objectSelector: + {{- $tp := typeOf $v -}} + {{ if eq $tp "string" }} + {{ tpl $v . | indent 6 | trim }} + {{ else }} + {{ toYaml $v | indent 6 | trim }} + {{ end }} + {{ end }} +{{ end }} + +{{/* +Sets extra ui service annotations +*/}} +{{- define "vault.ui.annotations" -}} + {{- if .Values.ui.annotations }} + annotations: + {{- $tp := typeOf .Values.ui.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.ui.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.ui.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "vault.serviceAccount.name" -}} +{{- if .Values.server.serviceAccount.create -}} + {{ default (include "vault.fullname" .) .Values.server.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.server.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Sets extra service account annotations +*/}} +{{- define "vault.serviceAccount.annotations" -}} + {{- if and (ne .mode "dev") .Values.server.serviceAccount.annotations }} + annotations: + {{- $tp := typeOf .Values.server.serviceAccount.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.serviceAccount.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.serviceAccount.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra ingress annotations +*/}} +{{- define "vault.ingress.annotations" -}} + {{- if .Values.server.ingress.annotations }} + annotations: + {{- $tp := typeOf .Values.server.ingress.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.ingress.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.ingress.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra route annotations +*/}} +{{- define "vault.route.annotations" -}} + {{- if .Values.server.route.annotations }} + annotations: + {{- $tp := typeOf .Values.server.route.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.route.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.route.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra vault server Service annotations +*/}} +{{- define "vault.service.annotations" -}} + {{- if .Values.server.service.annotations }} + {{- $tp := typeOf .Values.server.service.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.service.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.service.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets PodSecurityPolicy annotations +*/}} +{{- define "vault.psp.annotations" -}} + {{- if .Values.global.psp.annotations }} + annotations: + {{- $tp := typeOf .Values.global.psp.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.global.psp.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.global.psp.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra statefulset annotations +*/}} +{{- define "vault.statefulSet.annotations" -}} + {{- if .Values.server.statefulSet.annotations }} + annotations: + {{- $tp := typeOf .Values.server.statefulSet.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.statefulSet.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.statefulSet.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets VolumeClaim annotations for data volume +*/}} +{{- define "vault.dataVolumeClaim.annotations" -}} + {{- if and (ne .mode "dev") (.Values.server.dataStorage.enabled) (.Values.server.dataStorage.annotations) }} + annotations: + {{- $tp := typeOf .Values.server.dataStorage.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.dataStorage.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.dataStorage.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets VolumeClaim annotations for audit volume +*/}} +{{- define "vault.auditVolumeClaim.annotations" -}} + {{- if and (ne .mode "dev") (.Values.server.auditStorage.enabled) (.Values.server.auditStorage.annotations) }} + annotations: + {{- $tp := typeOf .Values.server.auditStorage.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.server.auditStorage.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.server.auditStorage.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Set's the container resources if the user has set any. +*/}} +{{- define "vault.resources" -}} + {{- if .Values.server.resources -}} + resources: +{{ toYaml .Values.server.resources | indent 12}} + {{ end }} +{{- end -}} + +{{/* +Sets the container resources if the user has set any. +*/}} +{{- define "injector.resources" -}} + {{- if .Values.injector.resources -}} + resources: +{{ toYaml .Values.injector.resources | indent 12}} + {{ end }} +{{- end -}} + +{{/* +Sets the container resources if the user has set any. +*/}} +{{- define "csi.resources" -}} + {{- if .Values.csi.resources -}} + resources: +{{ toYaml .Values.csi.resources | indent 12}} + {{ end }} +{{- end -}} + +{{/* +Sets extra CSI daemonset annotations +*/}} +{{- define "csi.daemonSet.annotations" -}} + {{- if .Values.csi.daemonSet.annotations }} + annotations: + {{- $tp := typeOf .Values.csi.daemonSet.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.csi.daemonSet.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.csi.daemonSet.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets CSI daemonset securityContext for pod template +*/}} +{{- define "csi.daemonSet.securityContext.pod" -}} + {{- if .Values.csi.daemonSet.securityContext.pod }} + securityContext: + {{- $tp := typeOf .Values.csi.daemonSet.securityContext.pod }} + {{- if eq $tp "string" }} + {{- tpl .Values.csi.daemonSet.securityContext.pod . | nindent 8 }} + {{- else }} + {{- toYaml .Values.csi.daemonSet.securityContext.pod | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets CSI daemonset securityContext for container +*/}} +{{- define "csi.daemonSet.securityContext.container" -}} + {{- if .Values.csi.daemonSet.securityContext.container }} + securityContext: + {{- $tp := typeOf .Values.csi.daemonSet.securityContext.container }} + {{- if eq $tp "string" }} + {{- tpl .Values.csi.daemonSet.securityContext.container . | nindent 12 }} + {{- else }} + {{- toYaml .Values.csi.daemonSet.securityContext.container | nindent 12 }} + {{- end }} + {{- end }} +{{- end -}} + + +{{/* +Sets the injector toleration for pod placement +*/}} +{{- define "csi.pod.tolerations" -}} + {{- if .Values.csi.pod.tolerations }} + tolerations: + {{- $tp := typeOf .Values.csi.pod.tolerations }} + {{- if eq $tp "string" }} + {{ tpl .Values.csi.pod.tolerations . | nindent 8 | trim }} + {{- else }} + {{- toYaml .Values.csi.pod.tolerations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra CSI provider pod annotations +*/}} +{{- define "csi.pod.annotations" -}} + {{- if .Values.csi.pod.annotations }} + annotations: + {{- $tp := typeOf .Values.csi.pod.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.csi.pod.annotations . | nindent 8 }} + {{- else }} + {{- toYaml .Values.csi.pod.annotations | nindent 8 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Sets extra CSI service account annotations +*/}} +{{- define "csi.serviceAccount.annotations" -}} + {{- if .Values.csi.serviceAccount.annotations }} + annotations: + {{- $tp := typeOf .Values.csi.serviceAccount.annotations }} + {{- if eq $tp "string" }} + {{- tpl .Values.csi.serviceAccount.annotations . | nindent 4 }} + {{- else }} + {{- toYaml .Values.csi.serviceAccount.annotations | nindent 4 }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Inject extra environment vars in the format key:value, if populated +*/}} +{{- define "vault.extraEnvironmentVars" -}} +{{- if .extraEnvironmentVars -}} +{{- range $key, $value := .extraEnvironmentVars }} +- name: {{ printf "%s" $key | replace "." "_" | upper | quote }} + value: {{ $value | quote }} +{{- end }} +{{- end -}} +{{- end -}} + +{{/* +Inject extra environment populated by secrets, if populated +*/}} +{{- define "vault.extraSecretEnvironmentVars" -}} +{{- if .extraSecretEnvironmentVars -}} +{{- range .extraSecretEnvironmentVars }} +- name: {{ .envName }} + valueFrom: + secretKeyRef: + name: {{ .secretName }} + key: {{ .secretKey }} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* Scheme for health check and local endpoint */}} +{{- define "vault.scheme" -}} +{{- if .Values.global.tlsDisable -}} +{{ "http" }} +{{- else -}} +{{ "https" }} +{{- end -}} +{{- end -}} + +{{/* +imagePullSecrets generates pull secrets from either string or map values. +A map value must be indexable by the key 'name'. +*/}} +{{- define "imagePullSecrets" -}} +{{- with .Values.global.imagePullSecrets -}} +imagePullSecrets: +{{- range . -}} +{{- if typeIs "string" . }} + - name: {{ . }} +{{- else if index . "name" }} + - name: {{ .name }} +{{- end }} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +externalTrafficPolicy sets a Service's externalTrafficPolicy if applicable. +Supported inputs are Values.server.service and Values.ui +*/}} +{{- define "service.externalTrafficPolicy" -}} +{{- $type := "" -}} +{{- if .serviceType -}} +{{- $type = .serviceType -}} +{{- else if .type -}} +{{- $type = .type -}} +{{- end -}} +{{- if and .externalTrafficPolicy (or (eq $type "LoadBalancer") (eq $type "NodePort")) }} + externalTrafficPolicy: {{ .externalTrafficPolicy }} +{{- else }} +{{- end }} +{{- end -}} + +{{/* +loadBalancer configuration for the the UI service. +Supported inputs are Values.ui +*/}} +{{- define "service.loadBalancer" -}} +{{- if eq (.serviceType | toString) "LoadBalancer" }} +{{- if .loadBalancerIP }} + loadBalancerIP: {{ .loadBalancerIP }} +{{- end }} +{{- with .loadBalancerSourceRanges }} + loadBalancerSourceRanges: +{{- range . }} + - {{ . }} +{{- end }} +{{- end -}} +{{- end }} +{{- end -}} diff --git a/charts/hashicorp/vault/0.22.0/templates/csi-clusterrole.yaml b/charts/hashicorp/vault/0.22.0/templates/csi-clusterrole.yaml new file mode 100644 index 000000000..ec6a3d2b9 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/csi-clusterrole.yaml @@ -0,0 +1,18 @@ +{{- template "vault.csiEnabled" . -}} +{{- if .csiEnabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "vault.fullname" . }}-csi-provider-clusterrole + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: + - "" + resources: + - serviceaccounts/token + verbs: + - create +{{- end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/csi-clusterrolebinding.yaml b/charts/hashicorp/vault/0.22.0/templates/csi-clusterrolebinding.yaml new file mode 100644 index 000000000..d5b62a5f0 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/csi-clusterrolebinding.yaml @@ -0,0 +1,19 @@ +{{- template "vault.csiEnabled" . -}} +{{- if .csiEnabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "vault.fullname" . }}-csi-provider-clusterrolebinding + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "vault.fullname" . }}-csi-provider-clusterrole +subjects: +- kind: ServiceAccount + name: {{ template "vault.fullname" . }}-csi-provider + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/csi-daemonset.yaml b/charts/hashicorp/vault/0.22.0/templates/csi-daemonset.yaml new file mode 100644 index 000000000..d131aac5f --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/csi-daemonset.yaml @@ -0,0 +1,100 @@ +{{- template "vault.csiEnabled" . -}} +{{- if .csiEnabled -}} +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: {{ template "vault.fullname" . }}-csi-provider + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- if .Values.csi.daemonSet.extraLabels -}} + {{- toYaml .Values.csi.daemonSet.extraLabels | nindent 4 -}} + {{- end -}} + {{ template "csi.daemonSet.annotations" . }} +spec: + updateStrategy: + type: {{ .Values.csi.daemonSet.updateStrategy.type }} + {{- if .Values.csi.daemonSet.updateStrategy.maxUnavailable }} + rollingUpdate: + maxUnavailable: {{ .Values.csi.daemonSet.updateStrategy.maxUnavailable }} + {{- end }} + selector: + matchLabels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + template: + metadata: + labels: + app.kubernetes.io/name: {{ template "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + {{- if .Values.csi.pod.extraLabels -}} + {{- toYaml .Values.csi.pod.extraLabels | nindent 8 -}} + {{- end -}} + {{ template "csi.pod.annotations" . }} + spec: + {{ template "csi.daemonSet.securityContext.pod" . }} + {{- if .Values.csi.priorityClassName }} + priorityClassName: {{ .Values.csi.priorityClassName }} + {{- end }} + serviceAccountName: {{ template "vault.fullname" . }}-csi-provider + {{- template "csi.pod.tolerations" . }} + containers: + - name: {{ include "vault.name" . }}-csi-provider + {{ template "csi.resources" . }} + {{ template "csi.daemonSet.securityContext.container" . }} + image: "{{ .Values.csi.image.repository }}:{{ .Values.csi.image.tag }}" + imagePullPolicy: {{ .Values.csi.image.pullPolicy }} + args: + - --endpoint=/provider/vault.sock + - --debug={{ .Values.csi.debug }} + {{- if .Values.csi.extraArgs }} + {{- toYaml .Values.csi.extraArgs | nindent 12 }} + {{- end }} + env: + - name: VAULT_ADDR + {{- if .Values.global.externalVaultAddr }} + value: "{{ .Values.global.externalVaultAddr }}" + {{- else }} + value: {{ include "vault.scheme" . }}://{{ template "vault.fullname" . }}.{{ .Release.Namespace }}.svc:{{ .Values.server.service.port }} + {{- end }} + volumeMounts: + - name: providervol + mountPath: "/provider" + - name: mountpoint-dir + mountPath: {{ .Values.csi.daemonSet.kubeletRootDir }}/pods + mountPropagation: HostToContainer + {{- if .Values.csi.volumeMounts }} + {{- toYaml .Values.csi.volumeMounts | nindent 12}} + {{- end }} + livenessProbe: + httpGet: + path: /health/ready + port: 8080 + failureThreshold: {{ .Values.csi.livenessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.csi.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.csi.livenessProbe.periodSeconds }} + successThreshold: {{ .Values.csi.livenessProbe.successThreshold }} + timeoutSeconds: {{ .Values.csi.livenessProbe.timeoutSeconds }} + readinessProbe: + httpGet: + path: /health/ready + port: 8080 + failureThreshold: {{ .Values.csi.readinessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.csi.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.csi.readinessProbe.periodSeconds }} + successThreshold: {{ .Values.csi.readinessProbe.successThreshold }} + timeoutSeconds: {{ .Values.csi.readinessProbe.timeoutSeconds }} + volumes: + - name: providervol + hostPath: + path: {{ .Values.csi.daemonSet.providersDir }} + - name: mountpoint-dir + hostPath: + path: {{ .Values.csi.daemonSet.kubeletRootDir }}/pods + {{- if .Values.csi.volumes }} + {{- toYaml .Values.csi.volumes | nindent 8}} + {{- end }} + {{- include "imagePullSecrets" . | nindent 6 }} +{{- end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/csi-serviceaccount.yaml b/charts/hashicorp/vault/0.22.0/templates/csi-serviceaccount.yaml new file mode 100644 index 000000000..8d6fa5329 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/csi-serviceaccount.yaml @@ -0,0 +1,16 @@ +{{- template "vault.csiEnabled" . -}} +{{- if .csiEnabled -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "vault.fullname" . }}-csi-provider + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-csi-provider + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- if .Values.csi.serviceAccount.extraLabels -}} + {{- toYaml .Values.csi.serviceAccount.extraLabels | nindent 4 -}} + {{- end -}} + {{ template "csi.serviceAccount.annotations" . }} +{{- end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/injector-certs-secret.yaml b/charts/hashicorp/vault/0.22.0/templates/injector-certs-secret.yaml new file mode 100644 index 000000000..e88685b5e --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/injector-certs-secret.yaml @@ -0,0 +1,14 @@ +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if and (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} +apiVersion: v1 +kind: Secret +metadata: + name: vault-injector-certs + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/hashicorp/vault/0.22.0/templates/injector-clusterrole.yaml b/charts/hashicorp/vault/0.22.0/templates/injector-clusterrole.yaml new file mode 100644 index 000000000..6a0d6be1a --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/injector-clusterrole.yaml @@ -0,0 +1,19 @@ +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-clusterrole + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: ["admissionregistration.k8s.io"] + resources: ["mutatingwebhookconfigurations"] + verbs: + - "get" + - "list" + - "watch" + - "patch" +{{ end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/injector-clusterrolebinding.yaml b/charts/hashicorp/vault/0.22.0/templates/injector-clusterrolebinding.yaml new file mode 100644 index 000000000..4c193f8a2 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/injector-clusterrolebinding.yaml @@ -0,0 +1,19 @@ +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-binding + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "vault.fullname" . }}-agent-injector-clusterrole +subjects: +- kind: ServiceAccount + name: {{ template "vault.fullname" . }}-agent-injector + namespace: {{ .Release.Namespace }} +{{ end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/injector-deployment.yaml b/charts/hashicorp/vault/0.22.0/templates/injector-deployment.yaml new file mode 100644 index 000000000..f0605599e --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/injector-deployment.yaml @@ -0,0 +1,156 @@ +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +# Deployment for the injector +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "vault.fullname" . }}-agent-injector + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + component: webhook +spec: + replicas: {{ .Values.injector.replicas }} + selector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + component: webhook + {{ template "injector.strategy" . }} + template: + metadata: + labels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + component: webhook + {{- if .Values.injector.extraLabels -}} + {{- toYaml .Values.injector.extraLabels | nindent 8 -}} + {{- end -}} + {{ template "injector.annotations" . }} + spec: + {{ template "injector.affinity" . }} + {{ template "injector.topologySpreadConstraints" . }} + {{ template "injector.tolerations" . }} + {{ template "injector.nodeselector" . }} + {{- if .Values.injector.priorityClassName }} + priorityClassName: {{ .Values.injector.priorityClassName }} + {{- end }} + serviceAccountName: "{{ template "vault.fullname" . }}-agent-injector" + {{ template "injector.securityContext.pod" . -}} + {{- if not .Values.global.openshift }} + hostNetwork: {{ .Values.injector.hostNetwork }} + {{- end }} + containers: + - name: sidecar-injector + {{ template "injector.resources" . }} + image: "{{ .Values.injector.image.repository }}:{{ .Values.injector.image.tag }}" + imagePullPolicy: "{{ .Values.injector.image.pullPolicy }}" + {{- template "injector.securityContext.container" . }} + env: + - name: AGENT_INJECT_LISTEN + value: {{ printf ":%v" .Values.injector.port }} + - name: AGENT_INJECT_LOG_LEVEL + value: {{ .Values.injector.logLevel | default "info" }} + - name: AGENT_INJECT_VAULT_ADDR + {{- if .Values.global.externalVaultAddr }} + value: "{{ .Values.global.externalVaultAddr }}" + {{- else if .Values.injector.externalVaultAddr }} + value: "{{ .Values.injector.externalVaultAddr }}" + {{- else }} + value: {{ include "vault.scheme" . }}://{{ template "vault.fullname" . }}.{{ .Release.Namespace }}.svc:{{ .Values.server.service.port }} + {{- end }} + - name: AGENT_INJECT_VAULT_AUTH_PATH + value: {{ .Values.injector.authPath }} + - name: AGENT_INJECT_VAULT_IMAGE + value: "{{ .Values.injector.agentImage.repository }}:{{ .Values.injector.agentImage.tag }}" + {{- if .Values.injector.certs.secretName }} + - name: AGENT_INJECT_TLS_CERT_FILE + value: "/etc/webhook/certs/{{ .Values.injector.certs.certName }}" + - name: AGENT_INJECT_TLS_KEY_FILE + value: "/etc/webhook/certs/{{ .Values.injector.certs.keyName }}" + {{- else }} + - name: AGENT_INJECT_TLS_AUTO + value: {{ template "vault.fullname" . }}-agent-injector-cfg + - name: AGENT_INJECT_TLS_AUTO_HOSTS + value: {{ template "vault.fullname" . }}-agent-injector-svc,{{ template "vault.fullname" . }}-agent-injector-svc.{{ .Release.Namespace }},{{ template "vault.fullname" . }}-agent-injector-svc.{{ .Release.Namespace }}.svc + {{- end }} + - name: AGENT_INJECT_LOG_FORMAT + value: {{ .Values.injector.logFormat | default "standard" }} + - name: AGENT_INJECT_REVOKE_ON_SHUTDOWN + value: "{{ .Values.injector.revokeOnShutdown | default false }}" + {{- if .Values.global.openshift }} + - name: AGENT_INJECT_SET_SECURITY_CONTEXT + value: "false" + {{- end }} + {{- if .Values.injector.metrics.enabled }} + - name: AGENT_INJECT_TELEMETRY_PATH + value: "/metrics" + {{- end }} + {{- if and (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} + - name: AGENT_INJECT_USE_LEADER_ELECTOR + value: "true" + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- end }} + - name: AGENT_INJECT_CPU_REQUEST + value: "{{ .Values.injector.agentDefaults.cpuRequest }}" + - name: AGENT_INJECT_CPU_LIMIT + value: "{{ .Values.injector.agentDefaults.cpuLimit }}" + - name: AGENT_INJECT_MEM_REQUEST + value: "{{ .Values.injector.agentDefaults.memRequest }}" + - name: AGENT_INJECT_MEM_LIMIT + value: "{{ .Values.injector.agentDefaults.memLimit }}" + - name: AGENT_INJECT_DEFAULT_TEMPLATE + value: "{{ .Values.injector.agentDefaults.template }}" + - name: AGENT_INJECT_TEMPLATE_CONFIG_EXIT_ON_RETRY_FAILURE + value: "{{ .Values.injector.agentDefaults.templateConfig.exitOnRetryFailure }}" + {{- if .Values.injector.agentDefaults.templateConfig.staticSecretRenderInterval }} + - name: AGENT_INJECT_TEMPLATE_STATIC_SECRET_RENDER_INTERVAL + value: "{{ .Values.injector.agentDefaults.templateConfig.staticSecretRenderInterval }}" + {{- end }} + {{- include "vault.extraEnvironmentVars" .Values.injector | nindent 12 }} + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + args: + - agent-inject + - 2>&1 + livenessProbe: + httpGet: + path: /health/ready + port: {{ .Values.injector.port }} + scheme: HTTPS + failureThreshold: 2 + initialDelaySeconds: 5 + periodSeconds: 2 + successThreshold: 1 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: /health/ready + port: {{ .Values.injector.port }} + scheme: HTTPS + failureThreshold: 2 + initialDelaySeconds: 5 + periodSeconds: 2 + successThreshold: 1 + timeoutSeconds: 5 +{{- if .Values.injector.certs.secretName }} + volumeMounts: + - name: webhook-certs + mountPath: /etc/webhook/certs + readOnly: true +{{- end }} +{{- if .Values.injector.certs.secretName }} + volumes: + - name: webhook-certs + secret: + secretName: "{{ .Values.injector.certs.secretName }}" +{{- end }} + {{- include "imagePullSecrets" . | nindent 6 }} +{{ end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/injector-disruptionbudget.yaml b/charts/hashicorp/vault/0.22.0/templates/injector-disruptionbudget.yaml new file mode 100644 index 000000000..b44fd7300 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/injector-disruptionbudget.yaml @@ -0,0 +1,20 @@ +{{- if .Values.injector.podDisruptionBudget }} +apiVersion: {{ ge .Capabilities.KubeVersion.Minor "21" | ternary "policy/v1" "policy/v1beta1" }} +kind: PodDisruptionBudget +metadata: + name: {{ template "vault.fullname" . }}-agent-injector + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + component: webhook +spec: + selector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + component: webhook + {{- toYaml .Values.injector.podDisruptionBudget | nindent 2 }} +{{- end -}} diff --git a/charts/hashicorp/vault/0.22.0/templates/injector-mutating-webhook.yaml b/charts/hashicorp/vault/0.22.0/templates/injector-mutating-webhook.yaml new file mode 100644 index 000000000..3d3fd3678 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/injector-mutating-webhook.yaml @@ -0,0 +1,39 @@ +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if .Capabilities.APIVersions.Has "admissionregistration.k8s.io/v1" }} +apiVersion: admissionregistration.k8s.io/v1 +{{- else }} +apiVersion: admissionregistration.k8s.io/v1beta1 +{{- end }} +kind: MutatingWebhookConfiguration +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-cfg + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- template "injector.webhookAnnotations" . }} +webhooks: + - name: vault.hashicorp.com + failurePolicy: {{ ((.Values.injector.webhook)).failurePolicy | default .Values.injector.failurePolicy }} + matchPolicy: {{ ((.Values.injector.webhook)).matchPolicy | default "Exact" }} + sideEffects: None + timeoutSeconds: {{ ((.Values.injector.webhook)).timeoutSeconds | default "30" }} + admissionReviewVersions: ["v1", "v1beta1"] + clientConfig: + service: + name: {{ template "vault.fullname" . }}-agent-injector-svc + namespace: {{ .Release.Namespace }} + path: "/mutate" + caBundle: {{ .Values.injector.certs.caBundle | quote }} + rules: + - operations: ["CREATE", "UPDATE"] + apiGroups: [""] + apiVersions: ["v1"] + resources: ["pods"] +{{- if or (.Values.injector.namespaceSelector) (((.Values.injector.webhook)).namespaceSelector) }} + namespaceSelector: +{{ toYaml (((.Values.injector.webhook)).namespaceSelector | default .Values.injector.namespaceSelector) | indent 6}} +{{ end }} +{{- template "injector.objectSelector" . -}} +{{ end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/injector-network-policy.yaml b/charts/hashicorp/vault/0.22.0/templates/injector-network-policy.yaml new file mode 100644 index 000000000..68892d23b --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/injector-network-policy.yaml @@ -0,0 +1,24 @@ +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if eq (.Values.global.openshift | toString) "true" }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ template "vault.fullname" . }}-agent-injector + labels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + component: webhook + ingress: + - from: + - namespaceSelector: {} + ports: + - port: 8080 + protocol: TCP +{{ end }} +{{ end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/injector-psp-role.yaml b/charts/hashicorp/vault/0.22.0/templates/injector-psp-role.yaml new file mode 100644 index 000000000..5d23c7556 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/injector-psp-role.yaml @@ -0,0 +1,20 @@ +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if eq (.Values.global.psp.enable | toString) "true" }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-psp + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: ['policy'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + - {{ template "vault.fullname" . }}-agent-injector +{{- end }} +{{- end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/injector-psp-rolebinding.yaml b/charts/hashicorp/vault/0.22.0/templates/injector-psp-rolebinding.yaml new file mode 100644 index 000000000..4f6b0a851 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/injector-psp-rolebinding.yaml @@ -0,0 +1,21 @@ +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if eq (.Values.global.psp.enable | toString) "true" }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-psp + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + kind: Role + name: {{ template "vault.fullname" . }}-agent-injector-psp + apiGroup: rbac.authorization.k8s.io +subjects: + - kind: ServiceAccount + name: {{ template "vault.fullname" . }}-agent-injector +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/hashicorp/vault/0.22.0/templates/injector-psp.yaml b/charts/hashicorp/vault/0.22.0/templates/injector-psp.yaml new file mode 100644 index 000000000..1eee2fcd0 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/injector-psp.yaml @@ -0,0 +1,46 @@ +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if eq (.Values.global.psp.enable | toString) "true" }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "vault.fullname" . }}-agent-injector + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- template "vault.psp.annotations" . }} +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + volumes: + - configMap + - emptyDir + - projected + - secret + - downwardAPI + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + # Require the container to run without root privileges. + rule: MustRunAsNonRoot + seLinux: + # This policy assumes the nodes are using AppArmor rather than SELinux. + rule: RunAsAny + supplementalGroups: + rule: MustRunAs + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + fsGroup: + rule: MustRunAs + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + readOnlyRootFilesystem: false +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/hashicorp/vault/0.22.0/templates/injector-role.yaml b/charts/hashicorp/vault/0.22.0/templates/injector-role.yaml new file mode 100644 index 000000000..08c8264cc --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/injector-role.yaml @@ -0,0 +1,29 @@ +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if and (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-leader-elector-role + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: + - apiGroups: [""] + resources: ["secrets", "configmaps"] + verbs: + - "create" + - "get" + - "watch" + - "list" + - "update" + - apiGroups: [""] + resources: ["pods"] + verbs: + - "get" + - "patch" + - "delete" +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/hashicorp/vault/0.22.0/templates/injector-rolebinding.yaml b/charts/hashicorp/vault/0.22.0/templates/injector-rolebinding.yaml new file mode 100644 index 000000000..ea0db11b9 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/injector-rolebinding.yaml @@ -0,0 +1,22 @@ +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +{{- if and (eq (.Values.injector.leaderElector.enabled | toString) "true") (gt (.Values.injector.replicas | int) 1) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-leader-elector-binding + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "vault.fullname" . }}-agent-injector-leader-elector-role +subjects: + - kind: ServiceAccount + name: {{ template "vault.fullname" . }}-agent-injector + namespace: {{ .Release.Namespace }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/hashicorp/vault/0.22.0/templates/injector-service.yaml b/charts/hashicorp/vault/0.22.0/templates/injector-service.yaml new file mode 100644 index 000000000..5e747d6f1 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/injector-service.yaml @@ -0,0 +1,22 @@ +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }}-agent-injector-svc + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{ template "injector.service.annotations" . }} +spec: + ports: + - name: https + port: 443 + targetPort: {{ .Values.injector.port }} + selector: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + component: webhook +{{- end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/injector-serviceaccount.yaml b/charts/hashicorp/vault/0.22.0/templates/injector-serviceaccount.yaml new file mode 100644 index 000000000..d1919b936 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/injector-serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- template "vault.injectorEnabled" . -}} +{{- if .injectorEnabled -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "vault.fullname" . }}-agent-injector + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }}-agent-injector + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{ template "injector.serviceAccount.annotations" . }} +{{ end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/prometheus-prometheusrules.yaml b/charts/hashicorp/vault/0.22.0/templates/prometheus-prometheusrules.yaml new file mode 100644 index 000000000..572f1a05a --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/prometheus-prometheusrules.yaml @@ -0,0 +1,26 @@ +{{ if and (.Values.serverTelemetry.prometheusRules.rules) + (or (.Values.global.serverTelemetry.prometheusOperator) (.Values.serverTelemetry.prometheusRules.enabled) ) +}} +--- +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ template "vault.fullname" . }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- /* update the selectors docs in values.yaml whenever the defaults below change. */ -}} + {{- $selectors := .Values.serverTelemetry.prometheusRules.selectors }} + {{- if $selectors }} + {{- toYaml $selectors | nindent 4 }} + {{- else }} + release: prometheus + {{- end }} +spec: + groups: + - name: {{ include "vault.fullname" . }} + rules: + {{- toYaml .Values.serverTelemetry.prometheusRules.rules | nindent 6 }} +{{- end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/prometheus-servicemonitor.yaml b/charts/hashicorp/vault/0.22.0/templates/prometheus-servicemonitor.yaml new file mode 100644 index 000000000..323e51fb9 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/prometheus-servicemonitor.yaml @@ -0,0 +1,44 @@ +{{ template "vault.mode" . }} +{{ if or (.Values.global.serverTelemetry.prometheusOperator) (.Values.serverTelemetry.serviceMonitor.enabled) }} +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ template "vault.fullname" . }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- /* update the selectors docs in values.yaml whenever the defaults below change. */ -}} + {{- $selectors := .Values.serverTelemetry.serviceMonitor.selectors }} + {{- if $selectors }} + {{- toYaml $selectors | nindent 4 }} + {{- else }} + release: prometheus + {{- end }} +spec: + selector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + {{- if eq .mode "ha" }} + vault-active: "true" + {{- else }} + vault-internal: "true" + {{- end }} + endpoints: + - port: {{ include "vault.scheme" . }} + interval: {{ .Values.serverTelemetry.serviceMonitor.interval }} + scrapeTimeout: {{ .Values.serverTelemetry.serviceMonitor.scrapeTimeout }} + scheme: {{ include "vault.scheme" . | lower }} + path: /v1/sys/metrics + params: + format: + - prometheus + tlsConfig: + insecureSkipVerify: true + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} +{{ end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/server-clusterrolebinding.yaml b/charts/hashicorp/vault/0.22.0/templates/server-clusterrolebinding.yaml new file mode 100644 index 000000000..8cdd61143 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/server-clusterrolebinding.yaml @@ -0,0 +1,24 @@ +{{ template "vault.serverAuthDelegator" . }} +{{- if .serverAuthDelegator -}} +{{- if .Capabilities.APIVersions.Has "rbac.authorization.k8s.io/v1" -}} +apiVersion: rbac.authorization.k8s.io/v1 +{{- else }} +apiVersion: rbac.authorization.k8s.io/v1beta1 +{{- end }} +kind: ClusterRoleBinding +metadata: + name: {{ template "vault.fullname" . }}-server-binding + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: +- kind: ServiceAccount + name: {{ template "vault.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{ end }} \ No newline at end of file diff --git a/charts/hashicorp/vault/0.22.0/templates/server-config-configmap.yaml b/charts/hashicorp/vault/0.22.0/templates/server-config-configmap.yaml new file mode 100644 index 000000000..f40c69608 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/server-config-configmap.yaml @@ -0,0 +1,40 @@ +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if .serverEnabled -}} +{{- if ne .mode "dev" -}} +{{ if or (.Values.server.standalone.config) (.Values.server.ha.config) -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "vault.fullname" . }}-config + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +data: + extraconfig-from-values.hcl: |- + {{- if or (eq .mode "ha") (eq .mode "standalone") }} + {{- $type := typeOf (index .Values.server .mode).config }} + {{- if eq $type "string" }} + disable_mlock = true + {{- if eq .mode "standalone" }} + {{ tpl .Values.server.standalone.config . | nindent 4 | trim }} + {{- else if and (eq .mode "ha") (eq (.Values.server.ha.raft.enabled | toString) "false") }} + {{ tpl .Values.server.ha.config . | nindent 4 | trim }} + {{- else if and (eq .mode "ha") (eq (.Values.server.ha.raft.enabled | toString) "true") }} + {{ tpl .Values.server.ha.raft.config . | nindent 4 | trim }} + {{ end }} + {{- else }} + {{- if and (eq .mode "ha") (eq (.Values.server.ha.raft.enabled | toString) "true") }} +{{ merge (dict "disable_mlock" true) (index .Values.server .mode).raft.config | toPrettyJson | indent 4 }} + {{- else }} +{{ merge (dict "disable_mlock" true) (index .Values.server .mode).config | toPrettyJson | indent 4 }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/server-discovery-role.yaml b/charts/hashicorp/vault/0.22.0/templates/server-discovery-role.yaml new file mode 100644 index 000000000..9ca23dd4c --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/server-discovery-role.yaml @@ -0,0 +1,21 @@ +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if .serverEnabled -}} +{{- if eq .mode "ha" }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + namespace: {{ .Release.Namespace }} + name: {{ template "vault.fullname" . }}-discovery-role + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: [""] + resources: ["pods"] + verbs: ["get", "watch", "list", "update", "patch"] +{{ end }} +{{ end }} +{{ end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/server-discovery-rolebinding.yaml b/charts/hashicorp/vault/0.22.0/templates/server-discovery-rolebinding.yaml new file mode 100644 index 000000000..6e22e4c2b --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/server-discovery-rolebinding.yaml @@ -0,0 +1,29 @@ +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if .serverEnabled -}} +{{- if eq .mode "ha" }} +{{- if .Capabilities.APIVersions.Has "rbac.authorization.k8s.io/v1" -}} +apiVersion: rbac.authorization.k8s.io/v1 +{{- else }} +apiVersion: rbac.authorization.k8s.io/v1beta1 +{{- end }} +kind: RoleBinding +metadata: + name: {{ template "vault.fullname" . }}-discovery-rolebinding + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "vault.fullname" . }}-discovery-role +subjects: +- kind: ServiceAccount + name: {{ template "vault.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{ end }} +{{ end }} +{{ end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/server-disruptionbudget.yaml b/charts/hashicorp/vault/0.22.0/templates/server-disruptionbudget.yaml new file mode 100644 index 000000000..d940fa4da --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/server-disruptionbudget.yaml @@ -0,0 +1,26 @@ +{{ template "vault.mode" . }} +{{- if ne .mode "external" -}} +{{- if .serverEnabled -}} +{{- if and (eq .mode "ha") (eq (.Values.server.ha.disruptionBudget.enabled | toString) "true") -}} +# PodDisruptionBudget to prevent degrading the server cluster through +# voluntary cluster changes. +apiVersion: {{ ge .Capabilities.KubeVersion.Minor "21" | ternary "policy/v1" "policy/v1beta1" }} +kind: PodDisruptionBudget +metadata: + name: {{ template "vault.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +spec: + maxUnavailable: {{ template "vault.pdb.maxUnavailable" . }} + selector: + matchLabels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/hashicorp/vault/0.22.0/templates/server-ha-active-service.yaml b/charts/hashicorp/vault/0.22.0/templates/server-ha-active-service.yaml new file mode 100644 index 000000000..ef212376d --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/server-ha-active-service.yaml @@ -0,0 +1,46 @@ +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- template "vault.serverServiceEnabled" . -}} +{{- if .serverServiceEnabled -}} +{{- if eq .mode "ha" }} +# Service for active Vault pod +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }}-active + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + vault-active: "true" + annotations: +{{ template "vault.service.annotations" .}} +spec: + {{- if .Values.server.service.type}} + type: {{ .Values.server.service.type }} + {{- end}} + {{- if .Values.server.service.clusterIP }} + clusterIP: {{ .Values.server.service.clusterIP }} + {{- end }} + {{- include "service.externalTrafficPolicy" .Values.server.service }} + publishNotReadyAddresses: {{ .Values.server.service.publishNotReadyAddresses }} + ports: + - name: {{ include "vault.scheme" . }} + port: {{ .Values.server.service.port }} + targetPort: {{ .Values.server.service.targetPort }} + {{- if and (.Values.server.service.activeNodePort) (eq (.Values.server.service.type | toString) "NodePort") }} + nodePort: {{ .Values.server.service.activeNodePort }} + {{- end }} + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server + vault-active: "true" +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/server-ha-standby-service.yaml b/charts/hashicorp/vault/0.22.0/templates/server-ha-standby-service.yaml new file mode 100644 index 000000000..e6d66af84 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/server-ha-standby-service.yaml @@ -0,0 +1,45 @@ +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- template "vault.serverServiceEnabled" . -}} +{{- if .serverServiceEnabled -}} +{{- if eq .mode "ha" }} +# Service for standby Vault pod +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }}-standby + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + annotations: +{{ template "vault.service.annotations" .}} +spec: + {{- if .Values.server.service.type}} + type: {{ .Values.server.service.type }} + {{- end}} + {{- if .Values.server.service.clusterIP }} + clusterIP: {{ .Values.server.service.clusterIP }} + {{- end }} + {{- include "service.externalTrafficPolicy" .Values.server.service }} + publishNotReadyAddresses: {{ .Values.server.service.publishNotReadyAddresses }} + ports: + - name: {{ include "vault.scheme" . }} + port: {{ .Values.server.service.port }} + targetPort: {{ .Values.server.service.targetPort }} + {{- if and (.Values.server.service.standbyNodePort) (eq (.Values.server.service.type | toString) "NodePort") }} + nodePort: {{ .Values.server.service.standbyNodePort }} + {{- end }} + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server + vault-active: "false" +{{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/hashicorp/vault/0.22.0/templates/server-headless-service.yaml b/charts/hashicorp/vault/0.22.0/templates/server-headless-service.yaml new file mode 100644 index 000000000..b03f491e3 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/server-headless-service.yaml @@ -0,0 +1,34 @@ +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- template "vault.serverServiceEnabled" . -}} +{{- if .serverServiceEnabled -}} +# Service for Vault cluster +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }}-internal + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + vault-internal: "true" + annotations: +{{ template "vault.service.annotations" .}} +spec: + clusterIP: None + publishNotReadyAddresses: {{ .Values.server.service.publishNotReadyAddresses }} + ports: + - name: "{{ include "vault.scheme" . }}" + port: {{ .Values.server.service.port }} + targetPort: {{ .Values.server.service.targetPort }} + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server +{{- end }} +{{- end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/server-ingress.yaml b/charts/hashicorp/vault/0.22.0/templates/server-ingress.yaml new file mode 100644 index 000000000..c81e5f5ce --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/server-ingress.yaml @@ -0,0 +1,77 @@ +{{- if not .Values.global.openshift }} +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if .Values.server.ingress.enabled -}} +{{- $extraPaths := .Values.server.ingress.extraPaths -}} +{{- $serviceName := include "vault.fullname" . -}} +{{- template "vault.serverServiceEnabled" . -}} +{{- if .serverServiceEnabled -}} +{{- if and (eq .mode "ha" ) (eq (.Values.server.ingress.activeService | toString) "true") }} +{{- $serviceName = printf "%s-%s" $serviceName "active" -}} +{{- end }} +{{- $servicePort := .Values.server.service.port -}} +{{- $pathType := .Values.server.ingress.pathType -}} +{{- $kubeVersion := .Capabilities.KubeVersion.Version }} +{{ if semverCompare ">= 1.19.0-0" $kubeVersion }} +apiVersion: networking.k8s.io/v1 +{{ else if .Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" }} +apiVersion: networking.k8s.io/v1beta1 +{{ else }} +apiVersion: extensions/v1beta1 +{{ end }} +kind: Ingress +metadata: + name: {{ template "vault.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- with .Values.server.ingress.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- template "vault.ingress.annotations" . }} +spec: +{{- if .Values.server.ingress.tls }} + tls: + {{- range .Values.server.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} +{{- if .Values.server.ingress.ingressClassName }} + ingressClassName: {{ .Values.server.ingress.ingressClassName }} +{{- end }} + rules: + {{- range .Values.server.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: +{{ if $extraPaths }} +{{ toYaml $extraPaths | indent 10 }} +{{- end }} + {{- range (.paths | default (list "/")) }} + - path: {{ . }} + {{ if semverCompare ">= 1.19.0-0" $kubeVersion }} + pathType: {{ $pathType }} + {{ end }} + backend: + {{ if semverCompare ">= 1.19.0-0" $kubeVersion }} + service: + name: {{ $serviceName }} + port: + number: {{ $servicePort }} + {{ else }} + serviceName: {{ $serviceName }} + servicePort: {{ $servicePort }} + {{ end }} + {{- end }} + {{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/hashicorp/vault/0.22.0/templates/server-network-policy.yaml b/charts/hashicorp/vault/0.22.0/templates/server-network-policy.yaml new file mode 100644 index 000000000..5f4c21a4b --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/server-network-policy.yaml @@ -0,0 +1,26 @@ +{{- if eq (.Values.server.networkPolicy.enabled | toString) "true" }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ template "vault.fullname" . }} + labels: + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + ingress: + - from: + - namespaceSelector: {} + ports: + - port: 8200 + protocol: TCP + - port: 8201 + protocol: TCP + {{- if .Values.server.networkPolicy.egress }} + egress: + {{- toYaml .Values.server.networkPolicy.egress | nindent 4 }} + {{ end }} +{{ end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/server-psp-role.yaml b/charts/hashicorp/vault/0.22.0/templates/server-psp-role.yaml new file mode 100644 index 000000000..b8eb897e5 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/server-psp-role.yaml @@ -0,0 +1,20 @@ +{{ template "vault.mode" . }} +{{- if .serverEnabled -}} +{{- if and (ne .mode "") (eq (.Values.global.psp.enable | toString) "true") }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "vault.fullname" . }}-psp + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +rules: +- apiGroups: ['policy'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + - {{ template "vault.fullname" . }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/server-psp-rolebinding.yaml b/charts/hashicorp/vault/0.22.0/templates/server-psp-rolebinding.yaml new file mode 100644 index 000000000..fded9fbc6 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/server-psp-rolebinding.yaml @@ -0,0 +1,21 @@ +{{ template "vault.mode" . }} +{{- if .serverEnabled -}} +{{- if and (ne .mode "") (eq (.Values.global.psp.enable | toString) "true") }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "vault.fullname" . }}-psp + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +roleRef: + kind: Role + name: {{ template "vault.fullname" . }}-psp + apiGroup: rbac.authorization.k8s.io +subjects: + - kind: ServiceAccount + name: {{ template "vault.fullname" . }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/server-psp.yaml b/charts/hashicorp/vault/0.22.0/templates/server-psp.yaml new file mode 100644 index 000000000..d210af351 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/server-psp.yaml @@ -0,0 +1,49 @@ +{{ template "vault.mode" . }} +{{- if .serverEnabled -}} +{{- if and (ne .mode "") (eq (.Values.global.psp.enable | toString) "true") }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "vault.fullname" . }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- template "vault.psp.annotations" . }} +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + volumes: + - configMap + - emptyDir + - projected + - secret + - downwardAPI + {{- if eq (.Values.server.dataStorage.enabled | toString) "true" }} + - persistentVolumeClaim + {{- end }} + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + # Require the container to run without root privileges. + rule: MustRunAsNonRoot + seLinux: + # This policy assumes the nodes are using AppArmor rather than SELinux. + rule: RunAsAny + supplementalGroups: + rule: MustRunAs + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + fsGroup: + rule: MustRunAs + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + readOnlyRootFilesystem: false +{{- end }} +{{- end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/server-route.yaml b/charts/hashicorp/vault/0.22.0/templates/server-route.yaml new file mode 100644 index 000000000..e122d936b --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/server-route.yaml @@ -0,0 +1,34 @@ +{{- if .Values.global.openshift }} +{{- if ne .mode "external" }} +{{- if .Values.server.route.enabled -}} +{{- $serviceName := include "vault.fullname" . -}} +{{- if and (eq .mode "ha" ) (eq (.Values.server.route.activeService | toString) "true") }} +{{- $serviceName = printf "%s-%s" $serviceName "active" -}} +{{- end }} +kind: Route +apiVersion: route.openshift.io/v1 +metadata: + name: {{ template "vault.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- with .Values.server.route.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- template "vault.route.annotations" . }} +spec: + host: {{ .Values.server.route.host }} + to: + kind: Service + name: {{ $serviceName }} + weight: 100 + port: + targetPort: 8200 + tls: + {{- toYaml .Values.server.route.tls | nindent 4 }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/server-service.yaml b/charts/hashicorp/vault/0.22.0/templates/server-service.yaml new file mode 100644 index 000000000..3a9b0e7e5 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/server-service.yaml @@ -0,0 +1,44 @@ +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- template "vault.serverServiceEnabled" . -}} +{{- if .serverServiceEnabled -}} +# Service for Vault cluster +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + annotations: +{{ template "vault.service.annotations" .}} +spec: + {{- if .Values.server.service.type}} + type: {{ .Values.server.service.type }} + {{- end}} + {{- if .Values.server.service.clusterIP }} + clusterIP: {{ .Values.server.service.clusterIP }} + {{- end }} + {{- include "service.externalTrafficPolicy" .Values.server.service }} + # We want the servers to become available even if they're not ready + # since this DNS is also used for join operations. + publishNotReadyAddresses: {{ .Values.server.service.publishNotReadyAddresses }} + ports: + - name: {{ include "vault.scheme" . }} + port: {{ .Values.server.service.port }} + targetPort: {{ .Values.server.service.targetPort }} + {{- if and (.Values.server.service.nodePort) (eq (.Values.server.service.type | toString) "NodePort") }} + nodePort: {{ .Values.server.service.nodePort }} + {{- end }} + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server +{{- end }} +{{- end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/server-serviceaccount.yaml b/charts/hashicorp/vault/0.22.0/templates/server-serviceaccount.yaml new file mode 100644 index 000000000..c0d32d190 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/server-serviceaccount.yaml @@ -0,0 +1,14 @@ +{{ template "vault.serverServiceAccountEnabled" . }} +{{- if .serverServiceAccountEnabled -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "vault.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{ template "vault.serviceAccount.annotations" . }} +{{ end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/server-statefulset.yaml b/charts/hashicorp/vault/0.22.0/templates/server-statefulset.yaml new file mode 100644 index 000000000..afc48d695 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/server-statefulset.yaml @@ -0,0 +1,206 @@ +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if ne .mode "" }} +{{- if .serverEnabled -}} +# StatefulSet to run the actual vault server cluster. +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ template "vault.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- template "vault.statefulSet.annotations" . }} +spec: + serviceName: {{ template "vault.fullname" . }}-internal + podManagementPolicy: Parallel + replicas: {{ template "vault.replicas" . }} + updateStrategy: + type: {{ .Values.server.updateStrategyType }} + selector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server + template: + metadata: + labels: + helm.sh/chart: {{ template "vault.chart" . }} + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server + {{- if .Values.server.extraLabels -}} + {{- toYaml .Values.server.extraLabels | nindent 8 -}} + {{- end -}} + {{ template "vault.annotations" . }} + spec: + {{ template "vault.affinity" . }} + {{ template "vault.topologySpreadConstraints" . }} + {{ template "vault.tolerations" . }} + {{ template "vault.nodeselector" . }} + {{- if .Values.server.priorityClassName }} + priorityClassName: {{ .Values.server.priorityClassName }} + {{- end }} + terminationGracePeriodSeconds: {{ .Values.server.terminationGracePeriodSeconds }} + serviceAccountName: {{ template "vault.serviceAccount.name" . }} + {{ if .Values.server.shareProcessNamespace }} + shareProcessNamespace: true + {{ end }} + {{- template "server.statefulSet.securityContext.pod" . }} + volumes: + {{ template "vault.volumes" . }} + - name: home + emptyDir: {} + {{- if .Values.server.extraInitContainers }} + initContainers: + {{ toYaml .Values.server.extraInitContainers | nindent 8}} + {{- end }} + containers: + - name: vault + {{ template "vault.resources" . }} + image: {{ .Values.server.image.repository }}:{{ .Values.server.image.tag | default "latest" }} + imagePullPolicy: {{ .Values.server.image.pullPolicy }} + command: + - "/bin/sh" + - "-ec" + args: {{ template "vault.args" . }} + {{- template "server.statefulSet.securityContext.container" . }} + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: VAULT_K8S_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: VAULT_K8S_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: VAULT_ADDR + value: "{{ include "vault.scheme" . }}://127.0.0.1:8200" + - name: VAULT_API_ADDR + {{- if .Values.server.ha.apiAddr }} + value: {{ .Values.server.ha.apiAddr }} + {{- else }} + value: "{{ include "vault.scheme" . }}://$(POD_IP):8200" + {{- end }} + - name: SKIP_CHOWN + value: "true" + - name: SKIP_SETCAP + value: "true" + - name: HOSTNAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: VAULT_CLUSTER_ADDR + {{- if .Values.server.ha.clusterAddr }} + value: {{ .Values.server.ha.clusterAddr }} + {{- else }} + value: "https://$(HOSTNAME).{{ template "vault.fullname" . }}-internal:8201" + {{- end }} + {{- if and (eq (.Values.server.ha.raft.enabled | toString) "true") (eq (.Values.server.ha.raft.setNodeId | toString) "true") }} + - name: VAULT_RAFT_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- end }} + - name: HOME + value: "/home/vault" + {{- if .Values.server.logLevel }} + - name: VAULT_LOG_LEVEL + value: "{{ .Values.server.logLevel }}" + {{- end }} + {{- if .Values.server.logFormat }} + - name: VAULT_LOG_FORMAT + value: "{{ .Values.server.logFormat }}" + {{- end }} + {{- if (and .Values.server.enterpriseLicense.secretName .Values.server.enterpriseLicense.secretKey) }} + - name: VAULT_LICENSE_PATH + value: /vault/license/{{ .Values.server.enterpriseLicense.secretKey }} + {{- end }} + {{ template "vault.envs" . }} + {{- include "vault.extraEnvironmentVars" .Values.server | nindent 12 }} + {{- include "vault.extraSecretEnvironmentVars" .Values.server | nindent 12 }} + volumeMounts: + {{ template "vault.mounts" . }} + - name: home + mountPath: /home/vault + ports: + - containerPort: 8200 + name: {{ include "vault.scheme" . }} + - containerPort: 8201 + name: https-internal + - containerPort: 8202 + name: {{ include "vault.scheme" . }}-rep + {{- if .Values.server.readinessProbe.enabled }} + readinessProbe: + {{- if .Values.server.readinessProbe.path }} + httpGet: + path: {{ .Values.server.readinessProbe.path | quote }} + port: 8200 + scheme: {{ include "vault.scheme" . | upper }} + {{- else }} + # Check status; unsealed vault servers return 0 + # The exit code reflects the seal status: + # 0 - unsealed + # 1 - error + # 2 - sealed + exec: + command: ["/bin/sh", "-ec", "vault status -tls-skip-verify"] + {{- end }} + failureThreshold: {{ .Values.server.readinessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.server.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.server.readinessProbe.periodSeconds }} + successThreshold: {{ .Values.server.readinessProbe.successThreshold }} + timeoutSeconds: {{ .Values.server.readinessProbe.timeoutSeconds }} + {{- end }} + {{- if .Values.server.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: {{ .Values.server.livenessProbe.path | quote }} + port: 8200 + scheme: {{ include "vault.scheme" . | upper }} + failureThreshold: {{ .Values.server.livenessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.server.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.server.livenessProbe.periodSeconds }} + successThreshold: {{ .Values.server.livenessProbe.successThreshold }} + timeoutSeconds: {{ .Values.server.livenessProbe.timeoutSeconds }} + {{- end }} + lifecycle: + # Vault container doesn't receive SIGTERM from Kubernetes + # and after the grace period ends, Kube sends SIGKILL. This + # causes issues with graceful shutdowns such as deregistering itself + # from Consul (zombie services). + preStop: + exec: + command: [ + "/bin/sh", "-c", + # Adding a sleep here to give the pod eviction a + # chance to propagate, so requests will not be made + # to this pod while it's terminating + "sleep {{ .Values.server.preStopSleepSeconds }} && kill -SIGTERM $(pidof vault)", + ] + {{- if .Values.server.postStart }} + postStart: + exec: + command: + {{- range (.Values.server.postStart) }} + - {{ . | quote }} + {{- end }} + {{- end }} + {{- if .Values.server.extraContainers }} + {{ toYaml .Values.server.extraContainers | nindent 8}} + {{- end }} + {{- include "imagePullSecrets" . | nindent 6 }} + {{ template "vault.volumeclaims" . }} +{{ end }} +{{ end }} +{{ end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/tests/server-test.yaml b/charts/hashicorp/vault/0.22.0/templates/tests/server-test.yaml new file mode 100644 index 000000000..56dbee78c --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/tests/server-test.yaml @@ -0,0 +1,51 @@ +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- if .serverEnabled -}} +apiVersion: v1 +kind: Pod +metadata: + name: "{{ .Release.Name }}-server-test" + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": test +spec: + {{- include "imagePullSecrets" . | nindent 2 }} + containers: + - name: {{ .Release.Name }}-server-test + image: {{ .Values.server.image.repository }}:{{ .Values.server.image.tag | default "latest" }} + imagePullPolicy: {{ .Values.server.image.pullPolicy }} + env: + - name: VAULT_ADDR + value: {{ include "vault.scheme" . }}://{{ template "vault.fullname" . }}.{{ .Release.Namespace }}.svc:{{ .Values.server.service.port }} + {{- include "vault.extraEnvironmentVars" .Values.server | nindent 8 }} + command: + - /bin/sh + - -c + - | + echo "Checking for sealed info in 'vault status' output" + ATTEMPTS=10 + n=0 + until [ "$n" -ge $ATTEMPTS ] + do + echo "Attempt" $n... + vault status -format yaml | grep -E '^sealed: (true|false)' && break + n=$((n+1)) + sleep 5 + done + if [ $n -ge $ATTEMPTS ]; then + echo "timed out looking for sealed info in 'vault status' output" + exit 1 + fi + + exit 0 + volumeMounts: + {{- if .Values.server.volumeMounts }} + {{- toYaml .Values.server.volumeMounts | nindent 8}} + {{- end }} + volumes: + {{- if .Values.server.volumes }} + {{- toYaml .Values.server.volumes | nindent 4}} + {{- end }} + restartPolicy: Never +{{- end }} +{{- end }} diff --git a/charts/hashicorp/vault/0.22.0/templates/ui-service.yaml b/charts/hashicorp/vault/0.22.0/templates/ui-service.yaml new file mode 100644 index 000000000..d45afdda4 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/templates/ui-service.yaml @@ -0,0 +1,37 @@ +{{ template "vault.mode" . }} +{{- if ne .mode "external" }} +{{- template "vault.uiEnabled" . -}} +{{- if .uiEnabled -}} + +apiVersion: v1 +kind: Service +metadata: + name: {{ template "vault.fullname" . }}-ui + namespace: {{ .Release.Namespace }} + labels: + helm.sh/chart: {{ include "vault.chart" . }} + app.kubernetes.io/name: {{ include "vault.name" . }}-ui + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- template "vault.ui.annotations" . }} +spec: + selector: + app.kubernetes.io/name: {{ include "vault.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + component: server + {{- if and (.Values.ui.activeVaultPodOnly) (eq .mode "ha") }} + vault-active: "true" + {{- end }} + publishNotReadyAddresses: {{ .Values.ui.publishNotReadyAddresses }} + ports: + - name: {{ include "vault.scheme" . }} + port: {{ .Values.ui.externalPort }} + targetPort: {{ .Values.ui.targetPort }} + {{- if .Values.ui.serviceNodePort }} + nodePort: {{ .Values.ui.serviceNodePort }} + {{- end }} + type: {{ .Values.ui.serviceType }} + {{- include "service.externalTrafficPolicy" .Values.ui }} + {{- include "service.loadBalancer" .Values.ui }} +{{- end -}} +{{- end }} diff --git a/charts/hashicorp/vault/0.22.0/values.openshift.yaml b/charts/hashicorp/vault/0.22.0/values.openshift.yaml new file mode 100644 index 000000000..c932a6897 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/values.openshift.yaml @@ -0,0 +1,18 @@ +# These overrides are appropriate defaults for deploying this chart on OpenShift + +global: + openshift: true + +injector: + image: + repository: "registry.connect.redhat.com/hashicorp/vault-k8s" + tag: "1.0.0-ubi" + + agentImage: + repository: "registry.connect.redhat.com/hashicorp/vault" + tag: "1.11.3-ubi" + +server: + image: + repository: "registry.connect.redhat.com/hashicorp/vault" + tag: "1.11.3-ubi" diff --git a/charts/hashicorp/vault/0.22.0/values.schema.json b/charts/hashicorp/vault/0.22.0/values.schema.json new file mode 100644 index 000000000..aad7ee7fc --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/values.schema.json @@ -0,0 +1,1027 @@ +{ + "$schema": "http://json-schema.org/schema#", + "type": "object", + "properties": { + "csi": { + "type": "object", + "properties": { + "daemonSet": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "extraLabels": { + "type": "object" + }, + "kubeletRootDir": { + "type": "string" + }, + "providersDir": { + "type": "string" + }, + "securityContext": { + "type": "object", + "properties": { + "container": { + "type": [ + "object", + "string" + ] + }, + "pod": { + "type": [ + "object", + "string" + ] + } + } + }, + "updateStrategy": { + "type": "object", + "properties": { + "maxUnavailable": { + "type": "string" + }, + "type": { + "type": "string" + } + } + } + } + }, + "debug": { + "type": "boolean" + }, + "enabled": { + "type": [ + "boolean", + "string" + ] + }, + "extraArgs": { + "type": "array" + }, + "image": { + "type": "object", + "properties": { + "pullPolicy": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "livenessProbe": { + "type": "object", + "properties": { + "failureThreshold": { + "type": "integer" + }, + "initialDelaySeconds": { + "type": "integer" + }, + "periodSeconds": { + "type": "integer" + }, + "successThreshold": { + "type": "integer" + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "pod": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "extraLabels": { + "type": "object" + }, + "tolerations": { + "type": [ + "null", + "array", + "string" + ] + } + } + }, + "priorityClassName": { + "type": "string" + }, + "readinessProbe": { + "type": "object", + "properties": { + "failureThreshold": { + "type": "integer" + }, + "initialDelaySeconds": { + "type": "integer" + }, + "periodSeconds": { + "type": "integer" + }, + "successThreshold": { + "type": "integer" + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "resources": { + "type": "object" + }, + "serviceAccount": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "extraLabels": { + "type": "object" + } + } + }, + "volumeMounts": { + "type": [ + "null", + "array" + ] + }, + "volumes": { + "type": [ + "null", + "array" + ] + } + } + }, + "global": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "externalVaultAddr": { + "type": "string" + }, + "imagePullSecrets": { + "type": "array" + }, + "openshift": { + "type": "boolean" + }, + "psp": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enable": { + "type": "boolean" + } + } + }, + "tlsDisable": { + "type": "boolean" + } + } + }, + "injector": { + "type": "object", + "properties": { + "affinity": { + "type": [ + "object", + "string" + ] + }, + "agentDefaults": { + "type": "object", + "properties": { + "cpuLimit": { + "type": "string" + }, + "cpuRequest": { + "type": "string" + }, + "memLimit": { + "type": "string" + }, + "memRequest": { + "type": "string" + }, + "template": { + "type": "string" + }, + "templateConfig": { + "type": "object", + "properties": { + "exitOnRetryFailure": { + "type": "boolean" + }, + "staticSecretRenderInterval": { + "type": "string" + } + } + } + } + }, + "agentImage": { + "type": "object", + "properties": { + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "authPath": { + "type": "string" + }, + "certs": { + "type": "object", + "properties": { + "caBundle": { + "type": "string" + }, + "certName": { + "type": "string" + }, + "keyName": { + "type": "string" + }, + "secretName": { + "type": [ + "null", + "string" + ] + } + } + }, + "enabled": { + "type": [ + "boolean", + "string" + ] + }, + "externalVaultAddr": { + "type": "string" + }, + "extraEnvironmentVars": { + "type": "object" + }, + "extraLabels": { + "type": "object" + }, + "failurePolicy": { + "type": "string" + }, + "hostNetwork": { + "type": "boolean" + }, + "image": { + "type": "object", + "properties": { + "pullPolicy": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "leaderElector": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "logFormat": { + "type": "string" + }, + "logLevel": { + "type": "string" + }, + "metrics": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "namespaceSelector": { + "type": "object" + }, + "nodeSelector": { + "type": [ + "null", + "object", + "string" + ] + }, + "objectSelector": { + "type": [ + "object", + "string" + ] + }, + "podDisruptionBudget": { + "type": "object" + }, + "port": { + "type": "integer" + }, + "priorityClassName": { + "type": "string" + }, + "replicas": { + "type": "integer" + }, + "resources": { + "type": "object" + }, + "revokeOnShutdown": { + "type": "boolean" + }, + "securityContext": { + "type": "object", + "properties": { + "container": { + "type": [ + "object", + "string" + ] + }, + "pod": { + "type": [ + "object", + "string" + ] + } + } + }, + "service": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + } + } + }, + "serviceAccount": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + } + } + }, + "strategy": { + "type": [ + "object", + "string" + ] + }, + "tolerations": { + "type": [ + "null", + "array", + "string" + ] + }, + "topologySpreadConstraints": { + "type": [ + "null", + "array", + "string" + ] + }, + "webhook": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "failurePolicy": { + "type": "string" + }, + "matchPolicy": { + "type": "string" + }, + "namespaceSelector": { + "type": "object" + }, + "objectSelector": { + "type": [ + "object", + "string" + ] + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "webhookAnnotations": { + "type": [ + "object", + "string" + ] + } + } + }, + "server": { + "type": "object", + "properties": { + "affinity": { + "type": [ + "object", + "string" + ] + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "auditStorage": { + "type": "object", + "properties": { + "accessMode": { + "type": "string" + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": [ + "boolean", + "string" + ] + }, + "mountPath": { + "type": "string" + }, + "size": { + "type": "string" + }, + "storageClass": { + "type": [ + "null", + "string" + ] + } + } + }, + "authDelegator": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "dataStorage": { + "type": "object", + "properties": { + "accessMode": { + "type": "string" + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": [ + "boolean", + "string" + ] + }, + "mountPath": { + "type": "string" + }, + "size": { + "type": "string" + }, + "storageClass": { + "type": [ + "null", + "string" + ] + } + } + }, + "dev": { + "type": "object", + "properties": { + "devRootToken": { + "type": "string" + }, + "enabled": { + "type": "boolean" + } + } + }, + "enabled": { + "type": [ + "boolean", + "string" + ] + }, + "enterpriseLicense": { + "type": "object", + "properties": { + "secretKey": { + "type": "string" + }, + "secretName": { + "type": "string" + } + } + }, + "extraArgs": { + "type": "string" + }, + "extraContainers": { + "type": [ + "null", + "array" + ] + }, + "extraEnvironmentVars": { + "type": "object" + }, + "extraInitContainers": { + "type": [ + "null", + "array" + ] + }, + "extraLabels": { + "type": "object" + }, + "extraSecretEnvironmentVars": { + "type": "array" + }, + "extraVolumes": { + "type": "array" + }, + "ha": { + "type": "object", + "properties": { + "apiAddr": { + "type": [ + "null", + "string" + ] + }, + "clusterAddr": { + "type": [ + "null", + "string" + ] + }, + "config": { + "type": [ + "string", + "object" + ] + }, + "disruptionBudget": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "maxUnavailable": { + "type": [ + "null", + "integer" + ] + } + } + }, + "enabled": { + "type": "boolean" + }, + "raft": { + "type": "object", + "properties": { + "config": { + "type": [ + "string", + "object" + ] + }, + "enabled": { + "type": "boolean" + }, + "setNodeId": { + "type": "boolean" + } + } + }, + "replicas": { + "type": "integer" + } + } + }, + "image": { + "type": "object", + "properties": { + "pullPolicy": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "ingress": { + "type": "object", + "properties": { + "activeService": { + "type": "boolean" + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": "boolean" + }, + "extraPaths": { + "type": "array" + }, + "hosts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "host": { + "type": "string" + }, + "paths": { + "type": "array" + } + } + } + }, + "ingressClassName": { + "type": "string" + }, + "labels": { + "type": "object" + }, + "pathType": { + "type": "string" + }, + "tls": { + "type": "array" + } + } + }, + "livenessProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "failureThreshold": { + "type": "integer" + }, + "initialDelaySeconds": { + "type": "integer" + }, + "path": { + "type": "string" + }, + "periodSeconds": { + "type": "integer" + }, + "successThreshold": { + "type": "integer" + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "logFormat": { + "type": "string" + }, + "logLevel": { + "type": "string" + }, + "networkPolicy": { + "type": "object", + "properties": { + "egress": { + "type": "array" + }, + "enabled": { + "type": "boolean" + } + } + }, + "nodeSelector": { + "type": [ + "null", + "object", + "string" + ] + }, + "postStart": { + "type": "array" + }, + "preStopSleepSeconds": { + "type": "integer" + }, + "priorityClassName": { + "type": "string" + }, + "readinessProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "failureThreshold": { + "type": "integer" + }, + "initialDelaySeconds": { + "type": "integer" + }, + "periodSeconds": { + "type": "integer" + }, + "successThreshold": { + "type": "integer" + }, + "timeoutSeconds": { + "type": "integer" + } + } + }, + "resources": { + "type": "object" + }, + "route": { + "type": "object", + "properties": { + "activeService": { + "type": "boolean" + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": "boolean" + }, + "host": { + "type": "string" + }, + "labels": { + "type": "object" + }, + "tls": { + "type": "object" + } + } + }, + "service": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": "boolean" + }, + "externalTrafficPolicy": { + "type": "string" + }, + "port": { + "type": "integer" + }, + "publishNotReadyAddresses": { + "type": "boolean" + }, + "targetPort": { + "type": "integer" + }, + "nodePort": { + "type": "integer" + }, + "activeNodePort": { + "type": "integer" + }, + "standbyNodePort": { + "type": "integer" + } + } + }, + "serviceAccount": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "create": { + "type": "boolean" + }, + "name": { + "type": "string" + } + } + }, + "shareProcessNamespace": { + "type": "boolean" + }, + "standalone": { + "type": "object", + "properties": { + "config": { + "type": [ + "string", + "object" + ] + }, + "enabled": { + "type": [ + "string", + "boolean" + ] + } + } + }, + "statefulSet": { + "type": "object", + "properties": { + "annotations": { + "type": [ + "object", + "string" + ] + }, + "securityContext": { + "type": "object", + "properties": { + "container": { + "type": [ + "object", + "string" + ] + }, + "pod": { + "type": [ + "object", + "string" + ] + } + } + } + } + }, + "terminationGracePeriodSeconds": { + "type": "integer" + }, + "tolerations": { + "type": [ + "null", + "array", + "string" + ] + }, + "topologySpreadConstraints": { + "type": [ + "null", + "array", + "string" + ] + }, + "updateStrategyType": { + "type": "string" + }, + "volumeMounts": { + "type": [ + "null", + "array" + ] + }, + "volumes": { + "type": [ + "null", + "array" + ] + } + } + }, + "ui": { + "type": "object", + "properties": { + "activeVaultPodOnly": { + "type": "boolean" + }, + "annotations": { + "type": [ + "object", + "string" + ] + }, + "enabled": { + "type": [ + "boolean", + "string" + ] + }, + "externalPort": { + "type": "integer" + }, + "externalTrafficPolicy": { + "type": "string" + }, + "publishNotReadyAddresses": { + "type": "boolean" + }, + "serviceNodePort": { + "type": [ + "null", + "integer" + ] + }, + "serviceType": { + "type": "string" + }, + "targetPort": { + "type": "integer" + } + } + } + } +} diff --git a/charts/hashicorp/vault/0.22.0/values.yaml b/charts/hashicorp/vault/0.22.0/values.yaml new file mode 100644 index 000000000..c4ac1ea19 --- /dev/null +++ b/charts/hashicorp/vault/0.22.0/values.yaml @@ -0,0 +1,1119 @@ +# Available parameters and their default values for the Vault chart. + +global: + # enabled is the master enabled switch. Setting this to true or false + # will enable or disable all the components within this chart by default. + enabled: true + + # Image pull secret to use for registry authentication. + # Alternatively, the value may be specified as an array of strings. + imagePullSecrets: [] + # imagePullSecrets: + # - name: image-pull-secret + + # TLS for end-to-end encrypted transport + tlsDisable: true + + # External vault server address for the injector and CSI provider to use. + # Setting this will disable deployment of a vault server. + externalVaultAddr: "" + + # If deploying to OpenShift + openshift: false + + # Create PodSecurityPolicy for pods + psp: + enable: false + # Annotation for PodSecurityPolicy. + # This is a multi-line templated string map, and can also be set as YAML. + annotations: | + seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default,runtime/default + apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default + seccomp.security.alpha.kubernetes.io/defaultProfileName: runtime/default + apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default + + serverTelemetry: + # Enable integration with the Prometheus Operator + # See the top level serverTelemetry section below before enabling this feature. + prometheusOperator: false + +injector: + # True if you want to enable vault agent injection. + # @default: global.enabled + enabled: "-" + + replicas: 1 + + # Configures the port the injector should listen on + port: 8080 + + # If multiple replicas are specified, by default a leader will be determined + # so that only one injector attempts to create TLS certificates. + leaderElector: + enabled: true + + # If true, will enable a node exporter metrics endpoint at /metrics. + metrics: + enabled: false + + # Deprecated: Please use global.externalVaultAddr instead. + externalVaultAddr: "" + + # image sets the repo and tag of the vault-k8s image to use for the injector. + image: + repository: "hashicorp/vault-k8s" + tag: "1.0.0" + pullPolicy: IfNotPresent + + # agentImage sets the repo and tag of the Vault image to use for the Vault Agent + # containers. This should be set to the official Vault image. Vault 1.3.1+ is + # required. + agentImage: + repository: "hashicorp/vault" + tag: "1.11.3" + + # The default values for the injected Vault Agent containers. + agentDefaults: + # For more information on configuring resources, see the K8s documentation: + # https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + cpuLimit: "500m" + cpuRequest: "250m" + memLimit: "128Mi" + memRequest: "64Mi" + + # Default template type for secrets when no custom template is specified. + # Possible values include: "json" and "map". + template: "map" + + # Default values within Agent's template_config stanza. + templateConfig: + exitOnRetryFailure: true + staticSecretRenderInterval: "" + + # Mount Path of the Vault Kubernetes Auth Method. + authPath: "auth/kubernetes" + + # Configures the log verbosity of the injector. + # Supported log levels include: trace, debug, info, warn, error + logLevel: "info" + + # Configures the log format of the injector. Supported log formats: "standard", "json". + logFormat: "standard" + + # Configures all Vault Agent sidecars to revoke their token when shutting down + revokeOnShutdown: false + + webhook: + # Configures failurePolicy of the webhook. The "unspecified" default behaviour depends on the + # API Version of the WebHook. + # To block pod creation while the webhook is unavailable, set the policy to `Fail` below. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy + # + failurePolicy: Ignore + + # matchPolicy specifies the approach to accepting changes based on the rules of + # the MutatingWebhookConfiguration. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy + # for more details. + # + matchPolicy: Exact + + # timeoutSeconds is the amount of seconds before the webhook request will be ignored + # or fails. + # If it is ignored or fails depends on the failurePolicy + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#timeouts + # for more details. + # + timeoutSeconds: 30 + + # namespaceSelector is the selector for restricting the webhook to only + # specific namespaces. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector + # for more details. + # Example: + # namespaceSelector: + # matchLabels: + # sidecar-injector: enabled + namespaceSelector: {} + + # objectSelector is the selector for restricting the webhook to only + # specific labels. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector + # for more details. + # Example: + # objectSelector: + # matchLabels: + # vault-sidecar-injector: enabled + objectSelector: | + matchExpressions: + - key: app.kubernetes.io/name + operator: NotIn + values: + - {{ template "vault.name" . }}-agent-injector + + # Extra annotations to attach to the webhook + annotations: {} + + # Deprecated: please use 'webhook.failurePolicy' instead + # Configures failurePolicy of the webhook. The "unspecified" default behaviour depends on the + # API Version of the WebHook. + # To block pod creation while webhook is unavailable, set the policy to `Fail` below. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy + # + failurePolicy: Ignore + + # Deprecated: please use 'webhook.namespaceSelector' instead + # namespaceSelector is the selector for restricting the webhook to only + # specific namespaces. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector + # for more details. + # Example: + # namespaceSelector: + # matchLabels: + # sidecar-injector: enabled + namespaceSelector: {} + + # Deprecated: please use 'webhook.objectSelector' instead + # objectSelector is the selector for restricting the webhook to only + # specific labels. + # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector + # for more details. + # Example: + # objectSelector: + # matchLabels: + # vault-sidecar-injector: enabled + objectSelector: {} + + # Deprecated: please use 'webhook.annotations' instead + # Extra annotations to attach to the webhook + webhookAnnotations: {} + + certs: + # secretName is the name of the secret that has the TLS certificate and + # private key to serve the injector webhook. If this is null, then the + # injector will default to its automatic management mode that will assign + # a service account to the injector to generate its own certificates. + secretName: null + + # caBundle is a base64-encoded PEM-encoded certificate bundle for the CA + # that signed the TLS certificate that the webhook serves. This must be set + # if secretName is non-null unless an external service like cert-manager is + # keeping the caBundle updated. + caBundle: "" + + # certName and keyName are the names of the files within the secret for + # the TLS cert and private key, respectively. These have reasonable + # defaults but can be customized if necessary. + certName: tls.crt + keyName: tls.key + + # Security context for the pod template and the injector container + # The default pod securityContext is: + # runAsNonRoot: true + # runAsGroup: {{ .Values.injector.gid | default 1000 }} + # runAsUser: {{ .Values.injector.uid | default 100 }} + # fsGroup: {{ .Values.injector.gid | default 1000 }} + # and for container is + # allowPrivilegeEscalation: false + # capabilities: + # drop: + # - ALL + securityContext: + pod: {} + container: {} + + resources: {} + # resources: + # requests: + # memory: 256Mi + # cpu: 250m + # limits: + # memory: 256Mi + # cpu: 250m + + # extraEnvironmentVars is a list of extra environment variables to set in the + # injector deployment. + extraEnvironmentVars: {} + # KUBERNETES_SERVICE_HOST: kubernetes.default.svc + + # Affinity Settings for injector pods + # This can either be a multi-line string or YAML matching the PodSpec's affinity field. + # Commenting out or setting as empty the affinity variable, will allow + # deployment of multiple replicas to single node services such as Minikube. + affinity: | + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }}-agent-injector + app.kubernetes.io/instance: "{{ .Release.Name }}" + component: webhook + topologyKey: kubernetes.io/hostname + + # Topology settings for injector pods + # ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ + # This should be either a multi-line string or YAML matching the topologySpreadConstraints array + # in a PodSpec. + topologySpreadConstraints: [] + + # Toleration Settings for injector pods + # This should be either a multi-line string or YAML matching the Toleration array + # in a PodSpec. + tolerations: [] + + # nodeSelector labels for server pod assignment, formatted as a multi-line string or YAML map. + # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + # Example: + # nodeSelector: + # beta.kubernetes.io/arch: amd64 + nodeSelector: {} + + # Priority class for injector pods + priorityClassName: "" + + # Extra annotations to attach to the injector pods + # This can either be YAML or a YAML-formatted multi-line templated string map + # of the annotations to apply to the injector pods + annotations: {} + + # Extra labels to attach to the agent-injector + # This should be a YAML map of the labels to apply to the injector + extraLabels: {} + + # Should the injector pods run on the host network (useful when using + # an alternate CNI in EKS) + hostNetwork: false + + # Injector service specific config + service: + # Extra annotations to attach to the injector service + annotations: {} + + # Injector serviceAccount specific config + serviceAccount: + # Extra annotations to attach to the injector serviceAccount + annotations: {} + + # A disruption budget limits the number of pods of a replicated application + # that are down simultaneously from voluntary disruptions + podDisruptionBudget: {} + # podDisruptionBudget: + # maxUnavailable: 1 + + # strategy for updating the deployment. This can be a multi-line string or a + # YAML map. + strategy: {} + # strategy: | + # rollingUpdate: + # maxSurge: 25% + # maxUnavailable: 25% + # type: RollingUpdate + +server: + # If true, or "-" with global.enabled true, Vault server will be installed. + # See vault.mode in _helpers.tpl for implementation details. + enabled: "-" + + # [Enterprise Only] This value refers to a Kubernetes secret that you have + # created that contains your enterprise license. If you are not using an + # enterprise image or if you plan to introduce the license key via another + # route, then leave secretName blank ("") or set it to null. + # Requires Vault Enterprise 1.8 or later. + enterpriseLicense: + # The name of the Kubernetes secret that holds the enterprise license. The + # secret must be in the same namespace that Vault is installed into. + secretName: "" + # The key within the Kubernetes secret that holds the enterprise license. + secretKey: "license" + + # Resource requests, limits, etc. for the server cluster placement. This + # should map directly to the value of the resources field for a PodSpec. + # By default no direct resource request is made. + + image: + repository: "hashicorp/vault" + tag: "1.11.3" + # Overrides the default Image Pull Policy + pullPolicy: IfNotPresent + + # Configure the Update Strategy Type for the StatefulSet + # See https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies + updateStrategyType: "OnDelete" + + # Configure the logging verbosity for the Vault server. + # Supported log levels include: trace, debug, info, warn, error + logLevel: "" + + # Configure the logging format for the Vault server. + # Supported log formats include: standard, json + logFormat: "" + + resources: {} + # resources: + # requests: + # memory: 256Mi + # cpu: 250m + # limits: + # memory: 256Mi + # cpu: 250m + + # Ingress allows ingress services to be created to allow external access + # from Kubernetes to access Vault pods. + # If deployment is on OpenShift, the following block is ignored. + # In order to expose the service, use the route section below + ingress: + enabled: false + labels: {} + # traffic: external + annotations: {} + # | + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + # or + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + + # Optionally use ingressClassName instead of deprecated annotation. + # See: https://kubernetes.io/docs/concepts/services-networking/ingress/#deprecated-annotation + ingressClassName: "" + + # As of Kubernetes 1.19, all Ingress Paths must have a pathType configured. The default value below should be sufficient in most cases. + # See: https://kubernetes.io/docs/concepts/services-networking/ingress/#path-types for other possible values. + pathType: Prefix + + # When HA mode is enabled and K8s service registration is being used, + # configure the ingress to point to the Vault active service. + activeService: true + hosts: + - host: chart-example.local + paths: [] + ## Extra paths to prepend to the host configuration. This is useful when working with annotation based services. + extraPaths: [] + # - path: /* + # backend: + # service: + # name: ssl-redirect + # port: + # number: use-annotation + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + + # OpenShift only - create a route to expose the service + # By default the created route will be of type passthrough + route: + enabled: false + + # When HA mode is enabled and K8s service registration is being used, + # configure the route to point to the Vault active service. + activeService: true + + labels: {} + annotations: {} + host: chart-example.local + # tls will be passed directly to the route's TLS config, which + # can be used to configure other termination methods that terminate + # TLS at the router + tls: + termination: passthrough + + # authDelegator enables a cluster role binding to be attached to the service + # account. This cluster role binding can be used to setup Kubernetes auth + # method. https://www.vaultproject.io/docs/auth/kubernetes.html + authDelegator: + enabled: true + + # extraInitContainers is a list of init containers. Specified as a YAML list. + # This is useful if you need to run a script to provision TLS certificates or + # write out configuration files in a dynamic way. + extraInitContainers: null + # # This example installs a plugin pulled from github into the /usr/local/libexec/vault/oauthapp folder, + # # which is defined in the volumes value. + # - name: oauthapp + # image: "alpine" + # command: [sh, -c] + # args: + # - cd /tmp && + # wget https://github.com/puppetlabs/vault-plugin-secrets-oauthapp/releases/download/v1.2.0/vault-plugin-secrets-oauthapp-v1.2.0-linux-amd64.tar.xz -O oauthapp.xz && + # tar -xf oauthapp.xz && + # mv vault-plugin-secrets-oauthapp-v1.2.0-linux-amd64 /usr/local/libexec/vault/oauthapp && + # chmod +x /usr/local/libexec/vault/oauthapp + # volumeMounts: + # - name: plugins + # mountPath: /usr/local/libexec/vault + + # extraContainers is a list of sidecar containers. Specified as a YAML list. + extraContainers: null + + # shareProcessNamespace enables process namespace sharing between Vault and the extraContainers + # This is useful if Vault must be signaled, e.g. to send a SIGHUP for a log rotation + shareProcessNamespace: false + + # extraArgs is a string containing additional Vault server arguments. + extraArgs: "" + + # Used to define custom readinessProbe settings + readinessProbe: + enabled: true + # If you need to use a http path instead of the default exec + # path: /v1/sys/health?standbyok=true + + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 2 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 5 + # How often (in seconds) to perform the probe + periodSeconds: 5 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 3 + # Used to enable a livenessProbe for the pods + livenessProbe: + enabled: false + path: "/v1/sys/health?standbyok=true" + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 2 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 60 + # How often (in seconds) to perform the probe + periodSeconds: 5 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 3 + + # Optional duration in seconds the pod needs to terminate gracefully. + # See: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/ + terminationGracePeriodSeconds: 10 + + # Used to set the sleep time during the preStop step + preStopSleepSeconds: 5 + + # Used to define commands to run after the pod is ready. + # This can be used to automate processes such as initialization + # or boostrapping auth methods. + postStart: [] + # - /bin/sh + # - -c + # - /vault/userconfig/myscript/run.sh + + # extraEnvironmentVars is a list of extra environment variables to set with the stateful set. These could be + # used to include variables required for auto-unseal. + extraEnvironmentVars: {} + # GOOGLE_REGION: global + # GOOGLE_PROJECT: myproject + # GOOGLE_APPLICATION_CREDENTIALS: /vault/userconfig/myproject/myproject-creds.json + + # extraSecretEnvironmentVars is a list of extra environment variables to set with the stateful set. + # These variables take value from existing Secret objects. + extraSecretEnvironmentVars: [] + # - envName: AWS_SECRET_ACCESS_KEY + # secretName: vault + # secretKey: AWS_SECRET_ACCESS_KEY + + # Deprecated: please use 'volumes' instead. + # extraVolumes is a list of extra volumes to mount. These will be exposed + # to Vault in the path `/vault/userconfig//`. The value below is + # an array of objects, examples are shown below. + extraVolumes: [] + # - type: secret (or "configMap") + # name: my-secret + # path: null # default is `/vault/userconfig` + + # volumes is a list of volumes made available to all containers. These are rendered + # via toYaml rather than pre-processed like the extraVolumes value. + # The purpose is to make it easy to share volumes between containers. + volumes: null + # - name: plugins + # emptyDir: {} + + # volumeMounts is a list of volumeMounts for the main server container. These are rendered + # via toYaml rather than pre-processed like the extraVolumes value. + # The purpose is to make it easy to share volumes between containers. + volumeMounts: null + # - mountPath: /usr/local/libexec/vault + # name: plugins + # readOnly: true + + # Affinity Settings + # Commenting out or setting as empty the affinity variable, will allow + # deployment to single node services such as Minikube + # This should be either a multi-line string or YAML matching the PodSpec's affinity field. + affinity: | + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app.kubernetes.io/name: {{ template "vault.name" . }} + app.kubernetes.io/instance: "{{ .Release.Name }}" + component: server + topologyKey: kubernetes.io/hostname + + # Topology settings for server pods + # ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ + # This should be either a multi-line string or YAML matching the topologySpreadConstraints array + # in a PodSpec. + topologySpreadConstraints: [] + + # Toleration Settings for server pods + # This should be either a multi-line string or YAML matching the Toleration array + # in a PodSpec. + tolerations: [] + + # nodeSelector labels for server pod assignment, formatted as a multi-line string or YAML map. + # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + # Example: + # nodeSelector: + # beta.kubernetes.io/arch: amd64 + nodeSelector: {} + + # Enables network policy for server pods + networkPolicy: + enabled: false + egress: [] + # egress: + # - to: + # - ipBlock: + # cidr: 10.0.0.0/24 + # ports: + # - protocol: TCP + # port: 443 + + # Priority class for server pods + priorityClassName: "" + + # Extra labels to attach to the server pods + # This should be a YAML map of the labels to apply to the server pods + extraLabels: {} + + # Extra annotations to attach to the server pods + # This can either be YAML or a YAML-formatted multi-line templated string map + # of the annotations to apply to the server pods + annotations: {} + + # Enables a headless service to be used by the Vault Statefulset + service: + enabled: true + # clusterIP controls whether a Cluster IP address is attached to the + # Vault service within Kubernetes. By default, the Vault service will + # be given a Cluster IP address, set to None to disable. When disabled + # Kubernetes will create a "headless" service. Headless services can be + # used to communicate with pods directly through DNS instead of a round-robin + # load balancer. + # clusterIP: None + + # Configures the service type for the main Vault service. Can be ClusterIP + # or NodePort. + #type: ClusterIP + + # Do not wait for pods to be ready + publishNotReadyAddresses: true + + # The externalTrafficPolicy can be set to either Cluster or Local + # and is only valid for LoadBalancer and NodePort service types. + # The default value is Cluster. + # ref: https://kubernetes.io/docs/concepts/services-networking/service/#external-traffic-policy + externalTrafficPolicy: Cluster + + # If type is set to "NodePort", a specific nodePort value can be configured, + # will be random if left blank. + #nodePort: 30000 + + # When HA mode is enabled + # If type is set to "NodePort", a specific nodePort value can be configured, + # will be random if left blank. + #activeNodePort: 30001 + + # When HA mode is enabled + # If type is set to "NodePort", a specific nodePort value can be configured, + # will be random if left blank. + #standbyNodePort: 30002 + + # Port on which Vault server is listening + port: 8200 + # Target port to which the service should be mapped to + targetPort: 8200 + # Extra annotations for the service definition. This can either be YAML or a + # YAML-formatted multi-line templated string map of the annotations to apply + # to the service. + annotations: {} + + # This configures the Vault Statefulset to create a PVC for data + # storage when using the file or raft backend storage engines. + # See https://www.vaultproject.io/docs/configuration/storage/index.html to know more + dataStorage: + enabled: true + # Size of the PVC created + size: 10Gi + # Location where the PVC will be mounted. + mountPath: "/vault/data" + # Name of the storage class to use. If null it will use the + # configured default Storage Class. + storageClass: null + # Access Mode of the storage device being used for the PVC + accessMode: ReadWriteOnce + # Annotations to apply to the PVC + annotations: {} + + # This configures the Vault Statefulset to create a PVC for audit + # logs. Once Vault is deployed, initialized, and unsealed, Vault must + # be configured to use this for audit logs. This will be mounted to + # /vault/audit + # See https://www.vaultproject.io/docs/audit/index.html to know more + auditStorage: + enabled: false + # Size of the PVC created + size: 10Gi + # Location where the PVC will be mounted. + mountPath: "/vault/audit" + # Name of the storage class to use. If null it will use the + # configured default Storage Class. + storageClass: null + # Access Mode of the storage device being used for the PVC + accessMode: ReadWriteOnce + # Annotations to apply to the PVC + annotations: {} + + # Run Vault in "dev" mode. This requires no further setup, no state management, + # and no initialization. This is useful for experimenting with Vault without + # needing to unseal, store keys, et. al. All data is lost on restart - do not + # use dev mode for anything other than experimenting. + # See https://www.vaultproject.io/docs/concepts/dev-server.html to know more + dev: + enabled: false + + # Set VAULT_DEV_ROOT_TOKEN_ID value + devRootToken: "root" + + # Run Vault in "standalone" mode. This is the default mode that will deploy if + # no arguments are given to helm. This requires a PVC for data storage to use + # the "file" backend. This mode is not highly available and should not be scaled + # past a single replica. + standalone: + enabled: "-" + + # config is a raw string of default configuration when using a Stateful + # deployment. Default is to use a PersistentVolumeClaim mounted at /vault/data + # and store data there. This is only used when using a Replica count of 1, and + # using a stateful set. This should be HCL. + + # Note: Configuration files are stored in ConfigMaps so sensitive data + # such as passwords should be either mounted through extraSecretEnvironmentVars + # or through a Kube secret. For more information see: + # https://www.vaultproject.io/docs/platform/k8s/helm/run#protecting-sensitive-vault-configurations + config: | + ui = true + + listener "tcp" { + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + # Enable unauthenticated metrics access (necessary for Prometheus Operator) + #telemetry { + # unauthenticated_metrics_access = "true" + #} + } + storage "file" { + path = "/vault/data" + } + + # Example configuration for using auto-unseal, using Google Cloud KMS. The + # GKMS keys must already exist, and the cluster must have a service account + # that is authorized to access GCP KMS. + #seal "gcpckms" { + # project = "vault-helm-dev" + # region = "global" + # key_ring = "vault-helm-unseal-kr" + # crypto_key = "vault-helm-unseal-key" + #} + + # Example configuration for enabling Prometheus metrics in your config. + #telemetry { + # prometheus_retention_time = "30s", + # disable_hostname = true + #} + + # Run Vault in "HA" mode. There are no storage requirements unless the audit log + # persistence is required. In HA mode Vault will configure itself to use Consul + # for its storage backend. The default configuration provided will work the Consul + # Helm project by default. It is possible to manually configure Vault to use a + # different HA backend. + ha: + enabled: false + replicas: 3 + + # Set the api_addr configuration for Vault HA + # See https://www.vaultproject.io/docs/configuration#api_addr + # If set to null, this will be set to the Pod IP Address + apiAddr: null + + # Set the cluster_addr confuguration for Vault HA + # See https://www.vaultproject.io/docs/configuration#cluster_addr + # If set to null, this will be set to https://$(HOSTNAME).{{ template "vault.fullname" . }}-internal:8201 + clusterAddr: null + + # Enables Vault's integrated Raft storage. Unlike the typical HA modes where + # Vault's persistence is external (such as Consul), enabling Raft mode will create + # persistent volumes for Vault to store data according to the configuration under server.dataStorage. + # The Vault cluster will coordinate leader elections and failovers internally. + raft: + + # Enables Raft integrated storage + enabled: false + # Set the Node Raft ID to the name of the pod + setNodeId: false + + # Note: Configuration files are stored in ConfigMaps so sensitive data + # such as passwords should be either mounted through extraSecretEnvironmentVars + # or through a Kube secret. For more information see: + # https://www.vaultproject.io/docs/platform/k8s/helm/run#protecting-sensitive-vault-configurations + config: | + ui = true + + listener "tcp" { + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + # Enable unauthenticated metrics access (necessary for Prometheus Operator) + #telemetry { + # unauthenticated_metrics_access = "true" + #} + } + + storage "raft" { + path = "/vault/data" + } + + service_registration "kubernetes" {} + + # config is a raw string of default configuration when using a Stateful + # deployment. Default is to use a Consul for its HA storage backend. + # This should be HCL. + + # Note: Configuration files are stored in ConfigMaps so sensitive data + # such as passwords should be either mounted through extraSecretEnvironmentVars + # or through a Kube secret. For more information see: + # https://www.vaultproject.io/docs/platform/k8s/helm/run#protecting-sensitive-vault-configurations + config: | + ui = true + + listener "tcp" { + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + } + storage "consul" { + path = "vault" + address = "HOST_IP:8500" + } + + service_registration "kubernetes" {} + + # Example configuration for using auto-unseal, using Google Cloud KMS. The + # GKMS keys must already exist, and the cluster must have a service account + # that is authorized to access GCP KMS. + #seal "gcpckms" { + # project = "vault-helm-dev-246514" + # region = "global" + # key_ring = "vault-helm-unseal-kr" + # crypto_key = "vault-helm-unseal-key" + #} + + # Example configuration for enabling Prometheus metrics. + # If you are using Prometheus Operator you can enable a ServiceMonitor resource below. + # You may wish to enable unauthenticated metrics in the listener block above. + #telemetry { + # prometheus_retention_time = "30s", + # disable_hostname = true + #} + + # A disruption budget limits the number of pods of a replicated application + # that are down simultaneously from voluntary disruptions + disruptionBudget: + enabled: true + + # maxUnavailable will default to (n/2)-1 where n is the number of + # replicas. If you'd like a custom value, you can specify an override here. + maxUnavailable: null + + # Definition of the serviceAccount used to run Vault. + # These options are also used when using an external Vault server to validate + # Kubernetes tokens. + serviceAccount: + # Specifies whether a service account should be created + create: true + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + # Extra annotations for the serviceAccount definition. This can either be + # YAML or a YAML-formatted multi-line templated string map of the + # annotations to apply to the serviceAccount. + annotations: {} + + # Settings for the statefulSet used to run Vault. + statefulSet: + # Extra annotations for the statefulSet. This can either be YAML or a + # YAML-formatted multi-line templated string map of the annotations to apply + # to the statefulSet. + annotations: {} + + # Set the pod and container security contexts. + # If not set, these will default to, and for *not* OpenShift: + # pod: + # runAsNonRoot: true + # runAsGroup: {{ .Values.server.gid | default 1000 }} + # runAsUser: {{ .Values.server.uid | default 100 }} + # fsGroup: {{ .Values.server.gid | default 1000 }} + # container: + # allowPrivilegeEscalation: false + # + # If not set, these will default to, and for OpenShift: + # pod: {} + # container: {} + securityContext: + pod: {} + container: {} + + +# Vault UI +ui: + # True if you want to create a Service entry for the Vault UI. + # + # serviceType can be used to control the type of service created. For + # example, setting this to "LoadBalancer" will create an external load + # balancer (for supported K8S installations) to access the UI. + enabled: false + publishNotReadyAddresses: true + # The service should only contain selectors for active Vault pod + activeVaultPodOnly: false + serviceType: "ClusterIP" + serviceNodePort: null + externalPort: 8200 + targetPort: 8200 + + # The externalTrafficPolicy can be set to either Cluster or Local + # and is only valid for LoadBalancer and NodePort service types. + # The default value is Cluster. + # ref: https://kubernetes.io/docs/concepts/services-networking/service/#external-traffic-policy + externalTrafficPolicy: Cluster + + #loadBalancerSourceRanges: + # - 10.0.0.0/16 + # - 1.78.23.3/32 + + # loadBalancerIP: + + # Extra annotations to attach to the ui service + # This can either be YAML or a YAML-formatted multi-line templated string map + # of the annotations to apply to the ui service + annotations: {} + +# secrets-store-csi-driver-provider-vault +csi: + # True if you want to install a secrets-store-csi-driver-provider-vault daemonset. + # + # Requires installing the secrets-store-csi-driver separately, see: + # https://github.com/kubernetes-sigs/secrets-store-csi-driver#install-the-secrets-store-csi-driver + # + # With the driver and provider installed, you can mount Vault secrets into volumes + # similar to the Vault Agent injector, and you can also sync those secrets into + # Kubernetes secrets. + enabled: false + + image: + repository: "hashicorp/vault-csi-provider" + tag: "1.2.0" + pullPolicy: IfNotPresent + + # volumes is a list of volumes made available to all containers. These are rendered + # via toYaml rather than pre-processed like the extraVolumes value. + # The purpose is to make it easy to share volumes between containers. + volumes: null + # - name: tls + # secret: + # secretName: vault-tls + + # volumeMounts is a list of volumeMounts for the main server container. These are rendered + # via toYaml rather than pre-processed like the extraVolumes value. + # The purpose is to make it easy to share volumes between containers. + volumeMounts: null + # - name: tls + # mountPath: "/vault/tls" + # readOnly: true + + resources: {} + # resources: + # requests: + # cpu: 50m + # memory: 128Mi + # limits: + # cpu: 50m + # memory: 128Mi + + # Settings for the daemonSet used to run the provider. + daemonSet: + updateStrategy: + type: RollingUpdate + maxUnavailable: "" + # Extra annotations for the daemonSet. This can either be YAML or a + # YAML-formatted multi-line templated string map of the annotations to apply + # to the daemonSet. + annotations: {} + # Provider host path (must match the CSI provider's path) + providersDir: "/etc/kubernetes/secrets-store-csi-providers" + # Kubelet host path + kubeletRootDir: "/var/lib/kubelet" + # Extra labels to attach to the vault-csi-provider daemonSet + # This should be a YAML map of the labels to apply to the csi provider daemonSet + extraLabels: {} + # security context for the pod template and container in the csi provider daemonSet + securityContext: + pod: {} + container: {} + + pod: + # Extra annotations for the provider pods. This can either be YAML or a + # YAML-formatted multi-line templated string map of the annotations to apply + # to the pod. + annotations: {} + + # Toleration Settings for provider pods + # This should be either a multi-line string or YAML matching the Toleration array + # in a PodSpec. + tolerations: [] + + # Extra labels to attach to the vault-csi-provider pod + # This should be a YAML map of the labels to apply to the csi provider pod + extraLabels: {} + + + + # Priority class for csi pods + priorityClassName: "" + + serviceAccount: + # Extra annotations for the serviceAccount definition. This can either be + # YAML or a YAML-formatted multi-line templated string map of the + # annotations to apply to the serviceAccount. + annotations: {} + + # Extra labels to attach to the vault-csi-provider serviceAccount + # This should be a YAML map of the labels to apply to the csi provider serviceAccount + extraLabels: {} + + # Used to configure readinessProbe for the pods. + readinessProbe: + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 2 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 5 + # How often (in seconds) to perform the probe + periodSeconds: 5 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 3 + # Used to configure livenessProbe for the pods. + livenessProbe: + # When a probe fails, Kubernetes will try failureThreshold times before giving up + failureThreshold: 2 + # Number of seconds after the container has started before probe initiates + initialDelaySeconds: 5 + # How often (in seconds) to perform the probe + periodSeconds: 5 + # Minimum consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # Number of seconds after which the probe times out. + timeoutSeconds: 3 + + # Enables debug logging. + debug: false + + # Pass arbitrary additional arguments to vault-csi-provider. + # See https://www.vaultproject.io/docs/platform/k8s/csi/configurations#command-line-arguments + # for the available command line flags. + extraArgs: [] + +# Vault is able to collect and publish various runtime metrics. +# Enabling this feature requires setting adding `telemetry{}` stanza to +# the Vault configuration. There are a few examples included in the `config` sections above. +# +# For more information see: +# https://www.vaultproject.io/docs/configuration/telemetry +# https://www.vaultproject.io/docs/internals/telemetry +serverTelemetry: + # Enable support for the Prometheus Operator. Currently, this chart does not support + # authenticating to Vault's metrics endpoint, so the following `telemetry{}` must be included + # in the `listener "tcp"{}` stanza + # telemetry { + # unauthenticated_metrics_access = "true" + # } + # + # See the `standalone.config` for a more complete example of this. + # + # In addition, a top level `telemetry{}` stanza must also be included in the Vault configuration: + # + # example: + # telemetry { + # prometheus_retention_time = "30s", + # disable_hostname = true + # } + # + # Configuration for monitoring the Vault server. + serviceMonitor: + # The Prometheus operator *must* be installed before enabling this feature, + # if not the chart will fail to install due to missing CustomResourceDefinitions + # provided by the operator. + # + # Instructions on how to install the Helm chart can be found here: + # https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack + # More information can be found here: + # https://github.com/prometheus-operator/prometheus-operator + # https://github.com/prometheus-operator/kube-prometheus + + # Enable deployment of the Vault Server ServiceMonitor CustomResource. + enabled: false + + # Selector labels to add to the ServiceMonitor. + # When empty, defaults to: + # release: prometheus + selectors: {} + + # Interval at which Prometheus scrapes metrics + interval: 30s + + # Timeout for Prometheus scrapes + scrapeTimeout: 10s + + prometheusRules: + # The Prometheus operator *must* be installed before enabling this feature, + # if not the chart will fail to install due to missing CustomResourceDefinitions + # provided by the operator. + + # Deploy the PrometheusRule custom resource for AlertManager based alerts. + # Requires that AlertManager is properly deployed. + enabled: false + + # Selector labels to add to the PrometheusRules. + # When empty, defaults to: + # release: prometheus + selectors: {} + + # Some example rules. + rules: {} + # - alert: vault-HighResponseTime + # annotations: + # message: The response time of Vault is over 500ms on average over the last 5 minutes. + # expr: vault_core_handle_request{quantile="0.5", namespace="mynamespace"} > 500 + # for: 5m + # labels: + # severity: warning + # - alert: vault-HighResponseTime + # annotations: + # message: The response time of Vault is over 1s on average over the last 5 minutes. + # expr: vault_core_handle_request{quantile="0.5", namespace="mynamespace"} > 1000 + # for: 5m + # labels: + # severity: critical diff --git a/index.yaml b/index.yaml index 6bd599d3d..5e228c72a 100644 --- a/index.yaml +++ b/index.yaml @@ -755,6 +755,46 @@ entries: urls: - assets/confluent-for-kubernetes/confluent-for-kubernetes-0.174.2101.tgz version: 0.174.2101 + consul: + - annotations: + artifacthub.io/images: | + - name: consul + image: hashicorp/consul:1.13.2 + - name: consul-k8s-control-plane + image: hashicorp/consul-k8s-control-plane:0.49.0 + - name: envoy + image: envoyproxy/envoy:v1.23.1 + artifacthub.io/license: MPL-2.0 + artifacthub.io/links: | + - name: Documentation + url: https://www.consul.io/docs/k8s + - name: hashicorp/consul + url: https://github.com/hashicorp/consul + - name: hashicorp/consul-k8s + url: https://github.com/hashicorp/consul-k8s + artifacthub.io/prerelease: "false" + artifacthub.io/signKey: | + fingerprint: C874011F0AB405110D02105534365D9472D7468F + url: https://keybase.io/hashicorp/pgp_keys.asc + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Hashicorp Consul + catalog.cattle.io/kube-version: '>=1.21.0-0' + catalog.cattle.io/release-name: consul + apiVersion: v2 + appVersion: 1.13.2 + created: "2022-10-18T04:12:58.86086-04:00" + description: Official HashiCorp Consul Chart + digest: 6913f05d625d066a7929fbe7da312b04280e9fad06cb1a2b1325c69452ed6a84 + home: https://www.consul.io + icon: https://raw.githubusercontent.com/hashicorp/consul-k8s/main/assets/icon.png + kubeVersion: '>=1.21.0-0' + name: consul + sources: + - https://github.com/hashicorp/consul + - https://github.com/hashicorp/consul-k8s + urls: + - assets/hashicorp/consul-0.49.0.tgz + version: 0.49.0 control-agent: - annotations: catalog.cattle.io/certified: partner @@ -6282,6 +6322,37 @@ entries: urls: - assets/vals-operator/vals-operator-0.2.1.tgz version: 0.2.1 + vault: + - annotations: + catalog.cattle.io/certified: partner + catalog.cattle.io/display-name: Hashicorp Vault + catalog.cattle.io/kube-version: '>= 1.16.0-0' + catalog.cattle.io/release-name: vault + apiVersion: v2 + appVersion: 1.11.3 + created: "2022-10-18T04:12:58.86272-04:00" + description: Official HashiCorp Vault Chart + digest: 7f456874871dce21fcb146bdf2737a30be936b8b377af7fc77a1e22c0ca5e726 + home: https://www.vaultproject.io + icon: https://github.com/hashicorp/vault/raw/f22d202cde2018f9455dec755118a9b84586e082/Vault_PrimaryLogo_Black.png + keywords: + - vault + - security + - encryption + - secrets + - management + - automation + - infrastructure + kubeVersion: '>= 1.16.0-0' + name: vault + sources: + - https://github.com/hashicorp/vault + - https://github.com/hashicorp/vault-helm + - https://github.com/hashicorp/vault-k8s + - https://github.com/hashicorp/vault-csi-provider + urls: + - assets/hashicorp/vault-0.22.0.tgz + version: 0.22.0 ziti-host: - annotations: catalog.cattle.io/certified: partner diff --git a/packages/hashicorp/consul/upstream.yaml b/packages/hashicorp/consul/upstream.yaml new file mode 100644 index 000000000..972906ebe --- /dev/null +++ b/packages/hashicorp/consul/upstream.yaml @@ -0,0 +1,4 @@ +HelmRepo: https://helm.releases.hashicorp.com +HelmChart: consul +Vendor: Hashicorp +DisplayName: Hashicorp Consul diff --git a/packages/hashicorp/vault/upstream.yaml b/packages/hashicorp/vault/upstream.yaml new file mode 100644 index 000000000..abf5bc31e --- /dev/null +++ b/packages/hashicorp/vault/upstream.yaml @@ -0,0 +1,4 @@ +HelmRepo: https://helm.releases.hashicorp.com +HelmChart: vault +Vendor: Hashicorp +DisplayName: Hashicorp Vault