Download cert/key to agent with single HTTP request

Since generated cert/keys are stored locally, each server has a different
copy.  In a HA setup we need to ensure we download the cert and key from
the same server so we combined HTTP requests to do that.
This commit is contained in:
Darren Shepherd 2019-11-15 23:48:03 +00:00 committed by Craig Jellick
parent fe4b9cafd6
commit ff34c5c5cf
2 changed files with 83 additions and 37 deletions

View File

@ -6,6 +6,7 @@ import (
cryptorand "crypto/rand" cryptorand "crypto/rand"
"crypto/tls" "crypto/tls"
"encoding/hex" "encoding/hex"
"encoding/pem"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
sysnet "net" sysnet "net"
@ -141,14 +142,13 @@ func getServingCert(nodeName, servingCertFile, servingKeyFile, nodePasswordFile
if err != nil { if err != nil {
return nil, err return nil, err
} }
servingCert, servingKey := splitCertKeyPEM(servingCert)
if err := ioutil.WriteFile(servingCertFile, servingCert, 0600); err != nil { if err := ioutil.WriteFile(servingCertFile, servingCert, 0600); err != nil {
return nil, errors.Wrapf(err, "failed to write node cert") return nil, errors.Wrapf(err, "failed to write node cert")
} }
servingKey, err := clientaccess.Get("/v1-k3s/serving-kubelet.key", info)
if err != nil {
return nil, err
}
if err := ioutil.WriteFile(servingKeyFile, servingKey, 0600); err != nil { if err := ioutil.WriteFile(servingKeyFile, servingKey, 0600); err != nil {
return nil, errors.Wrapf(err, "failed to write node key") return nil, errors.Wrapf(err, "failed to write node key")
} }
@ -160,27 +160,60 @@ func getServingCert(nodeName, servingCertFile, servingKeyFile, nodePasswordFile
return &cert, nil return &cert, nil
} }
func getHostFile(filename string, info *clientaccess.Info) error { func getHostFile(filename, keyFile string, info *clientaccess.Info) error {
basename := filepath.Base(filename) basename := filepath.Base(filename)
fileBytes, err := clientaccess.Get("/v1-k3s/"+basename, info) fileBytes, err := clientaccess.Get("/v1-k3s/"+basename, info)
if err != nil { if err != nil {
return err return err
} }
if err := ioutil.WriteFile(filename, fileBytes, 0600); err != nil { if keyFile == "" {
return errors.Wrapf(err, "failed to write cert %s", filename) if err := ioutil.WriteFile(filename, fileBytes, 0600); err != nil {
return errors.Wrapf(err, "failed to write cert %s", filename)
}
} else {
fileBytes, keyBytes := splitCertKeyPEM(fileBytes)
if err := ioutil.WriteFile(filename, fileBytes, 0600); err != nil {
return errors.Wrapf(err, "failed to write cert %s", filename)
}
if err := ioutil.WriteFile(keyFile, keyBytes, 0600); err != nil {
return errors.Wrapf(err, "failed to write key %s", filename)
}
} }
return nil return nil
} }
func getNodeNamedHostFile(filename, nodeName, nodePasswordFile string, info *clientaccess.Info) error { func splitCertKeyPEM(bytes []byte) (certPem []byte, keyPem []byte) {
for {
b, rest := pem.Decode(bytes)
if b == nil {
break
}
bytes = rest
if strings.Contains(b.Type, "PRIVATE KEY") {
keyPem = append(keyPem, pem.EncodeToMemory(b)...)
} else {
certPem = append(certPem, pem.EncodeToMemory(b)...)
}
}
return
}
func getNodeNamedHostFile(filename, keyFile, nodeName, nodePasswordFile string, info *clientaccess.Info) error {
basename := filepath.Base(filename) basename := filepath.Base(filename)
fileBytes, err := Request("/v1-k3s/"+basename, info, getNodeNamedCrt(nodeName, nodePasswordFile)) fileBytes, err := Request("/v1-k3s/"+basename, info, getNodeNamedCrt(nodeName, nodePasswordFile))
if err != nil { if err != nil {
return err return err
} }
fileBytes, keyBytes := splitCertKeyPEM(fileBytes)
if err := ioutil.WriteFile(filename, fileBytes, 0600); err != nil { if err := ioutil.WriteFile(filename, fileBytes, 0600); err != nil {
return errors.Wrapf(err, "failed to write cert %s", filename) return errors.Wrapf(err, "failed to write cert %s", filename)
} }
if err := ioutil.WriteFile(keyFile, keyBytes, 0600); err != nil {
return errors.Wrapf(err, "failed to write key %s", filename)
}
return nil return nil
} }
@ -287,12 +320,12 @@ func get(envInfo *cmds.Agent) (*config.Node, error) {
} }
clientCAFile := filepath.Join(envInfo.DataDir, "client-ca.crt") clientCAFile := filepath.Join(envInfo.DataDir, "client-ca.crt")
if err := getHostFile(clientCAFile, info); err != nil { if err := getHostFile(clientCAFile, "", info); err != nil {
return nil, err return nil, err
} }
serverCAFile := filepath.Join(envInfo.DataDir, "server-ca.crt") serverCAFile := filepath.Join(envInfo.DataDir, "server-ca.crt")
if err := getHostFile(serverCAFile, info); err != nil { if err := getHostFile(serverCAFile, "", info); err != nil {
return nil, err return nil, err
} }
@ -331,12 +364,8 @@ func get(envInfo *cmds.Agent) (*config.Node, error) {
} }
clientKubeletCert := filepath.Join(envInfo.DataDir, "client-kubelet.crt") clientKubeletCert := filepath.Join(envInfo.DataDir, "client-kubelet.crt")
if err := getNodeNamedHostFile(clientKubeletCert, nodeName, newNodePasswordFile, info); err != nil {
return nil, err
}
clientKubeletKey := filepath.Join(envInfo.DataDir, "client-kubelet.key") clientKubeletKey := filepath.Join(envInfo.DataDir, "client-kubelet.key")
if err := getHostFile(clientKubeletKey, info); err != nil { if err := getNodeNamedHostFile(clientKubeletCert, clientKubeletKey, nodeName, newNodePasswordFile, info); err != nil {
return nil, err return nil, err
} }
@ -346,12 +375,8 @@ func get(envInfo *cmds.Agent) (*config.Node, error) {
} }
clientKubeProxyCert := filepath.Join(envInfo.DataDir, "client-kube-proxy.crt") clientKubeProxyCert := filepath.Join(envInfo.DataDir, "client-kube-proxy.crt")
if err := getHostFile(clientKubeProxyCert, info); err != nil {
return nil, err
}
clientKubeProxyKey := filepath.Join(envInfo.DataDir, "client-kube-proxy.key") clientKubeProxyKey := filepath.Join(envInfo.DataDir, "client-kube-proxy.key")
if err := getHostFile(clientKubeProxyKey, info); err != nil { if err := getHostFile(clientKubeProxyCert, clientKubeProxyKey, info); err != nil {
return nil, err return nil, err
} }
@ -361,12 +386,8 @@ func get(envInfo *cmds.Agent) (*config.Node, error) {
} }
clientK3sControllerCert := filepath.Join(envInfo.DataDir, "client-k3s-controller.crt") clientK3sControllerCert := filepath.Join(envInfo.DataDir, "client-k3s-controller.crt")
if err := getHostFile(clientK3sControllerCert, info); err != nil {
return nil, err
}
clientK3sControllerKey := filepath.Join(envInfo.DataDir, "client-k3s-controller.key") clientK3sControllerKey := filepath.Join(envInfo.DataDir, "client-k3s-controller.key")
if err := getHostFile(clientK3sControllerKey, info); err != nil { if err := getHostFile(clientK3sControllerCert, clientK3sControllerKey, info); err != nil {
return nil, err return nil, err
} }

