apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: vault-helm-gke namespace: argocd annotations: # Deploys the Vault Helm chart to the GKE cluster via Legion Argo CD. # The destination.server must be updated after `terraform apply`: # terraform -chdir=servers/gcp output -raw gke_cluster_endpoint spec: project: default source: repoURL: https://helm.releases.hashicorp.com chart: vault targetRevision: "0.29.1" helm: values: | global: enabled: true injector: enabled: false ui: enabled: true server: image: repository: hashicorp/vault tag: "1.19.2" # Workload Identity — Vault pod k8s SA impersonates vault-unseal GCP SA # for KMS auto-unseal. Binding is in servers/gcp/gke.tf. serviceAccount: create: true name: vault annotations: iam.gke.io/gcp-service-account: vault-unseal@neuron-785695.iam.gserviceaccount.com # GKE Autopilot: no privileged containers. Vault doesn't need privilege. # Request IPC_LOCK so Vault can lock memory (prevents secrets swap). securityContext: capabilities: add: - IPC_LOCK # HA mode — 3 replicas with Raft storage ha: enabled: true replicas: 3 raft: enabled: true setNodeId: true config: | ui = true listener "tcp" { tls_disable = 1 address = "[::]:8200" cluster_address = "[::]:8201" } storage "raft" { path = "/vault/data" retry_join { leader_api_addr = "http://vault-helm-gke-0.vault-helm-gke-internal:8200" } retry_join { leader_api_addr = "http://vault-helm-gke-1.vault-helm-gke-internal:8200" } retry_join { leader_api_addr = "http://vault-helm-gke-2.vault-helm-gke-internal:8200" } } seal "gcpckms" { project = "neuron-785695" region = "global" key_ring = "vault" crypto_key = "vault-unseal" } telemetry { prometheus_retention_time = "30s" disable_hostname = false } # Spread pods across GKE zones topologySpreadConstraints: - maxSkew: 1 topologyKey: topology.kubernetes.io/zone whenUnsatisfiable: ScheduleAnyway labelSelector: matchLabels: app.kubernetes.io/name: vault component: server # 10Gi SSD per pod — premium-rwo = pd-ssd on GKE Autopilot dataStorage: enabled: true size: 10Gi storageClass: standard-rwo accessMode: ReadWriteOnce readinessProbe: enabled: true path: "/v1/sys/health?standbyok=true&sealedok=true&uninitcode=200" initialDelaySeconds: 5 periodSeconds: 5 failureThreshold: 3 livenessProbe: enabled: true path: "/v1/sys/health?standbyok=true&sealedok=true&uninitcode=200" initialDelaySeconds: 60 periodSeconds: 10 failureThreshold: 3 # GKE Autopilot requires resource requests on all containers resources: requests: memory: 256Mi cpu: 500m limits: memory: 512Mi cpu: 1000m service: enabled: true type: ClusterIP port: 8200 targetPort: 8200 annotations: # Container-native NEG for GCP Global HTTPS LB backend cutover. # GKE will create a zonal NEG named k8s1--vault-vault-helm-gke-8200- # in each zone where Vault pods are scheduled. # After this syncs, list NEGs: # gcloud compute network-endpoint-groups list --filter="name~vault" --project neuron-785695 # Then reference them in servers/gcp/vault-gke-lb.tf. cloud.google.com/neg: '{"exposed_ports":{"8200":{}}}' # Ingress disabled — Vault is exposed via GCP HTTPS LB. # After migration, update the existing LB backend (vault-nodes.tf) # to target a GKE NEG instead of the GCE instance groups. # See: https://cloud.google.com/kubernetes-engine/docs/how-to/standalone-neg ingress: enabled: false destination: # Replace GKE_CLUSTER_ENDPOINT after `terraform apply`: # terraform -chdir=servers/gcp output -raw gke_cluster_endpoint server: https://34.63.89.52 namespace: vault syncPolicy: automated: prune: true selfHeal: true syncOptions: - CreateNamespace=true - ServerSideApply=true