Skip to content

Commit

Permalink
[tf][vault-init] add terraform for vault initialization
Browse files Browse the repository at this point in the history
  • Loading branch information
sherry-x authored and bors-libra committed Dec 13, 2021
1 parent ee87270 commit c849ccf
Show file tree
Hide file tree
Showing 10 changed files with 702 additions and 0 deletions.
41 changes: 41 additions & 0 deletions terraform/vault-init/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 58 additions & 0 deletions terraform/vault-init/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
Diem Validator Vault Initialisation
===================================

This directory contains Terraform configuration to initialise the keys and data
in Hashicorp Vault needed by a Diem Validator deployment. It does not deploy
Vault itself; for that please see the cloud-specific Terraform configs. You
will need a root token (or similar) to apply this configuration.

What it does
------------

You should review the Terraform configuration to understand what is being done
in your Vault deployment, but at a high level it creates:

* KV-v2 data: `diem/owner_account`, `diem/operator_account`, `diem/waypoint`,
`diem/safety_data`
* Transit keys: `diem__owner`, `diem__operator`, `diem__consensus`,
`diem__validator_network`, `diem__fullnode_network`, `diem__execution`
* Policies: `diem-validator`, `diem-safety-rules`, `diem-key-manager`,
`diem-fullnode`, `diem-management`

Kubernetes Integration
----------------------

This also configures authentication with the Kubernetes cluster which the Diem
Validator runs in, and maps the Kubernetes Service Accounts to the appropriate
Vault policies. If you want to configure authentication yourself please delete
`kubernetes.tf` before applying. Otherwise you will need to provide some
information about your Kubernetes cluster. If you are using the Diem
cloud-specific Terraform configs to create your Kubernetes cluster, this
information will be written to `kubernetes.json` by that Terraform and can be
directly provided to this Terraform.


Setting up Hashicorp Vault Locally
----------------------------------
If you don't use Terraform or Cloud infrastructure, you can follow this instructions to setup Vault locally.

1. Install Vault and set up a vault server instance https://learn.hashicorp.com/tutorials/vault/getting-started-install?in=vault/getting-started

2. Initialize Vault server https://learn.hashicorp.com/tutorials/vault/getting-started-deploy?in=vault/getting-started. Record the Recovery Key and Initial Root Token securely (e.g. in a password manager)

3. Create vault policies used by validator deployment
* Create the policy content in HCL files (json format compatible) https://learn.hashicorp.com/tutorials/vault/getting-started-policies?in=vault/getting-started#policy-format
* Write the policies into vault server https://learn.hashicorp.com/tutorials/vault/getting-started-policies?in=vault/getting-started#write-a-policy
* List of policies: diem-validator, diem-safety-rules, diem-key-manager, diem-fullnode, diem-management. Details of each policy can be found in this file [policy.tf][]

4. Create KV-v2 data used by validator deployment
* List of KV-v2 data can be found in this file as “vault_generic_secret” [main.tf][]
* Writing each of the KV-v2 secrets into secret engine https://learn.hashicorp.com/tutorials/vault/getting-started-first-secret?in=vault/getting-started#writing-a-secret

5. Create transit keys used by validator deployment
* Enable transit engine https://learn.hashicorp.com/tutorials/vault/eaas-transit#configure-transit-secrets-engine
* Create transit keys in ED25519 type https://www.vaultproject.io/api/secret/transit#create-key
* List of transit keys can be found in this file as “vault_transit_secret_backend_key” [main.tf][]

[policy.tf]: policy.tf
[main.tf]: main.tf
10 changes: 10 additions & 0 deletions terraform/vault-init/backend.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
terraform {
backend "s3" {} # AWS
#backend "gcs" {} # GCP
#backend "azurerm" {} # Azure

#backend "s3" { #SCW
#skip_credentials_validation = true
#skip_region_validation = true
#}
}
10 changes: 10 additions & 0 deletions terraform/vault-init/backend.tf.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
terraform {
#backend "s3" {} # AWS
#backend "gcs" {} # GCP
#backend "azurerm" {} # Azure

#backend "s3" { #SCW
#skip_credentials_validation = true
#skip_region_validation = true
#}
}
14 changes: 14 additions & 0 deletions terraform/vault-init/backend.tfvars
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# AWS S3
#bucket = ""
#key = "state/vault"
#region = ""

# Google GCS
#bucket = ""
#prefix = "state/vault"

# Azure Storage Container
#resource_group_name = ""
#storage_account_name = ""
#container_name = ""
#key = "state/vault"
13 changes: 13 additions & 0 deletions terraform/vault-init/diem-root.tf.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
resource "vault_transit_secret_backend_key" "diem_root" {
backend = var.transit_mount
name = "${var.namespace}__diem_root"
type = "ed25519"
depends_on = [null_resource.mounts_created]
}

resource "vault_transit_secret_backend_key" "treasury_compliance" {
backend = var.transit_mount
name = "${var.namespace}__treasury_compliance"
type = "ed25519"
depends_on = [null_resource.mounts_created]
}
86 changes: 86 additions & 0 deletions terraform/vault-init/kubernetes.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
variable "kubernetes_host" {
type = string
description = "URL of Kubernetes master API"
}

variable "kubernetes_ca_cert" {
type = string
description = "PEM certificate for the Kubernetes CA"
}

variable "issuer" {
default = ""
description = "JWT issuer"
}

variable "service_account_prefix" {
type = string
description = "Prefix for Diem service accounts, e.g. default-diem-validator"
}

variable "pod_cidrs" {
default = []
description = "List of IP CIDRs which are allowed to authenticate"
}

resource "vault_auth_backend" "kubernetes" {
type = "kubernetes"
path = "kubernetes-${var.namespace}"
}

resource "vault_kubernetes_auth_backend_config" "kubernetes" {
backend = vault_auth_backend.kubernetes.path
kubernetes_host = var.kubernetes_host
kubernetes_ca_cert = var.kubernetes_ca_cert
issuer = var.issuer
}

resource "vault_kubernetes_auth_backend_role" "safety-rules" {
backend = vault_auth_backend.kubernetes.path
role_name = "${var.namespace}-safety-rules"
bound_service_account_names = ["${var.service_account_prefix}-safety-rules"]
bound_service_account_namespaces = ["*"]
token_bound_cidrs = var.pod_cidrs
token_period = 3600
token_policies = [vault_policy.safety-rules.name]
}

resource "vault_kubernetes_auth_backend_role" "validator" {
backend = vault_auth_backend.kubernetes.path
role_name = "${var.namespace}-validator"
bound_service_account_names = ["${var.service_account_prefix}-validator"]
bound_service_account_namespaces = ["*"]
token_bound_cidrs = var.pod_cidrs
token_period = 3600
token_policies = [vault_policy.validator.name]
}

resource "vault_kubernetes_auth_backend_role" "fullnode" {
backend = vault_auth_backend.kubernetes.path
role_name = "${var.namespace}-fullnode"
bound_service_account_names = ["${var.service_account_prefix}-fullnode"]
bound_service_account_namespaces = ["*"]
token_bound_cidrs = var.pod_cidrs
token_period = 3600
token_policies = [vault_policy.fullnode.name]
}

resource "vault_kubernetes_auth_backend_role" "key-manager" {
backend = vault_auth_backend.kubernetes.path
role_name = "${var.namespace}-key-manager"
bound_service_account_names = ["${var.service_account_prefix}-key-manager"]
bound_service_account_namespaces = ["*"]
token_bound_cidrs = var.pod_cidrs
token_period = 3600
token_policies = [vault_policy.key-manager.name]
}

variable "depends_on_" {
description = "Dummy variable used by testnet Terraform"
type = list(string)
default = []
}

output "kubernetes_auth_path" {
value = vault_auth_backend.kubernetes.path
}
151 changes: 151 additions & 0 deletions terraform/vault-init/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
provider "vault" {}

