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.

Revisions

  1. dalekurt created this gist Jan 22, 2019.
    371 changes: 371 additions & 0 deletions gcp.tf
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,371 @@
    # 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}"
    }