From 9c32b4184ff2d2c0d60f80590aeb662225849a9f Mon Sep 17 00:00:00 2001 From: Patrick Stadler Date: Wed, 19 Dec 2018 08:45:12 +0100 Subject: [PATCH] secure etcd --- main.tf | 15 +- service/etcd/certs.tf | 233 ++++++++++++++++++ service/etcd/main.tf | 18 +- service/etcd/templates/etcd.service | 10 +- service/kubernetes/main.tf | 17 +- .../templates/master-configuration.yml | 3 + 6 files changed, 282 insertions(+), 14 deletions(-) create mode 100644 service/etcd/certs.tf diff --git a/main.tf b/main.tf index d080570..911a804 100644 --- a/main.tf +++ b/main.tf @@ -121,10 +121,13 @@ module "etcd" { module "kubernetes" { source = "./service/kubernetes" - count = "${var.node_count}" - connections = "${module.provider.public_ips}" - cluster_name = "${var.domain}" - vpn_interface = "${module.wireguard.vpn_interface}" - vpn_ips = "${module.wireguard.vpn_ips}" - etcd_endpoints = "${module.etcd.endpoints}" + count = "${var.node_count}" + connections = "${module.provider.public_ips}" + cluster_name = "${var.domain}" + vpn_interface = "${module.wireguard.vpn_interface}" + vpn_ips = "${module.wireguard.vpn_ips}" + etcd_endpoints = "${module.etcd.endpoints}" + etcd_ca_certificate = "${module.etcd.ca_certificate}" + etcd_client_certificate = "${module.etcd.client_certificate}" + etcd_client_key = "${module.etcd.client_key}" } diff --git a/service/etcd/certs.tf b/service/etcd/certs.tf new file mode 100644 index 0000000..279c21b --- /dev/null +++ b/service/etcd/certs.tf @@ -0,0 +1,233 @@ +variable "certificates_validity_hours" { + default = 87600 # 10 years +} + +variable "certificates_directory" { + default = "/etc/etcd" +} + +locals { + ca_certificate = "${var.certificates_directory}/etcd-ca.pem" + client_certificate = "${var.certificates_directory}/client.pem" + client_key = "${var.certificates_directory}/client.key" + server_certificate = "${var.certificates_directory}/server.pem" + server_key = "${var.certificates_directory}/server.key" + peer_certificate = "${var.certificates_directory}/peer.pem" + peer_key = "${var.certificates_directory}/peer.key" + + ip_addresses = ["127.0.0.1", "${var.vpn_ips}"] + dns_names = ["localhost", "${var.hostnames}"] +} + +resource "null_resource" "etcd-certs" { + count = "${var.count}" + + connection { + host = "${element(var.connections, count.index)}" + user = "root" + agent = true + } + + provisioner "remote-exec" { + inline = [ + "mkdir -p ${var.certificates_directory}", + ] + } + + provisioner "file" { + content = "${tls_self_signed_cert.etcd-ca.cert_pem}" + destination = "${local.ca_certificate}" + } + + provisioner "file" { + content = "${tls_locally_signed_cert.client.cert_pem}" + destination = "${local.client_certificate}" + } + + provisioner "file" { + content = "${tls_private_key.client.private_key_pem}" + destination = "${local.client_key}" + } + + provisioner "file" { + content = "${tls_locally_signed_cert.server.cert_pem}" + destination = "${local.server_certificate}" + } + + provisioner "file" { + content = "${tls_private_key.server.private_key_pem}" + destination = "${local.server_key}" + } + + provisioner "file" { + content = "${tls_locally_signed_cert.peer.cert_pem}" + destination = "${local.peer_certificate}" + } + + provisioner "file" { + content = "${tls_private_key.peer.private_key_pem}" + destination = "${local.peer_key}" + } + + provisioner "remote-exec" { + inline = [ + "chmod -R 600 ${var.certificates_directory}/*.key", + ] + } +} + +resource "tls_private_key" "etcd-ca" { + algorithm = "RSA" + rsa_bits = "2048" +} + +resource "tls_self_signed_cert" "etcd-ca" { + key_algorithm = "${tls_private_key.etcd-ca.algorithm}" + private_key_pem = "${tls_private_key.etcd-ca.private_key_pem}" + + subject { + common_name = "etcd-ca" + organization = "etcd" + } + + is_ca_certificate = true + validity_period_hours = "${var.certificates_validity_hours}" + + allowed_uses = [ + "key_encipherment", + "digital_signature", + "cert_signing", + ] +} + +resource "tls_private_key" "client" { + algorithm = "RSA" + rsa_bits = "2048" +} + +resource "tls_cert_request" "client" { + key_algorithm = "${tls_private_key.client.algorithm}" + private_key_pem = "${tls_private_key.client.private_key_pem}" + + subject { + common_name = "etcd-client" + organization = "etcd" + } + + ip_addresses = ["${local.ip_addresses}"] + dns_names = ["${local.dns_names}"] +} + +resource "tls_locally_signed_cert" "client" { + cert_request_pem = "${tls_cert_request.client.cert_request_pem}" + + ca_key_algorithm = "${join(" ", tls_self_signed_cert.etcd-ca.*.key_algorithm)}" + ca_private_key_pem = "${join(" ", tls_private_key.etcd-ca.*.private_key_pem)}" + ca_cert_pem = "${join(" ", tls_self_signed_cert.etcd-ca.*.cert_pem)}" + + validity_period_hours = "${var.certificates_validity_hours}" + + allowed_uses = [ + "key_encipherment", + "digital_signature", + "client_auth", + ] +} + +resource "tls_private_key" "server" { + algorithm = "RSA" + rsa_bits = "2048" +} + +resource "tls_cert_request" "server" { + key_algorithm = "${tls_private_key.server.algorithm}" + private_key_pem = "${tls_private_key.server.private_key_pem}" + + subject { + common_name = "etcd-server" + organization = "etcd" + } + + ip_addresses = ["${local.ip_addresses}"] + dns_names = ["${local.dns_names}"] +} + +resource "tls_locally_signed_cert" "server" { + cert_request_pem = "${tls_cert_request.server.cert_request_pem}" + + ca_key_algorithm = "${join(" ", tls_self_signed_cert.etcd-ca.*.key_algorithm)}" + ca_private_key_pem = "${join(" ", tls_private_key.etcd-ca.*.private_key_pem)}" + ca_cert_pem = "${join(" ", tls_self_signed_cert.etcd-ca.*.cert_pem)}" + + validity_period_hours = "${var.certificates_validity_hours}" + + allowed_uses = [ + "key_encipherment", + "digital_signature", + "client_auth", + "server_auth", + ] +} + +resource "tls_private_key" "peer" { + algorithm = "RSA" + rsa_bits = "2048" +} + +resource "tls_cert_request" "peer" { + key_algorithm = "${tls_private_key.peer.algorithm}" + private_key_pem = "${tls_private_key.peer.private_key_pem}" + + subject { + common_name = "etcd-peer" + organization = "etcd" + } + + ip_addresses = ["${local.ip_addresses}"] + dns_names = ["${local.dns_names}"] +} + +resource "tls_locally_signed_cert" "peer" { + cert_request_pem = "${tls_cert_request.peer.cert_request_pem}" + + ca_key_algorithm = "${join(" ", tls_self_signed_cert.etcd-ca.*.key_algorithm)}" + ca_private_key_pem = "${join(" ", tls_private_key.etcd-ca.*.private_key_pem)}" + ca_cert_pem = "${join(" ", tls_self_signed_cert.etcd-ca.*.cert_pem)}" + + validity_period_hours = "${var.certificates_validity_hours}" + + allowed_uses = [ + "key_encipherment", + "digital_signature", + "server_auth", + "client_auth", + ] +} + +output "ca_certificate" { + value = "${local.ca_certificate}" +} + +output "client_certificate" { + value = "${local.client_certificate}" +} + +output "client_key" { + value = "${local.client_key}" +} + +output "server_certificate" { + value = "${local.server_certificate}" +} + +output "server_key" { + value = "${local.server_key}" +} + +output "peer_certificate" { + value = "${local.peer_certificate}" +} + +output "peer_key" { + value = "${local.peer_key}" +} diff --git a/service/etcd/main.tf b/service/etcd/main.tf index 3198895..171dd18 100644 --- a/service/etcd/main.tf +++ b/service/etcd/main.tf @@ -21,7 +21,8 @@ variable "version" { } resource "null_resource" "etcd" { - count = "${var.count}" + depends_on = ["null_resource.etcd-certs"] + count = "${var.count}" triggers = { template = "${join("", data.template_file.etcd-service.*.rendered)}" @@ -59,10 +60,15 @@ data "template_file" "etcd-service" { vars { hostname = "${element(var.hostnames, count.index)}" - intial_cluster = "${join(",", formatlist("%s=http://%s:2380", var.hostnames, var.vpn_ips))}" - listen_client_urls = "http://${element(var.vpn_ips, count.index)}:2379" - advertise_client_urls = "http://${element(var.vpn_ips, count.index)}:2379" - listen_peer_urls = "http://${element(var.vpn_ips, count.index)}:2380" + intial_cluster = "${join(",", formatlist("%s=https://%s:2380", var.hostnames, var.vpn_ips))}" + listen_client_urls = "https://${element(var.vpn_ips, count.index)}:2379" + advertise_client_urls = "https://${element(var.vpn_ips, count.index)}:2379" + listen_peer_urls = "https://${element(var.vpn_ips, count.index)}:2380" + ca_certificate = "${local.ca_certificate}" + server_certificate = "${local.server_certificate}" + server_key = "${local.server_key}" + peer_certificate = "${local.peer_certificate}" + peer_key = "${local.peer_key}" vpn_unit = "${var.vpn_unit}" } } @@ -79,7 +85,7 @@ data "null_data_source" "endpoints" { depends_on = ["null_resource.etcd"] inputs = { - list = "${join(",", formatlist("http://%s:2379", var.vpn_ips))}" + list = "${join(",", formatlist("https://%s:2379", var.vpn_ips))}" } } diff --git a/service/etcd/templates/etcd.service b/service/etcd/templates/etcd.service index 9512ad0..df3817a 100644 --- a/service/etcd/templates/etcd.service +++ b/service/etcd/templates/etcd.service @@ -6,11 +6,19 @@ After=network.target ${vpn_unit} Type=notify ExecStart=/opt/etcd/etcd --name ${hostname} \ --data-dir /var/lib/etcd \ - --listen-client-urls "${listen_client_urls},http://localhost:2379" \ + --listen-client-urls "${listen_client_urls},https://localhost:2379" \ --advertise-client-urls "${advertise_client_urls}" \ --listen-peer-urls "${listen_peer_urls}" \ --initial-cluster "${intial_cluster}" \ --initial-advertise-peer-urls "${listen_peer_urls}" \ + --client-cert-auth=true \ + --trusted-ca-file="${ca_certificate}" \ + --cert-file="${server_certificate}" \ + --key-file="${server_key}" \ + --peer-client-cert-auth=true \ + --peer-trusted-ca-file="${ca_certificate}" \ + --peer-cert-file="${peer_certificate}" \ + --peer-key-file="${peer_key}" \ --heartbeat-interval 200 \ --election-timeout 5000 Restart=always diff --git a/service/kubernetes/main.tf b/service/kubernetes/main.tf index 7f96c08..2b73416 100644 --- a/service/kubernetes/main.tf +++ b/service/kubernetes/main.tf @@ -16,6 +16,18 @@ variable "etcd_endpoints" { type = "list" } +variable "etcd_ca_certificate" { + type = "string" +} + +variable "etcd_client_certificate" { + type = "string" +} + +variable "etcd_client_key" { + type = "string" +} + variable "overlay_interface" { default = "weave" } @@ -72,8 +84,11 @@ data "template_file" "master-configuration" { vars { api_advertise_addresses = "${element(var.vpn_ips, 0)}" - etcd_endpoints = "- ${join("\n - ", var.etcd_endpoints)}" cert_sans = "- ${element(var.connections, 0)}" + etcd_endpoints = "- ${join("\n - ", var.etcd_endpoints)}" + etcd_ca_certificate = "${var.etcd_ca_certificate}" + etcd_client_certificate = "${var.etcd_client_certificate}" + etcd_client_key = "${var.etcd_client_key}" } } diff --git a/service/kubernetes/templates/master-configuration.yml b/service/kubernetes/templates/master-configuration.yml index 431f561..86474e3 100644 --- a/service/kubernetes/templates/master-configuration.yml +++ b/service/kubernetes/templates/master-configuration.yml @@ -13,6 +13,9 @@ etcd: external: endpoints: ${etcd_endpoints} + caFile: ${etcd_ca_certificate} + certFile: ${etcd_client_certificate} + keyFile: ${etcd_client_key} --- apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration