~damien/infrastructure

a78c6d090287cb842866f596c21d70e5d61f6601 — Damien Radtke 11 months ago 6e780e4
Add vault
M stackscripts/cluster-member.sh => stackscripts/cluster-member.sh +4 -0
@@ 139,6 139,10 @@ EOF
  chown -R "${name}:${name}" "${certs_dir}"

  usermod -a -G "${name}" damien

  # vault and nomad need to be in the consul group so they can access the unix socket.
  # Technically, nomad doesn't need it since it runs as root, but it's good for consistency.
  usermod -a -G consul "${name}"
}
# }}}


M terraform/main.tf => terraform/main.tf +17 -0
@@ 69,6 69,23 @@ module "nomad-client" {
  ca_key           = var.ca_key
}

module "vault-server" {
  source = "./vault-server"

  servers           = 2
  consul_version    = "1.7.2"
  vault_version     = "1.4.0"
  consul_server_ips = module.consul-server.ips

  datacenter       = local.region
  image            = local.image
  instance_type    = local.instance_type
  stackscript_id   = local.stackscript_id
  authorized_users = local.authorized_users
  ca_host          = var.ca_host
  ca_key           = var.ca_key
}

// resource "linode_instance" "ca_central_nomad_servers" {
// 	count = 1
//     region = "ca-central"

