Skip to content
This repository was archived by the owner on Jul 30, 2021. It is now read-only.

Commit 45ef63b

Browse files
authored
Merge pull request #433 from diegs/etcd-tls
Add support for generating and using etcd TLS assets.
2 parents ada4927 + 1af5409 commit 45ef63b

File tree

11 files changed

+268
-51
lines changed

11 files changed

+268
-51
lines changed

cmd/bootkube/render.go

Lines changed: 73 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ import (
1818
)
1919

2020
const (
21-
apiOffset = 1
22-
dnsOffset = 10
23-
etcdOffset = 15
24-
defaultServiceBaseIP = "10.3.0.0"
25-
defaultEtcdServers = "http://127.0.0.1:2379"
21+
apiOffset = 1
22+
dnsOffset = 10
23+
etcdOffset = 15
24+
defaultServiceBaseIP = "10.3.0.0"
25+
defaultEtcdServers = "https://127.0.0.1:2379"
26+
defaultSelfHostedEtcdServers = "http://127.0.0.1:2379"
2627
)
2728

2829
var (
@@ -36,17 +37,20 @@ var (
3637
}
3738

3839
renderOpts struct {
39-
assetDir string
40-
caCertificatePath string
41-
caPrivateKeyPath string
42-
etcdServers string
43-
apiServers string
44-
altNames string
45-
podCIDR string
46-
serviceCIDR string
47-
selfHostKubelet bool
48-
cloudProvider string
49-
selfHostedEtcd bool
40+
assetDir string
41+
caCertificatePath string
42+
caPrivateKeyPath string
43+
etcdCAPath string
44+
etcdCertificatePath string
45+
etcdPrivateKeyPath string
46+
etcdServers string
47+
apiServers string
48+
altNames string
49+
podCIDR string
50+
serviceCIDR string
51+
selfHostKubelet bool
52+
cloudProvider string
53+
selfHostedEtcd bool
5054
}
5155
)
5256