View File

@ -29,14 +29,10 @@ func router(serverConfig *config.Control, tunnel http.Handler, ca []byte) http.H
authed := mux.NewRouter() authed := mux.NewRouter()
authed.Use(authMiddleware(serverConfig, "k3s:agent")) authed.Use(authMiddleware(serverConfig, "k3s:agent"))
authed.NotFoundHandler = serverConfig.Runtime.Handler authed.NotFoundHandler = serverConfig.Runtime.Handler
authed.Path("/v1-k3s/serving-kubelet.crt").Handler(servingKubeletCert(serverConfig)) authed.Path("/v1-k3s/serving-kubelet.crt").Handler(servingKubeletCert(serverConfig, serverConfig.Runtime.ServingKubeletKey))
authed.Path("/v1-k3s/serving-kubelet.key").Handler(fileHandler(serverConfig.Runtime.ServingKubeletKey)) authed.Path("/v1-k3s/client-kubelet.crt").Handler(clientKubeletCert(serverConfig, serverConfig.Runtime.ClientKubeletKey))
authed.Path("/v1-k3s/client-kubelet.crt").Handler(clientKubeletCert(serverConfig)) authed.Path("/v1-k3s/client-kube-proxy.crt").Handler(fileHandler(serverConfig.Runtime.ClientKubeProxyCert, serverConfig.Runtime.ClientKubeProxyKey))
authed.Path("/v1-k3s/client-kubelet.key").Handler(fileHandler(serverConfig.Runtime.ClientKubeletKey)) authed.Path("/v1-k3s/client-k3s-controller.crt").Handler(fileHandler(serverConfig.Runtime.ClientK3sControllerCert, serverConfig.Runtime.ClientK3sControllerKey))
authed.Path("/v1-k3s/client-kube-proxy.crt").Handler(fileHandler(serverConfig.Runtime.ClientKubeProxyCert))
authed.Path("/v1-k3s/client-kube-proxy.key").Handler(fileHandler(serverConfig.Runtime.ClientKubeProxyKey))
authed.Path("/v1-k3s/client-k3s-controller.crt").Handler(fileHandler(serverConfig.Runtime.ClientK3sControllerCert))
authed.Path("/v1-k3s/client-k3s-controller.key").Handler(fileHandler(serverConfig.Runtime.ClientK3sControllerKey))
authed.Path("/v1-k3s/client-ca.crt").Handler(fileHandler(serverConfig.Runtime.ClientCA)) authed.Path("/v1-k3s/client-ca.crt").Handler(fileHandler(serverConfig.Runtime.ClientCA))
authed.Path("/v1-k3s/server-ca.crt").Handler(fileHandler(serverConfig.Runtime.ServerCA)) authed.Path("/v1-k3s/server-ca.crt").Handler(fileHandler(serverConfig.Runtime.ServerCA))
authed.Path("/v1-k3s/config").Handler(configHandler(serverConfig)) authed.Path("/v1-k3s/config").Handler(configHandler(serverConfig))
@ -119,7 +115,7 @@ func getCACertAndKeys(caCertFile, caKeyFile, signingKeyFile string) ([]*x509.Cer
return caCert, caKey.(crypto.Signer), key.(crypto.Signer), nil return caCert, caKey.(crypto.Signer), key.(crypto.Signer), nil
} }
func servingKubeletCert(server *config.Control) http.Handler { func servingKubeletCert(server *config.Control, keyFile string) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
if req.TLS == nil { if req.TLS == nil {
resp.WriteHeader(http.StatusNotFound) resp.WriteHeader(http.StatusNotFound)
@ -156,11 +152,18 @@ func servingKubeletCert(server *config.Control) http.Handler {
return return
} }
keyBytes, err := ioutil.ReadFile(keyFile)
if err != nil {
http.Error(resp, err.Error(), http.StatusInternalServerError)
return
}
resp.Write(append(certutil.EncodeCertPEM(cert), certutil.EncodeCertPEM(caCert[0])...)) resp.Write(append(certutil.EncodeCertPEM(cert), certutil.EncodeCertPEM(caCert[0])...))
resp.Write(keyBytes)
}) })
} }
func clientKubeletCert(server *config.Control) http.Handler { func clientKubeletCert(server *config.Control, keyFile string) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
if req.TLS == nil { if req.TLS == nil {
resp.WriteHeader(http.StatusNotFound) resp.WriteHeader(http.StatusNotFound)
@ -194,17 +197,39 @@ func clientKubeletCert(server *config.Control) http.Handler {
return return
} }
keyBytes, err := ioutil.ReadFile(keyFile)
if err != nil {
http.Error(resp, err.Error(), http.StatusInternalServerError)
return
}
resp.Write(append(certutil.EncodeCertPEM(cert), certutil.EncodeCertPEM(caCert[0])...)) resp.Write(append(certutil.EncodeCertPEM(cert), certutil.EncodeCertPEM(caCert[0])...))
resp.Write(keyBytes)
}) })
} }
func fileHandler(fileName string) http.Handler { func fileHandler(fileName ...string) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
if req.TLS == nil { if req.TLS == nil {
resp.WriteHeader(http.StatusNotFound) resp.WriteHeader(http.StatusNotFound)
return return
} }
http.ServeFile(resp, req, fileName) resp.Header().Set("Content-Type", "text/plain")
if len(fileName) == 1 {
http.ServeFile(resp, req, fileName[0])
return
}
for _, f := range fileName {
bytes, err := ioutil.ReadFile(f)
if err != nil {
logrus.Errorf("Failed to read %s: %v", f, err)
resp.WriteHeader(http.StatusInternalServerError)
return
}
resp.Write(bytes)
}
}) })
} }