M terraform/nomad-server/main.tf => terraform/nomad-server/main.tf +1 -0
@@ 124,6 124,7 @@ resource "linode_instance" "servers" {
    destination = "/etc/profile.local"
    content     = <<-EOT
      export CONSUL_HTTP_ADDR=unix:///var/run/consul/consul_https.sock
			export CONSUL_HTTP_SSL=true
			export NOMAD_ADDR=https://localhost:4646
			export NOMAD_CACERT=/etc/ssl/nomad/ca.pem
			export NOMAD_CLIENT_CERT=/etc/ssl/nomad/cli.pem

A terraform/vault-server/main.tf => terraform/vault-server/main.tf +209 -0
@@ 0,0 1,209 @@
resource "linode_instance" "servers" {
  count            = var.servers
  label            = "vault-server-${random_id.servers[count.index].keepers.datacenter}-${random_id.servers[count.index].b64_url}"
  region           = random_id.servers[count.index].keepers.datacenter
  image            = random_id.servers[count.index].keepers.image
  type             = random_id.servers[count.index].keepers.instance_type
  authorized_users = var.authorized_users

  stackscript_id = var.stackscript_id
  stackscript_data = {
    hostname       = "vault-server-${random_id.servers[count.index].keepers.datacenter}-${random_id.servers[count.index].b64_url}"
    consul_version = random_id.servers[count.index].keepers.consul_version
    vault_version  = random_id.servers[count.index].keepers.vault_version
  }

  // wait for stackscript to complete
  provisioner "remote-exec" {
    connection { host = split("/", self.ipv6)[0] }
    inline = [
      "while ! [[ -f /root/StackScript.complete ]]; do echo 'waiting for stackscript to complete...'; sleep 3; done",
    ]
  }

  // systemd service files
  provisioner "file" {
    connection { host = split("/", self.ipv6)[0] }
    destination = "/etc/systemd/system/"
    source      = "../services/"
  }

  // cfssl config
  provisioner "file" {
    connection { host = split("/", self.ipv6)[0] }
    destination = "/etc/ssl/cfssl.json"
    content     = data.template_file.cfssl_config.rendered
  }

  // consul config
  provisioner "file" {
    connection { host = split("/", self.ipv6)[0] }
    destination = "/etc/consul.d"
    source      = "../config/consul"
  }

  provisioner "file" {
    connection { host = split("/", self.ipv6)[0] }
    destination = "/etc/consul.d/client.hcl"
    content     = <<-EOT
      datacenter       = "${var.datacenter}"
      server           = false
      retry_join       = [
        %{for ip in var.consul_server_ips~}
          "${split("/", ip)[0]}",
        %{endfor~}
      ]
    EOT
  }

  // vault config
  provisioner "file" {
    connection { host = split("/", self.ipv6)[0] }
    destination = "/etc/vault.d"
    source      = "../config/vault"
  }

  provisioner "file" {
    connection { host = split("/", self.ipv6)[0] }
    destination = "/etc/vault.d/server.hcl"
    content     = <<-EOT
		  datacenter = "${var.datacenter}"

      server {
				enabled = true
				bootstrap_expect = ${var.servers}
			}
    EOT
  }

  // firewall services
  provisioner "file" {
    connection { host = split("/", self.ipv6)[0] }
    destination = "/etc/firewalld/services"
    source      = "../firewall/services/"
  }

  // firewall zones
  provisioner "file" {
    connection { host = split("/", self.ipv6)[0] }
    destination = "/etc/firewalld/zones"
    source      = "../firewall/zones/"
  }

  // issue-cert script
  provisioner "file" {
    connection { host = split("/", self.ipv6)[0] }
    destination = "/usr/local/bin/issue-cert.sh"
    source      = "../scripts/issue-cert.sh"
  }

  // Consul certificate authority
  provisioner "file" {
    connection { host = split("/", self.ipv6)[0] }
    destination = "/etc/ssl/consul/ca.pem"
    source      = "/etc/ssl/consul/ca.pem"
  }

  // Vault certificate authority
  provisioner "file" {
    connection { host = split("/", self.ipv6)[0] }
    destination = "/etc/ssl/vault/ca.pem"
    source      = "/etc/ssl/vault/ca.pem"
  }

  // set global environment variables
  provisioner "file" {
    connection { host = split("/", self.ipv6)[0] }
    destination = "/etc/profile.local"
    content     = <<-EOT
      export CONSUL_HTTP_ADDR=unix:///var/run/consul/consul_https.sock
			export VAULT_ADDR=https://localhost:8200
			export VAULT_CACERT=/etc/ssl/vault/ca.pem
			export VAULT_CLIENT_CERT=/etc/ssl/vault/cli.pem
			export VAULT_CLIENT_KEY=/etc/ssl/vault/cli-key.pem
    EOT
  }

  // reload firewall
  provisioner "remote-exec" {
    connection { host = split("/", self.ipv6)[0] }
    inline = ["service firewalld reload"]
  }

  // fix permissions
  provisioner "remote-exec" {
    connection { host = split("/", self.ipv6)[0] }
    inline = [
      "chown -R consul:consul /etc/consul.d",
      "chown -R vault:vault /etc/vault.d",
      "chmod +x /usr/local/bin/issue-cert.sh",
      "chmod 0400 /etc/ssl/cfssl.json",
    ]
  }

  // issue certs
  provisioner "remote-exec" {
    connection { host = split("/", self.ipv6)[0] }
    inline = [
      "/usr/local/bin/issue-cert.sh --user consul --ca consul --name consul",
      "/usr/local/bin/issue-cert.sh --user vault --ca consul --name consul",
      "/usr/local/bin/issue-cert.sh --user vault --ca vault --name vault",
      "/usr/local/bin/issue-cert.sh --user vault --ca vault --name cli",
    ]
  }

  // fix CLI key permissions
  provisioner "remote-exec" {
    connection { host = split("/", self.ipv6)[0] }
    inline = [
      "chmod g+r /etc/ssl/vault/cli-key.pem",
    ]
  }

  // start services
  provisioner "remote-exec" {
    connection { host = split("/", self.ipv6)[0] }
    inline = [
      "systemctl enable consul && service consul start",
      "systemctl enable vault && service vault start",
    ]
  }

  // install autocompletion
  provisioner "remote-exec" {
    connection { host = split("/", self.ipv6)[0] }
    inline = [
			"sudo -u damien /usr/local/bin/consul -autocomplete-install",
			"sudo -u damien /usr/local/bin/vault -autocomplete-install",
		]
  }

  // disable further root ssh
  provisioner "remote-exec" {
    connection { host = split("/", self.ipv6)[0] }
    inline = [
      "sed -i 's/PermitRootLogin .*/PermitRootLogin no/' /etc/ssh/sshd_config",
      "service sshd reload",
    ]
  }
}

resource "random_id" "servers" {
  count = var.servers
  keepers = {
    datacenter     = var.datacenter
    image          = var.image
    instance_type  = var.instance_type
    consul_version = var.consul_version
    vault_version  = var.vault_version
  }
  byte_length = 4
}

data "template_file" "cfssl_config" {
  template = file("${path.module}/../../config/cfssl.json")
  vars = {
    ca_host = var.ca_host
    ca_key  = var.ca_key
  }
}

A terraform/vault-server/outputs.tf => terraform/vault-server/outputs.tf +4 -0
@@ 0,0 1,4 @@
output "ips" {
  description = "Vault server IP addresses"
  value       = linode_instance.servers[*].ipv6
}

A terraform/vault-server/variables.tf => terraform/vault-server/variables.tf +14 -0
@@ 0,0 1,14 @@
variable servers { type = number }

variable datacenter { type = string }
variable image { type = string }
variable instance_type { type = string }
variable stackscript_id { type = number }
variable authorized_users { type = list(string) }

variable ca_host { type = string }
variable ca_key { type = string }

variable consul_version { type = string }
variable vault_version { type = string }
variable consul_server_ips { type = list(string) }