Skip to content

Commit 945b729

Browse files
committed
secure etcd
1 parent 157c302 commit 945b729

File tree

6 files changed

+282
-14
lines changed

6 files changed

+282
-14
lines changed

main.tf

+9-6
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,13 @@ module "etcd" {
121121
module "kubernetes" {
122122
source = "./service/kubernetes"
123123

124-
count = "${var.node_count}"
125-
connections = "${module.provider.public_ips}"
126-
cluster_name = "${var.domain}"
127-
vpn_interface = "${module.wireguard.vpn_interface}"
128-
vpn_ips = "${module.wireguard.vpn_ips}"
129-
etcd_endpoints = "${module.etcd.endpoints}"
124+
count = "${var.node_count}"
125+
connections = "${module.provider.public_ips}"
126+
cluster_name = "${var.domain}"
127+
vpn_interface = "${module.wireguard.vpn_interface}"
128+
vpn_ips = "${module.wireguard.vpn_ips}"
129+
etcd_endpoints = "${module.etcd.endpoints}"
130+
etcd_ca_certificate = "${module.etcd.ca_certificate}"
131+
etcd_client_certificate = "${module.etcd.client_certificate}"
132+
etcd_client_key = "${module.etcd.client_key}"
130133
}

service/etcd/certs.tf

+233
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
variable "certificates_validity_hours" {
2+
default = 87600 # 10 years
3+
}
4+
5+
variable "certificates_directory" {
6+
default = "/etc/etcd"
7+
}
8+
9+
locals {
10+
ca_certificate = "${var.certificates_directory}/etcd-ca.pem"
11+
client_certificate = "${var.certificates_directory}/client.pem"
12+
client_key = "${var.certificates_directory}/client.key"
13+
server_certificate = "${var.certificates_directory}/server.pem"
14+
server_key = "${var.certificates_directory}/server.key"
15+
peer_certificate = "${var.certificates_directory}/peer.pem"
16+
peer_key = "${var.certificates_directory}/peer.key"
17+
18+
ip_addresses = ["127.0.0.1", "${var.vpn_ips}"]
19+
dns_names = ["localhost", "${var.hostnames}", "${var.vpn_ips}"]
20+
}
21+
22+
resource "null_resource" "etcd-certs" {
23+
count = "${var.count}"
24+
25+
connection {
26+
host = "${element(var.connections, count.index)}"
27+
user = "root"
28+
agent = true
29+
}
30+
31+
provisioner "remote-exec" {
32+
inline = [
33+
"mkdir -p ${var.certificates_directory}",
34+
]
35+
}
36+
37+
provisioner "file" {
38+
content = "${tls_self_signed_cert.etcd-ca.cert_pem}"
39+
destination = "${local.ca_certificate}"
40+
}
41+
42+
provisioner "file" {
43+
content = "${tls_locally_signed_cert.client.cert_pem}"
44+
destination = "${local.client_certificate}"
45+
}
46+
47+
provisioner "file" {
48+
content = "${tls_private_key.client.private_key_pem}"
49+
destination = "${local.client_key}"
50+
}
51+
52+
provisioner "file" {
53+
content = "${tls_locally_signed_cert.server.cert_pem}"
54+
destination = "${local.server_certificate}"
55+
}
56+
57+
provisioner "file" {
58+
content = "${tls_private_key.server.private_key_pem}"
59+
destination = "${local.server_key}"
60+
}
61+
62+
provisioner "file" {
63+
content = "${tls_locally_signed_cert.peer.cert_pem}"
64+
destination = "${local.peer_certificate}"
65+
}
66+
67+
provisioner "file" {
68+
content = "${tls_private_key.peer.private_key_pem}"
69+
destination = "${local.peer_key}"
70+
}
71+
72+
provisioner "remote-exec" {
73+
inline = [
74+
"chmod -R 600 ${var.certificates_directory}/*.key",
75+
]
76+
}
77+
}
78+
79+
resource "tls_private_key" "etcd-ca" {
80+
algorithm = "RSA"
81+
rsa_bits = "2048"
82+
}
83+
84+
resource "tls_self_signed_cert" "etcd-ca" {
85+
key_algorithm = "${tls_private_key.etcd-ca.algorithm}"
86+
private_key_pem = "${tls_private_key.etcd-ca.private_key_pem}"
87+
88+
subject {
89+
common_name = "etcd-ca"
90+
organization = "etcd"
91+
}
92+
93+
is_ca_certificate = true
94+
validity_period_hours = "${var.certificates_validity_hours}"
95+
96+
allowed_uses = [
97+
"key_encipherment",
98+
"digital_signature",
99+
"cert_signing",
100+
]
101+
}
102+
103+
resource "tls_private_key" "client" {
104+
algorithm = "RSA"
105+
rsa_bits = "2048"
106+
}
107+
108+
resource "tls_cert_request" "client" {
109+
key_algorithm = "${tls_private_key.client.algorithm}"
110+
private_key_pem = "${tls_private_key.client.private_key_pem}"
111+
112+
subject {
113+
common_name = "etcd-client"
114+
organization = "etcd"
115+
}
116+
117+
ip_addresses = ["${local.ip_addresses}"]
118+
dns_names = ["${local.dns_names}"]
119+
}
120+
121+
resource "tls_locally_signed_cert" "client" {
122+
cert_request_pem = "${tls_cert_request.client.cert_request_pem}"
123+
124+
ca_key_algorithm = "${join(" ", tls_self_signed_cert.etcd-ca.*.key_algorithm)}"
125+
ca_private_key_pem = "${join(" ", tls_private_key.etcd-ca.*.private_key_pem)}"
126+
ca_cert_pem = "${join(" ", tls_self_signed_cert.etcd-ca.*.cert_pem)}"
127+
128+
validity_period_hours = "${var.certificates_validity_hours}"
129+
130+
allowed_uses = [
131+
"key_encipherment",
132+
"digital_signature",
133+
"client_auth",
134+
]
135+
}
136+
137+
resource "tls_private_key" "server" {
138+
algorithm = "RSA"
139+
rsa_bits = "2048"
140+
}
141+
142+
resource "tls_cert_request" "server" {
143+
key_algorithm = "${tls_private_key.server.algorithm}"
144+
private_key_pem = "${tls_private_key.server.private_key_pem}"
145+
146+
subject {
147+
common_name = "etcd-server"
148+
organization = "etcd"
149+
}
150+
151+
ip_addresses = ["${local.ip_addresses}"]
152+
dns_names = ["${local.dns_names}"]
153+
}
154+
155+
resource "tls_locally_signed_cert" "server" {
156+
cert_request_pem = "${tls_cert_request.server.cert_request_pem}"
157+
158+
ca_key_algorithm = "${join(" ", tls_self_signed_cert.etcd-ca.*.key_algorithm)}"
159+
ca_private_key_pem = "${join(" ", tls_private_key.etcd-ca.*.private_key_pem)}"
160+
ca_cert_pem = "${join(" ", tls_self_signed_cert.etcd-ca.*.cert_pem)}"
161+
162+
validity_period_hours = "${var.certificates_validity_hours}"
163+
164+
allowed_uses = [
165+
"key_encipherment",
166+
"digital_signature",
167+
"client_auth",
168+
"server_auth",
169+
]
170+
}
171+
172+
resource "tls_private_key" "peer" {
173+
algorithm = "RSA"
174+
rsa_bits = "2048"
175+
}
176+
177+
resource "tls_cert_request" "peer" {
178+
key_algorithm = "${tls_private_key.peer.algorithm}"
179+
private_key_pem = "${tls_private_key.peer.private_key_pem}"
180+
181+
subject {
182+
common_name = "etcd-peer"
183+
organization = "etcd"
184+
}
185+
186+
ip_addresses = ["${local.ip_addresses}"]
187+
dns_names = ["${local.dns_names}"]
188+
}
189+
190+
resource "tls_locally_signed_cert" "peer" {
191+
cert_request_pem = "${tls_cert_request.peer.cert_request_pem}"
192+
193+
ca_key_algorithm = "${join(" ", tls_self_signed_cert.etcd-ca.*.key_algorithm)}"
194+
ca_private_key_pem = "${join(" ", tls_private_key.etcd-ca.*.private_key_pem)}"
195+
ca_cert_pem = "${join(" ", tls_self_signed_cert.etcd-ca.*.cert_pem)}"
196+
197+
validity_period_hours = "${var.certificates_validity_hours}"
198+
199+
allowed_uses = [
200+
"key_encipherment",
201+
"digital_signature",
202+
"server_auth",
203+
"client_auth",
204+
]
205+
}
206+
207+
output "ca_certificate" {
208+
value = "${local.ca_certificate}"
209+
}
210+
211+
output "client_certificate" {
212+
value = "${local.client_certificate}"
213+
}
214+
215+
output "client_key" {
216+
value = "${local.client_key}"
217+
}
218+
219+
output "server_certificate" {
220+
value = "${local.server_certificate}"
221+
}
222+
223+
output "server_key" {
224+
value = "${local.server_key}"
225+
}
226+
227+
output "peer_certificate" {
228+
value = "${local.peer_certificate}"
229+
}
230+
231+
output "peer_key" {
232+
value = "${local.peer_key}"
233+
}

service/etcd/main.tf

+12-6
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ variable "version" {
2121
}
2222

2323
resource "null_resource" "etcd" {
24-
count = "${var.count}"
24+
depends_on = ["null_resource.etcd-certs"]
25+
count = "${var.count}"
2526

2627
triggers = {
2728
template = "${join("", data.template_file.etcd-service.*.rendered)}"
@@ -59,10 +60,15 @@ data "template_file" "etcd-service" {
5960

6061
vars {
6162
hostname = "${element(var.hostnames, count.index)}"
62-
intial_cluster = "${join(",", formatlist("%s=http://%s:2380", var.hostnames, var.vpn_ips))}"
63-
listen_client_urls = "http://${element(var.vpn_ips, count.index)}:2379"
64-
advertise_client_urls = "http://${element(var.vpn_ips, count.index)}:2379"
65-
listen_peer_urls = "http://${element(var.vpn_ips, count.index)}:2380"
63+
intial_cluster = "${join(",", formatlist("%s=https://%s:2380", var.hostnames, var.vpn_ips))}"
64+
listen_client_urls = "https://${element(var.vpn_ips, count.index)}:2379"
65+
advertise_client_urls = "https://${element(var.vpn_ips, count.index)}:2379"
66+
listen_peer_urls = "https://${element(var.vpn_ips, count.index)}:2380"
67+
ca_certificate = "${local.ca_certificate}"
68+
server_certificate = "${local.server_certificate}"
69+
server_key = "${local.server_key}"
70+
peer_certificate = "${local.peer_certificate}"
71+
peer_key = "${local.peer_key}"
6672
vpn_unit = "${var.vpn_unit}"
6773
}
6874
}
@@ -79,7 +85,7 @@ data "null_data_source" "endpoints" {
7985
depends_on = ["null_resource.etcd"]
8086

8187
inputs = {
82-
list = "${join(",", formatlist("http://%s:2379", var.vpn_ips))}"
88+
list = "${join(",", formatlist("https://%s:2379", var.vpn_ips))}"
8389
}
8490
}
8591

service/etcd/templates/etcd.service

+9-1
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,19 @@ After=network.target ${vpn_unit}
66
Type=notify
77
ExecStart=/opt/etcd/etcd --name ${hostname} \
88
--data-dir /var/lib/etcd \
9-
--listen-client-urls "${listen_client_urls},http://localhost:2379" \
9+
--listen-client-urls "${listen_client_urls},https://localhost:2379" \
1010
--advertise-client-urls "${advertise_client_urls}" \
1111
--listen-peer-urls "${listen_peer_urls}" \
1212
--initial-cluster "${intial_cluster}" \
1313
--initial-advertise-peer-urls "${listen_peer_urls}" \
14+
--client-cert-auth=true \
15+
--trusted-ca-file="${ca_certificate}" \
16+
--cert-file="${server_certificate}" \
17+
--key-file="${server_key}" \
18+
--peer-client-cert-auth=true \
19+
--peer-trusted-ca-file="${ca_certificate}" \
20+
--peer-cert-file="${peer_certificate}" \
21+
--peer-key-file="${peer_key}" \
1422
--heartbeat-interval 200 \
1523
--election-timeout 5000
1624
Restart=always

service/kubernetes/main.tf

+16-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ variable "etcd_endpoints" {
1616
type = "list"
1717
}
1818

19+
variable "etcd_ca_certificate" {
20+
type = "string"
21+
}
22+
23+
variable "etcd_client_certificate" {
24+
type = "string"
25+
}
26+
27+
variable "etcd_client_key" {
28+
type = "string"
29+
}
30+
1931
variable "overlay_interface" {
2032
default = "weave"
2133
}
@@ -72,8 +84,11 @@ data "template_file" "master-configuration" {
7284

7385
vars {
7486
api_advertise_addresses = "${element(var.vpn_ips, 0)}"
75-
etcd_endpoints = "- ${join("\n - ", var.etcd_endpoints)}"
7687
cert_sans = "- ${element(var.connections, 0)}"
88+
etcd_endpoints = "- ${join("\n - ", var.etcd_endpoints)}"
89+
etcd_ca_certificate = "${var.etcd_ca_certificate}"
90+
etcd_client_certificate = "${var.etcd_client_certificate}"
91+
etcd_client_key = "${var.etcd_client_key}"
7792
}
7893
}
7994

service/kubernetes/templates/master-configuration.yml

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ etcd:
1313
external:
1414
endpoints:
1515
${etcd_endpoints}
16+
caFile: ${etcd_ca_certificate}
17+
certFile: ${etcd_client_certificate}
18+
keyFile: ${etcd_client_key}
1619
---
1720
apiVersion: kubelet.config.k8s.io/v1beta1
1821
kind: KubeletConfiguration

0 commit comments

Comments
 (0)