@@ -55,6 +59,9 @@ func init() {
5559
cmdRender.Flags().StringVar(&renderOpts.assetDir, "asset-dir", "", "Output path for rendered assets")
5660
cmdRender.Flags().StringVar(&renderOpts.caCertificatePath, "ca-certificate-path", "", "Path to an existing PEM encoded CA. If provided, TLS assets will be generated using this certificate authority.")
5761
cmdRender.Flags().StringVar(&renderOpts.caPrivateKeyPath, "ca-private-key-path", "", "Path to an existing Certificate Authority RSA private key. Required if --ca-certificate is set.")
62+
cmdRender.Flags().StringVar(&renderOpts.etcdCAPath, "etcd-ca-path", "", "Path to an existing PEM encoded CA that will be used for TLS-enabled communication between the apiserver and etcd. Must be used in conjunction with --etcd-certificate-path and --etcd-private-key-path, and must have etcd configured to use TLS with matching secrets.")
63+
cmdRender.Flags().StringVar(&renderOpts.etcdCertificatePath, "etcd-certificate-path", "", "Path to an existing certificate that will be used for TLS-enabled communication between the apiserver and etcd. Must be used in conjunction with --etcd-ca-path and --etcd-private-key-path, and must have etcd configured to use TLS with matching secrets.")
64+
cmdRender.Flags().StringVar(&renderOpts.etcdPrivateKeyPath, "etcd-private-key-path", "", "Path to an existing private key that will be used for TLS-enabled communication between the apiserver and etcd. Must be used in conjunction with --etcd-ca-path and --etcd-certificate-path, and must have etcd configured to use TLS with matching secrets.")
5865
cmdRender.Flags().StringVar(&renderOpts.etcdServers, "etcd-servers", defaultEtcdServers, "List of etcd servers URLs including host:port, comma separated")
5966
cmdRender.Flags().StringVar(&renderOpts.apiServers, "api-servers", "https://127.0.0.1:443", "List of API server URLs including host:port, commma seprated")
6067
cmdRender.Flags().StringVar(&renderOpts.altNames, "api-server-alt-names", "", "List of SANs to use in api-server certificate. Example: 'IP=127.0.0.1,IP=127.0.0.2,DNS=localhost'. If empty, SANs will be extracted from the --api-servers flag.")
@@ -86,6 +93,12 @@ func validateRenderOpts(cmd *cobra.Command, args []string) error {
8693
if renderOpts.caPrivateKeyPath != "" && renderOpts.caCertificatePath == "" {
8794
return errors.New("You must provide the --ca-certificate-path flag when --ca-private-key-path is provided.")
8895
}
96+
if (renderOpts.etcdCAPath != "" || renderOpts.etcdCertificatePath != "" || renderOpts.etcdPrivateKeyPath != "") && (renderOpts.etcdCAPath == "" || renderOpts.etcdCertificatePath == "" || renderOpts.etcdPrivateKeyPath == "") {
97+
return errors.New("You must specify either all or none of --etcd-ca-path, --etcd-certificate-path, and --etcd-private-key-path")
98+
}
99+
if renderOpts.etcdCertificatePath != "" && renderOpts.selfHostedEtcd {
100+
return errors.New("Cannot specify --etcd-certificate-path with --experimental-self-hosted-etcd")
101+
}
89102
if renderOpts.assetDir == "" {
90103
return errors.New("Missing required flag: --asset-dir")
91104
}
@@ -156,10 +169,8 @@ func flagsToAssetConfig() (c *asset.Config, err error) {
156169
if err != nil {
157170
return nil, err
158171
}
159-
160172
etcdServers = append(etcdServers, etcdServerUrl)
161-
162-
if renderOpts.etcdServers != defaultEtcdServers {
173+
if renderOpts.etcdServers != defaultSelfHostedEtcdServers {
163174
bootkube.UserOutput("--experimental-self-hosted-etcd and --service-cidr set. Overriding --etcd-servers setting with %s\n", etcdServers)
164175
}
165176
} else {
@@ -169,13 +180,44 @@ func flagsToAssetConfig() (c *asset.Config, err error) {
169180
}
170181
}
171182

183+
etcdUseTLS := false
184+
for _, url := range etcdServers {
185+
if url.Scheme == "https" {
186+
etcdUseTLS = true
187+
}
188+
}
189+
190+
var etcdCACert *x509.Certificate
191+
if renderOpts.etcdCAPath != "" {
192+
etcdCACert, err = parseCertFromDisk(renderOpts.etcdCAPath)
193+
if err != nil {
194+
return nil, err
195+
}
196+
}
197+
var etcdClientCert *x509.Certificate
198+
var etcdClientKey *rsa.PrivateKey
199+
if renderOpts.etcdCertificatePath != "" {
200+
etcdClientKey, etcdClientCert, err = parseCertAndPrivateKeyFromDisk(renderOpts.etcdCertificatePath, renderOpts.etcdPrivateKeyPath)
201+
if err != nil {
202+
return nil, err
203+
}
204+
}
205+
206+
if etcdUseTLS && etcdCACert == nil {
207+
bootkube.UserOutput("NOTE: --etcd-servers=%s but -etcd-ca-path, --etcd-certificate-path, and --etcd-private-key-path were not set. Bootkube will create etcd certificates under '%s/tls'. You must configure etcd to use these certificates before invoking 'bootkube run'.\n", renderOpts.etcdServers, renderOpts.assetDir)
208+
}
209+
172210
// TODO: Find better option than asking users to make manual changes
173211
if serviceNet.IP.String() != defaultServiceBaseIP {
174212
fmt.Printf("You have selected a non-default service CIDR %s - be sure your kubelet service file uses --cluster-dns=%s\n", serviceNet.String(), dnsServiceIP.String())
175213
}
176214

177215
return &asset.Config{
216+
EtcdCACert: etcdCACert,
217+
EtcdClientCert: etcdClientCert,
218+
EtcdClientKey: etcdClientKey,
178219
EtcdServers: etcdServers,
220+
EtcdUseTLS: etcdUseTLS,
179221
CACert: caCert,
180222
CAPrivKey: caPrivKey,
181223
APIServers: apiServers,
@@ -184,7 +226,7 @@ func flagsToAssetConfig() (c *asset.Config, err error) {
184226
ServiceCIDR: serviceNet,
185227
APIServiceIP: apiServiceIP,
186228
DNSServiceIP: dnsServiceIP,
187-
ETCDServiceIP: etcdServiceIP,
229+
EtcdServiceIP: etcdServiceIP,
188230
SelfHostKubelet: renderOpts.selfHostKubelet,
189231
CloudProvider: renderOpts.cloudProvider,
190232
SelfHostedEtcd: renderOpts.selfHostedEtcd,
@@ -202,15 +244,23 @@ func parseCertAndPrivateKeyFromDisk(caCertPath, privKeyPath string) (*rsa.Privat
202244
return nil, nil, fmt.Errorf("unable to parse CA private key: %v", err)
203245
}
204246
// Parse CA Cert.
247+
cert, err := parseCertFromDisk(caCertPath)
248+
if err != nil {
249+
return nil, nil, err
250+
}
251+
return key, cert, nil
252+
}
253+
254+
func parseCertFromDisk(caCertPath string) (*x509.Certificate, error) {
205255
capem, err := ioutil.ReadFile(caCertPath)
206256
if err != nil {
207-
return nil, nil, fmt.Errorf("error reading ca cert file at %s: %v", caCertPath, err)
257+
return nil, fmt.Errorf("error reading ca cert file at %s: %v", caCertPath, err)
208258
}
209259
cert, err := tlsutil.ParsePEMEncodedCACert(capem)
210260
if err != nil {
211-
return nil, nil, fmt.Errorf("unable to parse CA Cert: %v", err)
261+
return nil, fmt.Errorf("unable to parse CA Cert: %v", err)
212262
}
213-
return key, cert, nil
263+
return cert, nil
214264
}
215265

216266
func parseURLs(s string) ([]*url.URL, error) {

hack/multi-node/Vagrantfile

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ CONTROLLER_USER_DATA_PATH = File.expand_path("./cluster/user-data-controller")
2222
WORKER_USER_DATA_PATH = File.expand_path("./cluster/user-data-worker")
2323
KUBECONFIG_PATH = File.expand_path("cluster/auth/kubeconfig")
2424
CA_CERT_PATH = File.expand_path("cluster/tls/ca.crt")
25+
ETCD_CERT_GLOB = File.expand_path("cluster/tls/etcd-*")
2526

2627
def etcdIP(num)
2728
return "172.17.4.#{num+50}"
@@ -41,7 +42,7 @@ if !$self_host_etcd
4142
$etcd_vm_memory = 512
4243
ETCD_CLOUD_CONFIG_PATH = File.expand_path("./etcd-cloud-config.yaml")
4344
etcdIPs = [*1..$etcd_count].map{ |i| etcdIP(i) }
44-
initial_etcd_cluster = etcdIPs.map.with_index{ |ip, i| "e#{i+1}=http://#{ip}:2380" }.join(",")
45+
initial_etcd_cluster = etcdIPs.map.with_index{ |ip, i| "e#{i+1}=https://#{ip}:2380" }.join(",")
4546
end
4647

4748
Vagrant.configure("2") do |config|
@@ -109,6 +110,14 @@ Vagrant.configure("2") do |config|
109110

110111
etcd.vm.provision :file, source: etcd_config_file.path, destination: "/tmp/vagrantfile-user-data"
111112
etcd.vm.provision :shell, inline: "mv /tmp/vagrantfile-user-data /var/lib/coreos-vagrant/", privileged: true
113+
114+
etcd.vm.provision :shell, :inline => "mkdir -p /etc/etcd/tls", :privileged => true
115+
Dir.glob(ETCD_CERT_GLOB) do |etcd_cert_file|
116+
etcd.vm.provision :file, :source => etcd_cert_file, :destination => "/tmp/#{File.basename(etcd_cert_file)}"
117+
etcd.vm.provision :shell, :inline => "mv /tmp/#{File.basename(etcd_cert_file)} /etc/etcd/tls/", :privileged => true
118+
end
119+
etcd.vm.provision :shell, :inline => "chown -R etcd:etcd /etc/etcd", :privileged => true
120+
etcd.vm.provision :shell, :inline => "chmod -R u=rX,g=,o= /etc/etcd", :privileged => true
112121
end
113122
end
114123
end

hack/multi-node/bootkube-up

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ if [ ${SELF_HOST_ETCD} = "true" ]; then
1414
echo "WARNING: THIS IS NOT YET FULLY WORKING - merely here to make ongoing testing easier"
1515
etcd_render_flags="--experimental-self-hosted-etcd"
1616
else
17-
etcd_render_flags="--etcd-servers=http://172.17.4.51:2379"
17+
# Note: if you increase the number of etcd servers in the Vagrantfile you must also add them here.
18+
etcd_render_flags="--etcd-servers=https://172.17.4.51:2379"
1819
fi
1920

2021
# Render assets

hack/multi-node/etcd-cloud-config.yaml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,16 @@ coreos:
1515
[Service]
1616
Environment="ETCD_IMAGE_TAG=v3.1.0"
1717
Environment="ETCD_NAME={{ETCD_NODE_NAME}}"
18-
Environment="ETCD_ADVERTISE_CLIENT_URLS=http://$private_ipv4:2379"
19-
Environment="ETCD_INITIAL_ADVERTISE_PEER_URLS=http://$private_ipv4:2380"
20-
Environment="ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379"
21-
Environment="ETCD_LISTEN_PEER_URLS=http://$private_ipv4:2380"
18+
Environment="ETCD_ADVERTISE_CLIENT_URLS=https://$private_ipv4:2379"
19+
Environment="ETCD_INITIAL_ADVERTISE_PEER_URLS=https://$private_ipv4:2380"
20+
Environment="ETCD_LISTEN_CLIENT_URLS=https://0.0.0.0:2379"
21+
Environment="ETCD_LISTEN_PEER_URLS=https://$private_ipv4:2380"
2222
Environment="ETCD_INITIAL_CLUSTER={{ETCD_INITIAL_CLUSTER}}"
23+
Environment="ETCD_SSL_DIR=/etc/etcd/tls"
24+
Environment="ETCD_TRUSTED_CA_FILE=/etc/ssl/certs/etcd-ca.crt"
25+
Environment="ETCD_CERT_FILE=/etc/ssl/certs/etcd-client.crt"
26+
Environment="ETCD_KEY_FILE=/etc/ssl/certs/etcd-client.key"
27+
Environment="ETCD_CLIENT_CERT_AUTH=true"
28+
Environment="ETCD_PEER_TRUSTED_CA_FILE=/etc/ssl/certs/etcd-ca.crt"
29+
Environment="ETCD_PEER_CERT_FILE=/etc/ssl/certs/etcd-peer.crt"
30+
Environment="ETCD_PEER_KEY_FILE=/etc/ssl/certs/etcd-peer.key"

hack/quickstart/init-master.sh

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,28 @@ function usage() {
1616

1717
function configure_etcd() {
1818
[ -f "/etc/systemd/system/etcd-member.service.d/10-etcd-member.conf" ] || {
19+
mkdir -p /etc/etcd/tls
20+
cp /home/core/assets/tls/etcd* /etc/etcd/tls
21+
chown -R etcd:etcd /etc/etcd
22+
chmod -R u=rX,g=,o= /etc/etcd
1923
mkdir -p /etc/systemd/system/etcd-member.service.d
2024
cat << EOF > /etc/systemd/system/etcd-member.service.d/10-etcd-member.conf
2125
[Service]
2226
Environment="ETCD_IMAGE_TAG=v3.1.0"
2327
Environment="ETCD_NAME=controller"
24-
Environment="ETCD_INITIAL_CLUSTER=controller=http://${COREOS_PRIVATE_IPV4}:2380"
25-
Environment="ETCD_INITIAL_ADVERTISE_PEER_URLS=http://${COREOS_PRIVATE_IPV4}:2380"
26-
Environment="ETCD_ADVERTISE_CLIENT_URLS=http://${COREOS_PRIVATE_IPV4}:2379"
27-
Environment="ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379"
28-
Environment="ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380"
28+
Environment="ETCD_INITIAL_CLUSTER=controller=https://${COREOS_PRIVATE_IPV4}:2380"
29+
Environment="ETCD_INITIAL_ADVERTISE_PEER_URLS=https://${COREOS_PRIVATE_IPV4}:2380"
30+
Environment="ETCD_ADVERTISE_CLIENT_URLS=https://${COREOS_PRIVATE_IPV4}:2379"
31+
Environment="ETCD_LISTEN_CLIENT_URLS=https://0.0.0.0:2379"
32+
Environment="ETCD_LISTEN_PEER_URLS=https://0.0.0.0:2380"
33+
Environment="ETCD_SSL_DIR=/etc/etcd/tls"
34+
Environment="ETCD_TRUSTED_CA_FILE=/etc/ssl/certs/etcd-ca.crt"
35+
Environment="ETCD_CERT_FILE=/etc/ssl/certs/etcd-client.crt"
36+
Environment="ETCD_KEY_FILE=/etc/ssl/certs/etcd-client.key"
37+
Environment="ETCD_CLIENT_CERT_AUTH=true"
38+
Environment="ETCD_PEER_TRUSTED_CA_FILE=/etc/ssl/certs/etcd-ca.crt"
39+
Environment="ETCD_PEER_CERT_FILE=/etc/ssl/certs/etcd-peer.crt"
40+
Environment="ETCD_PEER_KEY_FILE=/etc/ssl/certs/etcd-peer.key"
2941
EOF
3042
}
3143
}
@@ -35,26 +47,29 @@ function init_master_node() {
3547
systemctl daemon-reload
3648
systemctl stop update-engine; systemctl mask update-engine
3749

38-
etcd_render_flags=""
39-
40-
# Start etcd.
4150
if [ "$SELF_HOST_ETCD" = true ] ; then
4251
echo "WARNING: THIS IS NOT YET FULLY WORKING - merely here to make ongoing testing easier"
4352
etcd_render_flags="--experimental-self-hosted-etcd"
4453
else
45-
configure_etcd
46-
systemctl enable etcd-member; sudo systemctl start etcd-member
54+
etcd_render_flags="--etcd-servers=https://${COREOS_PRIVATE_IPV4}:2379"
4755
fi
4856

4957
# Render cluster assets
50-
/home/core/bootkube render --asset-dir=/home/core/assets --api-servers=https://${COREOS_PUBLIC_IPV4}:443,https://${COREOS_PRIVATE_IPV4}:443 ${etcd_render_flags}
58+
/home/core/bootkube render --asset-dir=/home/core/assets ${etcd_render_flags} \
59+
--api-servers=https://${COREOS_PUBLIC_IPV4}:443,https://${COREOS_PRIVATE_IPV4}:443
5160

5261
# Move the local kubeconfig into expected location
5362
chown -R core:core /home/core/assets
5463
mkdir -p /etc/kubernetes
5564
cp /home/core/assets/auth/kubeconfig /etc/kubernetes/
5665
cp /home/core/assets/tls/ca.crt /etc/kubernetes/ca.crt
5766

67+
# Start etcd.
68+
if [ "$SELF_HOST_ETCD" = false ] ; then
69+
configure_etcd
70+
systemctl enable etcd-member; sudo systemctl start etcd-member
71+
fi
72+
5873
# Start the kubelet
5974
systemctl enable kubelet; sudo systemctl start kubelet
6075

hack/single-node/Vagrantfile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ NODE_IP = "172.17.4.100"
1414
USER_DATA_PATH = File.expand_path("cluster/user-data")
1515
KUBECONFIG_PATH = File.expand_path("cluster/auth/kubeconfig")
1616
CA_CERT_PATH = File.expand_path("cluster/tls/ca.crt")
17+
ETCD_CERT_GLOB = File.expand_path("cluster/tls/etcd-*")
1718

1819
Vagrant.configure("2") do |config|
1920
# always use Vagrant's insecure key
@@ -61,4 +62,12 @@ Vagrant.configure("2") do |config|
6162

6263
config.vm.provision :file, :source => CA_CERT_PATH, :destination => "/tmp/ca.crt"
6364
config.vm.provision :shell, :inline => "mv /tmp/ca.crt /etc/kubernetes/ca.crt", :privileged => true
65+
66+
config.vm.provision :shell, :inline => "mkdir -p /etc/etcd/tls", :privileged => true
67+
Dir.glob(ETCD_CERT_GLOB) do |etcd_cert_file|
68+
config.vm.provision :file, :source => etcd_cert_file, :destination => "/tmp/#{File.basename(etcd_cert_file)}"
69+
config.vm.provision :shell, :inline => "mv /tmp/#{File.basename(etcd_cert_file)} /etc/etcd/tls/", :privileged => true
70+
end
71+
config.vm.provision :shell, :inline => "chown -R etcd:etcd /etc/etcd", :privileged => true
72+
config.vm.provision :shell, :inline => "chmod -R u=rX,g=,o= /etc/etcd", :privileged => true
6473
end

hack/single-node/user-data-etcd.sample

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,18 @@
55
content: |
66
[Service]
77
Environment="ETCD_IMAGE_TAG=v3.1.0"
8+
Environment="ETCD_NAME=default"
9+
Environment="ETCD_INITIAL_CLUSTER=default=https://127.0.0.1:2380"
10+
Environment="ETCD_INITIAL_ADVERTISE_PEER_URLS=https://127.0.0.1:2380"
11+
Environment="ETCD_ADVERTISE_CLIENT_URLS=https://127.0.0.1:2379"
12+
Environment="ETCD_LISTEN_CLIENT_URLS=https://0.0.0.0:2379"
13+
Environment="ETCD_LISTEN_PEER_URLS=https://0.0.0.0:2380"
14+
Environment="ETCD_SSL_DIR=/etc/etcd/tls"
15+
Environment="ETCD_TRUSTED_CA_FILE=/etc/ssl/certs/etcd-ca.crt"
16+
Environment="ETCD_CERT_FILE=/etc/ssl/certs/etcd-client.crt"
17+
Environment="ETCD_KEY_FILE=/etc/ssl/certs/etcd-client.key"
18+
Environment="ETCD_CLIENT_CERT_AUTH=true"
19+
Environment="ETCD_PEER_TRUSTED_CA_FILE=/etc/ssl/certs/etcd-ca.crt"
20+
Environment="ETCD_PEER_CERT_FILE=/etc/ssl/certs/etcd-peer.crt"
21+
Environment="ETCD_PEER_KEY_FILE=/etc/ssl/certs/etcd-peer.key"
822
command: start

0 commit comments

Comments
 (0)