variable "namespace" {
description = "Prefix to use when naming secrets and transit keys"
default = "diem"
}

variable "kv_v2_mount" {
description = "Mount path of a Key/Value version 2 engine"
default = "secret"
}

variable "transit_mount" {
description = "Mount path of a Transit engine"
default = "transit"
}

variable "mount_engines" {
description = "Create the KV-v2 and Transit engine mounts"
default = true
}

variable "reset_safety_data" {
description = "Reset the Diem Safety Rules counters when applying"
default = true
}

variable "validator_network_address_key" {
description = "Decryption key for validator network address"
type = string
}

resource "vault_mount" "secret" {
count = var.mount_engines ? 1 : 0
path = var.kv_v2_mount
type = "kv-v2"
}

resource "vault_mount" "transit" {
count = var.mount_engines ? 1 : 0
path = var.transit_mount
type = "transit"
}

resource "null_resource" "mounts_created" {
triggers = {
kv_v2 = join("", vault_mount.secret[*].accessor)
transit = join("", vault_mount.transit[*].accessor)
}
}

resource "vault_generic_secret" "safety_data" {
path = "${var.kv_v2_mount}/${var.namespace}/safety_data"
data_json = jsonencode({
safety_data = {
epoch = 0
last_voted_round = 0
preferred_round = 0
last_vote = null
}
})
disable_read = ! var.reset_safety_data
depends_on = [null_resource.mounts_created]
}

resource "vault_generic_secret" "owner_account" {
path = "${var.kv_v2_mount}/${var.namespace}/owner_account"
data_json = "{}"
depends_on = [null_resource.mounts_created]
disable_read = true
}

resource "vault_generic_secret" "operator_account" {
path = "${var.kv_v2_mount}/${var.namespace}/operator_account"
data_json = "{}"
depends_on = [null_resource.mounts_created]
disable_read = true
}

resource "vault_generic_secret" "validator_network_address_keys" {
path = "${var.kv_v2_mount}/${var.namespace}/validator_network_address_keys"
data_json = jsonencode({
validator_network_address_keys = {
current = 0
keys = {
"0" = var.validator_network_address_key
}
}
})
depends_on = [null_resource.mounts_created]
}

resource "vault_transit_secret_backend_key" "owner" {
backend = var.transit_mount
name = "${var.namespace}__owner"
type = "ed25519"
depends_on = [null_resource.mounts_created]
}

resource "vault_transit_secret_backend_key" "operator" {
backend = var.transit_mount
name = "${var.namespace}__operator"
type = "ed25519"
depends_on = [null_resource.mounts_created]
lifecycle {
ignore_changes = [min_decryption_version, min_encryption_version]
}
}

resource "vault_transit_secret_backend_key" "consensus" {
backend = var.transit_mount
name = "${var.namespace}__consensus"
type = "ed25519"
depends_on = [null_resource.mounts_created]
lifecycle {
ignore_changes = [min_decryption_version, min_encryption_version]
}
}

resource "vault_transit_secret_backend_key" "execution" {
backend = var.transit_mount
name = "${var.namespace}__execution"
type = "ed25519"
exportable = true
depends_on = [null_resource.mounts_created]
lifecycle {
ignore_changes = [min_decryption_version, min_encryption_version]
}
}

resource "vault_transit_secret_backend_key" "validator_network" {
backend = var.transit_mount
name = "${var.namespace}__validator_network"
type = "ed25519"
exportable = true
depends_on = [null_resource.mounts_created]
lifecycle {
ignore_changes = [min_decryption_version, min_encryption_version]
}
}

resource "vault_transit_secret_backend_key" "fullnode_network" {
backend = var.transit_mount
name = "${var.namespace}__fullnode_network"
type = "ed25519"
exportable = true
depends_on = [null_resource.mounts_created]
lifecycle {
ignore_changes = [min_decryption_version, min_encryption_version]
}
}
Loading

0 comments on commit c849ccf

Please sign in to comment.