9da2fc4b8b
Rather than assuming a short sleep will work, we instead wait until netcat is listening of the socket. We've also configured the netcat listener to persist after the first connection, which allows Vault and us to check the connection without the process closing. As we implemented this we also ran into AWS issues in us-east-1 and us-west-2, so we've changed our deploy regions until those issues are resolved. Signed-off-by: Ryan Cragun <me@ryan.ec>
166 lines
5 KiB
HCL
166 lines
5 KiB
HCL
# Copyright (c) HashiCorp, Inc.
|
|
# SPDX-License-Identifier: BUSL-1.1
|
|
|
|
terraform {
|
|
required_version = ">= 1.0"
|
|
|
|
required_providers {
|
|
enos = {
|
|
source = "app.terraform.io/hashicorp-qti/enos"
|
|
}
|
|
|
|
helm = {
|
|
source = "hashicorp/helm"
|
|
version = "2.6.0"
|
|
}
|
|
}
|
|
}
|
|
|
|
locals {
|
|
helm_chart_settings = {
|
|
"server.ha.enabled" = "true"
|
|
"server.ha.replicas" = var.vault_instance_count
|
|
"server.ha.raft.enabled" = "true"
|
|
"server.affinity" = ""
|
|
"server.image.repository" = var.image_repository
|
|
"server.image.tag" = var.image_tag
|
|
"server.image.pullPolicy" = "Never" # Forces local image use
|
|
"server.resources.requests.cpu" = "50m"
|
|
"server.limits.memory" = "200m"
|
|
"server.limits.cpu" = "200m"
|
|
"server.ha.raft.config" = file("${abspath(path.module)}/raft-config.hcl")
|
|
"server.dataStorage.size" = "100m"
|
|
"server.logLevel" = var.vault_log_level
|
|
}
|
|
all_helm_chart_settings = var.ent_license == null ? local.helm_chart_settings : merge(local.helm_chart_settings, {
|
|
"server.extraEnvironmentVars.VAULT_LICENSE" = var.ent_license
|
|
})
|
|
|
|
vault_address = "http://127.0.0.1:8200"
|
|
|
|
instance_indexes = [for idx in range(var.vault_instance_count) : tostring(idx)]
|
|
|
|
leader_idx = local.instance_indexes[0]
|
|
followers_idx = toset(slice(local.instance_indexes, 1, var.vault_instance_count))
|
|
}
|
|
|
|
resource "helm_release" "vault" {
|
|
name = "vault"
|
|
|
|
repository = "https://helm.releases.hashicorp.com"
|
|
chart = "vault"
|
|
|
|
dynamic "set" {
|
|
for_each = local.all_helm_chart_settings
|
|
|
|
content {
|
|
name = set.key
|
|
value = set.value
|
|
}
|
|
}
|
|
}
|
|
|
|
data "enos_kubernetes_pods" "vault_pods" {
|
|
kubeconfig_base64 = var.kubeconfig_base64
|
|
context_name = var.context_name
|
|
namespace = helm_release.vault.namespace
|
|
label_selectors = [
|
|
"app.kubernetes.io/name=vault",
|
|
"component=server"
|
|
]
|
|
|
|
depends_on = [helm_release.vault]
|
|
}
|
|
|
|
resource "enos_vault_init" "leader" {
|
|
bin_path = "/bin/vault"
|
|
vault_addr = local.vault_address
|
|
|
|
key_shares = 5
|
|
key_threshold = 3
|
|
|
|
transport = {
|
|
kubernetes = {
|
|
kubeconfig_base64 = var.kubeconfig_base64
|
|
context_name = var.context_name
|
|
pod = data.enos_kubernetes_pods.vault_pods.pods[local.leader_idx].name
|
|
namespace = data.enos_kubernetes_pods.vault_pods.pods[local.leader_idx].namespace
|
|
}
|
|
}
|
|
}
|
|
|
|
resource "enos_vault_unseal" "leader" {
|
|
bin_path = "/bin/vault"
|
|
vault_addr = local.vault_address
|
|
seal_type = "shamir"
|
|
unseal_keys = enos_vault_init.leader.unseal_keys_b64
|
|
|
|
transport = {
|
|
kubernetes = {
|
|
kubeconfig_base64 = var.kubeconfig_base64
|
|
context_name = var.context_name
|
|
pod = data.enos_kubernetes_pods.vault_pods.pods[local.leader_idx].name
|
|
namespace = data.enos_kubernetes_pods.vault_pods.pods[local.leader_idx].namespace
|
|
}
|
|
}
|
|
|
|
depends_on = [enos_vault_init.leader]
|
|
}
|
|
|
|
// We need to manually join the followers since the join request must only happen after the leader
|
|
// has been initialized. We could use retry join, but in that case we'd need to restart the follower
|
|
// pods once the leader is setup. The default helm deployment configuration for an HA cluster as
|
|
// documented here: https://learn.hashicorp.com/tutorials/vault/kubernetes-raft-deployment-guide#configure-vault-helm-chart
|
|
// uses a liveness probe that automatically restarts nodes that are not healthy. This works well for
|
|
// clusters that are configured with auto-unseal as eventually the nodes would join and unseal.
|
|
resource "enos_remote_exec" "raft_join" {
|
|
for_each = local.followers_idx
|
|
|
|
inline = [
|
|
// asserts that vault is ready
|
|
"for i in 1 2 3 4 5; do vault status > /dev/null 2>&1 && break || sleep 5; done",
|
|
// joins the follower to the leader
|
|
"vault operator raft join http://vault-0.vault-internal:8200"
|
|
]
|
|
|
|
transport = {
|
|
kubernetes = {
|
|
kubeconfig_base64 = var.kubeconfig_base64
|
|
context_name = var.context_name
|
|
pod = data.enos_kubernetes_pods.vault_pods.pods[each.key].name
|
|
namespace = data.enos_kubernetes_pods.vault_pods.pods[each.key].namespace
|
|
}
|
|
}
|
|
|
|
depends_on = [enos_vault_unseal.leader]
|
|
}
|
|
|
|
|
|
resource "enos_vault_unseal" "followers" {
|
|
for_each = local.followers_idx
|
|
|
|
bin_path = "/bin/vault"
|
|
vault_addr = local.vault_address
|
|
seal_type = "shamir"
|
|
unseal_keys = enos_vault_init.leader.unseal_keys_b64
|
|
|
|
transport = {
|
|
kubernetes = {
|
|
kubeconfig_base64 = var.kubeconfig_base64
|
|
context_name = var.context_name
|
|
pod = data.enos_kubernetes_pods.vault_pods.pods[each.key].name
|
|
namespace = data.enos_kubernetes_pods.vault_pods.pods[each.key].namespace
|
|
}
|
|
}
|
|
|
|
depends_on = [enos_remote_exec.raft_join]
|
|
}
|
|
|
|
output "vault_root_token" {
|
|
value = enos_vault_init.leader.root_token
|
|
}
|
|
|
|
output "vault_pods" {
|
|
value = data.enos_kubernetes_pods.vault_pods.pods
|
|
}
|