Skip to content

Instantly share code, notes, and snippets.

@dalekurt
Created January 22, 2019 18:29
Show Gist options
  • Save dalekurt/3e4f1d9b1d45eb3a1622a60aa9d12cff to your computer and use it in GitHub Desktop.
Save dalekurt/3e4f1d9b1d45eb3a1622a60aa9d12cff to your computer and use it in GitHub Desktop.
Terraform template based on Seth Vargo's vault-on-gke
# This file contains all the interactions with Google Cloud
provider "google" {
region = "${var.region}"
project = "${var.project}"
# An earlier attempt to use a service account with roles/owner
# credentials = "${file("account.json")}"
}
terraform {
required_version = ">= 0.10.0"
backend "gcs" {
bucket = "my-project-vault-terraform-remote-state"
prefix = "terraform/production/state"
}
}
resource "random_id" "name" {
byte_length = "4"
}
# Generate a random id for the project - GCP projects must have globally
# unique names
resource "random_id" "random" {
prefix = "${var.project_prefix}"
byte_length = "8"
}
# Create the project
# resource "google_project" "vault" {
# name = "${random_id.random.hex}"
# project_id = "${random_id.random.hex}"
# org_id = "${var.org_id}"
# billing_account = "${var.billing_account}"
# }
# Create the vault service account
resource "google_service_account" "vault-server" {
account_id = "vault-server"
display_name = "Vault Server"
project = "${var.project}"
}
# Create a service account key
resource "google_service_account_key" "vault" {
service_account_id = "${google_service_account.vault-server.name}"
}
# Add the service account to the project
resource "google_project_iam_member" "service-account" {
count = "${length(var.service_account_iam_roles)}"
project = "${var.project}"
role = "${element(var.service_account_iam_roles, count.index)}"
member = "serviceAccount:${google_service_account.vault-server.email}"
}
# Add user-specified roles
resource "google_project_iam_member" "service-account-custom" {
count = "${length(var.service_account_custom_iam_roles)}"
project = "${var.project}"
role = "${element(var.service_account_custom_iam_roles, count.index)}"
member = "serviceAccount:${google_service_account.vault-server.email}"
}
# Enable required services on the project
resource "google_project_service" "service" {
count = "${length(var.project_services)}"
project = "${var.project}"
service = "${element(var.project_services, count.index)}"
# Do not disable the service on destroy. On destroy, we are going to
# destroy the project, but we need the APIs available to destroy the
# underlying resources.
disable_on_destroy = false
}
# Create the storage bucket
resource "google_storage_bucket" "vault" {
name = "${var.project}-vault-storage"
project = "${var.project}"
force_destroy = true
storage_class = "MULTI_REGIONAL"
versioning {
enabled = true
}
lifecycle_rule {
action {
type = "Delete"
}
condition {
num_newer_versions = 1
}
}
depends_on = ["google_project_service.service"]
}
# Grant service account access to the storage bucket
resource "google_storage_bucket_iam_member" "vault-server" {
count = "${length(var.storage_bucket_roles)}"
bucket = "${google_storage_bucket.vault.name}"
role = "${element(var.storage_bucket_roles, count.index)}"
member = "serviceAccount:${google_service_account.vault-server.email}"
}
# Create the KMS key ring
resource "google_kms_key_ring" "vault" {
name = "vault-${random_id.name.hex}"
location = "${var.region}"
project = "${var.project}"
depends_on = ["google_project_service.service"]
}
# Create the crypto key for encrypting init keys
resource "google_kms_crypto_key" "vault-init" {
name = "vault-init"
key_ring = "${google_kms_key_ring.vault.id}"
rotation_period = "604800s"
}
# Create a custom IAM role with the most minimal set of permissions for the
# KMS auto-unsealer. Once hashicorp/vault#5999 is merged, this can be replaced
# with the built-in roles/cloudkms.cryptoKeyEncrypterDecryptor role.
resource "google_project_iam_custom_role" "vault-seal-kms" {
project = "${var.project}"
role_id = "kmsEncrypterDecryptorViewer"
title = "KMS Encrypter Decryptor Viewer"
description = "KMS crypto key permissions to encrypt, decrypt, and view key data"
permissions = [
"cloudkms.cryptoKeyVersions.useToEncrypt",
"cloudkms.cryptoKeyVersions.useToDecrypt",
# This is required until hashicorp/vault#5999 is merged. The auto-unsealer
# attempts to read the key, which requires this additional permission.
"cloudkms.cryptoKeys.get",
]
}
# Grant service account access to the key
resource "google_kms_crypto_key_iam_member" "vault-init" {
crypto_key_id = "${google_kms_crypto_key.vault-init.id}"
role = "projects/${var.project}/roles/${google_project_iam_custom_role.vault-seal-kms.role_id}"
member = "serviceAccount:${google_service_account.vault-server.email}"
}
# Create an external NAT IP
resource "google_compute_address" "vault-nat" {
count = 2
name = "vault-nat-external-${count.index}"
project = "${var.project}"
region = "${var.region}"
depends_on = [
"google_project_service.service",
]
}
# Create a network for GKE
resource "google_compute_network" "vault-network" {
name = "vault-network"
project = "${var.project}"
auto_create_subnetworks = false
depends_on = [
"google_project_service.service",
]
}
# Create subnets
resource "google_compute_subnetwork" "vault-subnetwork" {
name = "vault-subnetwork"
project = "${var.project}"
network = "${google_compute_network.vault-network.self_link}"
region = "${var.region}"
ip_cidr_range = "${var.kubernetes_network_ipv4_cidr}"
private_ip_google_access = true
secondary_ip_range {
range_name = "vault-pods"
ip_cidr_range = "${var.kubernetes_pods_ipv4_cidr}"
}
secondary_ip_range {
range_name = "vault-svcs"
ip_cidr_range = "${var.kubernetes_services_ipv4_cidr}"
}
}
# Create a NAT router so the nodes can reach DockerHub, etc
resource "google_compute_router" "vault-router" {
name = "vault-router"
project = "${var.project}"
region = "${var.region}"
network = "${google_compute_network.vault-network.self_link}"
bgp {
asn = 64514
}
}
resource "google_compute_router_nat" "vault-nat" {
name = "vault-nat-1"
project = "${var.project}"
router = "${google_compute_router.vault-router.name}"
region = "${var.region}"
nat_ip_allocate_option = "MANUAL_ONLY"
nat_ips = ["${google_compute_address.vault-nat.*.self_link}"]
source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS"
subnetwork {
name = "${google_compute_subnetwork.vault-subnetwork.self_link}"
source_ip_ranges_to_nat = ["PRIMARY_IP_RANGE", "LIST_OF_SECONDARY_IP_RANGES"]
secondary_ip_range_names = [
"${google_compute_subnetwork.vault-subnetwork.secondary_ip_range.0.range_name}",
"${google_compute_subnetwork.vault-subnetwork.secondary_ip_range.1.range_name}",
]
}
}
# Get latest cluster version
data "google_container_engine_versions" "versions" {
project = "${var.project}"
region = "${var.region}"
}
# Create the GKE cluster
resource "google_container_cluster" "vault" {
name = "vault"
project = "${var.project}"
region = "${var.region}"
network = "${google_compute_network.vault-network.self_link}"
subnetwork = "${google_compute_subnetwork.vault-subnetwork.self_link}"
initial_node_count = "${var.kubernetes_nodes_per_zone}"
min_master_version = "${data.google_container_engine_versions.versions.latest_master_version}"
node_version = "${data.google_container_engine_versions.versions.latest_node_version}"
logging_service = "${var.kubernetes_logging_service}"
monitoring_service = "${var.kubernetes_monitoring_service}"
# Disable legacy ACLs. The default is false, but explicitly marking it false
# here as well.
enable_legacy_abac = false
node_config {
machine_type = "${var.kubernetes_instance_type}"
service_account = "${google_service_account.vault-server.email}"
oauth_scopes = [
"https://www.googleapis.com/auth/cloud-platform",
]
# Set metadata on the VM to supply more entropy
metadata {
google-compute-enable-virtio-rng = "true"
}
labels {
service = "vault"
}
tags = ["vault"]
# Protect node metadata
workload_metadata_config {
node_metadata = "SECURE"
}
}
# Configure various addons
addons_config {
# Disable the Kubernetes dashboard, which is often an attack vector. The
# cluster can still be managed via the GKE UI.
kubernetes_dashboard {
disabled = true
}
# Enable network policy configurations (like Calico).
network_policy_config {
disabled = false
}
}
# Disable basic authentication and cert-based authentication.
master_auth {
username = ""
password = ""
client_certificate_config {
issue_client_certificate = false
}
}
# Enable network policy configurations (like Calico) - for some reason this
# has to be in here twice.
network_policy {
enabled = true
}
# Set the maintenance window.
maintenance_policy {
daily_maintenance_window {
start_time = "${var.kubernetes_daily_maintenance_window}"
}
}
# Allocate IPs in our subnetwork
ip_allocation_policy {
cluster_secondary_range_name = "${google_compute_subnetwork.vault-subnetwork.secondary_ip_range.0.range_name}"
services_secondary_range_name = "${google_compute_subnetwork.vault-subnetwork.secondary_ip_range.1.range_name}"
}
# Specify the list of CIDRs which can access the master's API
master_authorized_networks_config {
cidr_blocks = ["${var.kubernetes_master_authorized_networks}"]
}
# Configure the cluster to be private (not have public facing IPs)
private_cluster_config {
# This field is misleading. This prevents access to the master API from
# any external IP. While that might represent the most secure
# configuration, it is not ideal for most setups. As such, we disable the
# private endpoint (allow the public endpoint) and restrict which CIDRs
# can talk to that endpoint.
enable_private_endpoint = false
enable_private_nodes = true
master_ipv4_cidr_block = "${var.kubernetes_masters_ipv4_cidr}"
}
depends_on = [
"google_project_service.service",
"google_kms_crypto_key_iam_member.vault-init",
"google_storage_bucket_iam_member.vault-server",
"google_project_iam_member.service-account",
"google_project_iam_member.service-account-custom",
"google_compute_router_nat.vault-nat",
]
}
# Provision IP
resource "google_compute_address" "vault" {
name = "vault-lb"
region = "${var.region}"
project = "${var.project}"
depends_on = ["google_project_service.service"]
}
output "address" {
value = "${google_compute_address.vault.address}"
}
output "project" {
value = "${var.project}"
}
output "region" {
value = "${var.region}"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment