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

Commit 558544c

Browse files
author
Diego Pontoriero
committed
Add support for generating and using etcd TLS assets.
This change adds rendering options to allow the apiserver to connect to etcd using TLS. This applies to both the temporary and self-hosted control plane. There are also some options (mostly intended for development) to help generate the etcd (client/server) certificates. Obviously this is only useful if etcd is not already up. Self-hosted etcd is not supported at this time.
1 parent 32ca215 commit 558544c

File tree

6 files changed

+199
-36
lines changed

6 files changed

+199
-36
lines changed

cmd/bootkube/render.go

Lines changed: 77 additions & 19 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 = "http://127.0.0.1:2379"
26+
defaultEtcdTLSServers = "https://127.0.0.1:2379"
2627
)
2728

2829
var (
@@ -36,17 +37,21 @@ 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+
etcdUseTLS bool
48+
apiServers string
49+
altNames string
50+
podCIDR string
51+
serviceCIDR string
52+
selfHostKubelet bool
53+
cloudProvider string
54+
selfHostedEtcd bool
5055
}
5156
)
5257

@@ -55,6 +60,9 @@ func init() {
5560
cmdRender.Flags().StringVar(&renderOpts.assetDir, "asset-dir", "", "Output path for rendered assets")
5661
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.")
5762
cmdRender.Flags().StringVar(&renderOpts.caPrivateKeyPath, "ca-private-key-path", "", "Path to an existing Certificate Authority RSA private key. Required if --ca-certificate is set.")
63+
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.")
64+
cmdRender.Flags().StringVar(&renderOpts.etcdCertificatePath, "etcd-certificate-path", "", "Path to an existing server 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.")
65+
cmdRender.Flags().StringVar(&renderOpts.etcdPrivateKeyPath, "etcd-private-key-path", "", "Path to an existing server 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.")
5866
cmdRender.Flags().StringVar(&renderOpts.etcdServers, "etcd-servers", defaultEtcdServers, "List of etcd servers URLs including host:port, comma separated")
5967
cmdRender.Flags().StringVar(&renderOpts.apiServers, "api-servers", "https://127.0.0.1:443", "List of API server URLs including host:port, commma seprated")
6068
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.")
@@ -63,6 +71,7 @@ func init() {
6371
cmdRender.Flags().BoolVar(&renderOpts.selfHostKubelet, "experimental-self-hosted-kubelet", false, "(Experimental) Create a self-hosted kubelet daemonset.")
6472
cmdRender.Flags().StringVar(&renderOpts.cloudProvider, "cloud-provider", "", "The provider for cloud services. Empty string for no provider")
6573
cmdRender.Flags().BoolVar(&renderOpts.selfHostedEtcd, "experimental-self-hosted-etcd", false, "(Experimental) Create self-hosted etcd assets.")
74+
cmdRender.Flags().BoolVar(&renderOpts.etcdUseTLS, "etcd-use-tls", false, "If true, uses TLS for etcd. Implicitly true if --etcd-ca-path,--etcd-certificate-path,--etcd-private-key-path are set. If true but those flags are not set etcd TLS certificates will be generated. Not supported if --experimental-self-hosted-etcd=true.")
6675
}
6776

6877
func runCmdRender(cmd *cobra.Command, args []string) error {
@@ -86,6 +95,16 @@ func validateRenderOpts(cmd *cobra.Command, args []string) error {
8695
if renderOpts.caPrivateKeyPath != "" && renderOpts.caCertificatePath == "" {
8796
return errors.New("You must provide the --ca-certificate-path flag when --ca-private-key-path is provided.")
8897
}
98+
if (renderOpts.etcdCAPath != "" || renderOpts.etcdCertificatePath != "" || renderOpts.etcdPrivateKeyPath != "") && (renderOpts.etcdCAPath == "" || renderOpts.etcdCertificatePath == "" || renderOpts.etcdPrivateKeyPath == "") {
99+
return errors.New("You must specify either all or none of --etcd-ca-path, --etcd-certificate-path, and --etcd-private-key-path")
100+
}
101+
if renderOpts.etcdCAPath != "" && !renderOpts.etcdUseTLS {
102+
bootkube.UserOutput("etcd TLS certificates specified. Overriding --etcd-use-tls=true\n")
103+
renderOpts.etcdUseTLS = true
104+
}
105+
if renderOpts.etcdUseTLS && renderOpts.selfHostedEtcd {
106+
return errors.New("Cannot use --etcd-use-tls with --experimental-self-hosted-etcd")
107+
}
89108
if renderOpts.assetDir == "" {
90109
return errors.New("Missing required flag: --asset-dir")
91110
}
@@ -163,19 +182,50 @@ func flagsToAssetConfig() (c *asset.Config, err error) {
163182
bootkube.UserOutput("--experimental-self-hosted-etcd and --service-cidr set. Overriding --etcd-servers setting with %s\n", etcdServers)
164183
}
165184
} else {
185+
if renderOpts.etcdUseTLS && renderOpts.etcdServers == defaultEtcdServers {
186+
renderOpts.etcdServers = defaultEtcdTLSServers
187+
}
166188
etcdServers, err = parseURLs(renderOpts.etcdServers)
167189
if err != nil {
168190
return nil, err
169191
}
170192
}
171193

194+
if renderOpts.etcdUseTLS {
195+
for _, url := range etcdServers {
196+
if url.Scheme != "https" {
197+
return nil, fmt.Errorf("--etcd-use-tls=true but insecure etcd server endpoint specified: %s\n", url)
198+
}
199+
}
200+
}
201+
202+
var etcdCACert *x509.Certificate
203+
if renderOpts.etcdCAPath != "" {
204+
etcdCACert, err = parseCertFromDisk(renderOpts.etcdCAPath)
205+
if err != nil {
206+
return nil, err
207+
}
208+
}
209+
var etcdServerCert *x509.Certificate
210+
var etcdServerKey *rsa.PrivateKey
211+
if renderOpts.etcdCertificatePath != "" {
212+
etcdServerKey, etcdServerCert, err = parseCertAndPrivateKeyFromDisk(renderOpts.etcdCertificatePath, renderOpts.etcdPrivateKeyPath)
213+
if err != nil {
214+
return nil, err
215+
}
216+
}
217+
172218
// TODO: Find better option than asking users to make manual changes
173219
if serviceNet.IP.String() != defaultServiceBaseIP {
174220
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())
175221
}
176222

177223
return &asset.Config{
224+
EtcdCACert: etcdCACert,
225+
EtcdServerCert: etcdServerCert,
226+
EtcdServerKey: etcdServerKey,
178227
EtcdServers: etcdServers,
228+
EtcdUseTLS: renderOpts.etcdUseTLS,
179229
CACert: caCert,
180230
CAPrivKey: caPrivKey,
181231
APIServers: apiServers,
@@ -202,15 +252,23 @@ func parseCertAndPrivateKeyFromDisk(caCertPath, privKeyPath string) (*rsa.Privat
202252
return nil, nil, fmt.Errorf("unable to parse CA private key: %v", err)
203253
}
204254
// Parse CA Cert.
255+
cert, err := parseCertFromDisk(caCertPath)
256+
if err != nil {
257+
return nil, nil, err
258+
}
259+
return key, cert, nil
260+
}
261+
262+
func parseCertFromDisk(caCertPath string) (*x509.Certificate, error) {
205263
capem, err := ioutil.ReadFile(caCertPath)
206264
if err != nil {
207-
return nil, nil, fmt.Errorf("error reading ca cert file at %s: %v", caCertPath, err)
265+
return nil, fmt.Errorf("error reading ca cert file at %s: %v", caCertPath, err)
208266
}
209267
cert, err := tlsutil.ParsePEMEncodedCACert(capem)
210268
if err != nil {
211-
return nil, nil, fmt.Errorf("unable to parse CA Cert: %v", err)
269+
return nil, fmt.Errorf("unable to parse CA Cert: %v", err)
212270
}
213-
return key, cert, nil
271+
return cert, nil
214272
}
215273

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

hack/quickstart/init-master.sh

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,25 @@ 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"
2428
Environment="ETCD_INITIAL_CLUSTER=controller=http://${COREOS_PRIVATE_IPV4}:2380"
2529
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"
30+
Environment="ETCD_ADVERTISE_CLIENT_URLS=https://${COREOS_PRIVATE_IPV4}:2379"
31+
Environment="ETCD_SSL_DIR=/etc/etcd/tls"
32+
Environment="ETCD_LISTEN_CLIENT_URLS=https://0.0.0.0:2379"
2833
Environment="ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380"
34+
Environment="ETCD_TRUSTED_CA_FILE=/etc/ssl/certs/etcd-ca.crt"
35+
Environment="ETCD_CERT_FILE=/etc/ssl/certs/etcd-server.crt"
36+
Environment="ETCD_KEY_FILE=/etc/ssl/certs/etcd-server.key"
37+
Environment="ETCD_CLIENT_CERT_AUTH=true"
2938
EOF
3039
}
3140
}
@@ -35,26 +44,29 @@ function init_master_node() {
3544
systemctl daemon-reload
3645
systemctl stop update-engine; systemctl mask update-engine
3746

38-
etcd_render_flags=""
39-
40-
# Start etcd.
4147
if [ "$SELF_HOST_ETCD" = true ] ; then
4248
echo "WARNING: THIS IS NOT YET FULLY WORKING - merely here to make ongoing testing easier"
4349
etcd_render_flags="--experimental-self-hosted-etcd"
4450
else
45-
configure_etcd
46-
systemctl enable etcd-member; sudo systemctl start etcd-member
51+
etcd_render_flags="--etcd-use-tls --etcd-servers=https://${COREOS_PRIVATE_IPV4}:2379"
4752
fi
4853

4954
# 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}
55+
/home/core/bootkube render --asset-dir=/home/core/assets ${etcd_render_flags} \
56+
--api-servers=https://${COREOS_PUBLIC_IPV4}:443,https://${COREOS_PRIVATE_IPV4}:443
5157

5258
# Move the local kubeconfig into expected location
5359
chown -R core:core /home/core/assets
5460
mkdir -p /etc/kubernetes
5561
cp /home/core/assets/auth/kubeconfig /etc/kubernetes/
5662
cp /home/core/assets/tls/ca.crt /etc/kubernetes/ca.crt
5763

64+
# Start etcd.
65+
if [ "$SELF_HOST_ETCD" = false ] ; then
66+
configure_etcd
67+
systemctl enable etcd-member; sudo systemctl start etcd-member
68+
fi
69+
5870
# Start the kubelet
5971
systemctl enable kubelet; sudo systemctl start kubelet
6072

pkg/asset/asset.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ const (
1919
AssetPathCACert = "tls/ca.crt"
2020
AssetPathAPIServerKey = "tls/apiserver.key"
2121
AssetPathAPIServerCert = "tls/apiserver.crt"
22+
AssetPathEtcdCA = "tls/etcd-ca.crt"
23+
AssetPathEtcdServerCert = "tls/etcd-server.crt"
24+
AssetPathEtcdServerKey = "tls/etcd-server.key"
2225
AssetPathServiceAccountPrivKey = "tls/service-account.key"
2326
AssetPathServiceAccountPubKey = "tls/service-account.pub"
2427
AssetPathKubeletKey = "tls/kubelet.key"
@@ -55,7 +58,11 @@ const (
5558
// AssetConfig holds all configuration needed when generating
5659
// the default set of assets.
5760
type Config struct {
61+
EtcdCACert *x509.Certificate
62+
EtcdServerCert *x509.Certificate
63+
EtcdServerKey *rsa.PrivateKey
5864
EtcdServers []*url.URL
65+
EtcdUseTLS bool
5966
APIServers []*url.URL
6067
CACert *x509.Certificate
6168
CAPrivKey *rsa.PrivateKey
@@ -83,13 +90,31 @@ func NewDefaultAssets(conf Config) (Assets, error) {
8390
// Add kube-apiserver service IP
8491
conf.AltNames.IPs = append(conf.AltNames.IPs, conf.APIServiceIP)
8592

93+
// Create a CA if none was provided.
94+
if conf.CACert == nil {
95+
var err error
96+
conf.CAPrivKey, conf.CACert, err = newCACert()
97+
if err != nil {
98+
return Assets{}, err
99+
}
100+
}
101+
86102
// TLS assets
87103
tlsAssets, err := newTLSAssets(conf.CACert, conf.CAPrivKey, *conf.AltNames)
88104
if err != nil {
89105
return Assets{}, err
90106
}
91107
as = append(as, tlsAssets...)
92108

109+
// etcd TLS assets.
110+
if conf.EtcdUseTLS {
111+
etcdTLSAssets, err := newEtcdTLSAssets(conf.EtcdCACert, conf.EtcdServerCert, conf.EtcdServerKey, conf.CACert, conf.CAPrivKey, conf.EtcdServers)
112+
if err != nil {
113+
return Assets{}, err
114+
}
115+
as = append(as, etcdTLSAssets...)
116+
}
117+
93118
// K8S kubeconfig
94119
kubeConfig, err := newKubeConfigAsset(as, conf)
95120
if err != nil {
@@ -98,7 +123,7 @@ func NewDefaultAssets(conf Config) (Assets, error) {
98123
as = append(as, kubeConfig)
99124

100125
// K8S APIServer secret
101-
apiSecret, err := newAPIServerSecretAsset(as)
126+
apiSecret, err := newAPIServerSecretAsset(as, conf.EtcdUseTLS)
102127
if err != nil {
103128
return Assets{}, err
104129
}

pkg/asset/internal/templates.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,11 @@ spec:
171171
- --bind-address=0.0.0.0
172172
- --client-ca-file=/etc/kubernetes/secrets/ca.crt
173173
- --cloud-provider={{ .CloudProvider }}
174+
{{- if .EtcdUseTLS }}
175+
- --etcd-cafile=/etc/kubernetes/secrets/etcd-ca.crt
176+
- --etcd-certfile=/etc/kubernetes/secrets/etcd-server.crt
177+
- --etcd-keyfile=/etc/kubernetes/secrets/etcd-server.key
178+
{{- end }}
174179
- --etcd-servers={{ range $i, $e := .EtcdServers }}{{ if $i }},{{end}}{{ $e }}{{end}}
175180
- --insecure-port=8080
176181
- --kubelet-client-certificate=/etc/kubernetes/secrets/apiserver.crt
@@ -231,6 +236,11 @@ spec:
231236
- --authorization-mode=RBAC
232237
- --bind-address=0.0.0.0
233238
- --client-ca-file=/etc/kubernetes/secrets/ca.crt
239+
{{- if .EtcdUseTLS }}
240+
- --etcd-cafile=/etc/kubernetes/secrets/etcd-ca.crt
241+
- --etcd-certfile=/etc/kubernetes/secrets/etcd-server.crt
242+
- --etcd-keyfile=/etc/kubernetes/secrets/etcd-server.key
243+
{{- end }}
234244
- --etcd-servers={{ range $i, $e := .EtcdServers }}{{ if $i }},{{end}}{{ $e }}{{end}}{{ if .SelfHostedEtcd }},http://127.0.0.1:12379{{end}}
235245
- --insecure-port=8080
236246
- --kubelet-client-certificate=/etc/kubernetes/secrets/apiserver.crt

pkg/asset/k8s.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,20 @@ func newKubeConfigAsset(assets Assets, conf Config) (Asset, error) {
8787
})
8888
}
8989

90-
func newAPIServerSecretAsset(assets Assets) (Asset, error) {
90+
func newAPIServerSecretAsset(assets Assets, etcdUseTLS bool) (Asset, error) {
9191
secretAssets := []string{
9292
AssetPathAPIServerKey,
9393
AssetPathAPIServerCert,
9494
AssetPathServiceAccountPubKey,
9595
AssetPathCACert,
9696
}
97+
if etcdUseTLS {
98+
secretAssets = append(secretAssets, []string{
99+
AssetPathEtcdCA,
100+
AssetPathEtcdServerCert,
101+
AssetPathEtcdServerKey,
102+
}...)
103+
}
97104

98105
secretYAML, err := secretFromAssets(secretAPIServerName, secretNamespace, secretAssets, assets)
99106
if err != nil {

0 commit comments

Comments
 (0)