From cf12a13175538b2116c7c9c5d4d242de48e68220 Mon Sep 17 00:00:00 2001 From: Brad Davidson Date: Tue, 31 Aug 2021 23:50:23 -0700 Subject: [PATCH] Add missing node name entry to apiserver SAN list Also honor node-ip when adding the node address to the SAN list, instead of hardcoding the autodetected IP address. Signed-off-by: Brad Davidson --- pkg/agent/config/config.go | 61 +++++++------------------------- pkg/cli/server/server.go | 23 ++++++------ pkg/cluster/https.go | 2 +- pkg/daemons/control/deps/deps.go | 13 ++----- pkg/util/net.go | 43 ++++++++++++++++++++++ 5 files changed, 71 insertions(+), 71 deletions(-) diff --git a/pkg/agent/config/config.go b/pkg/agent/config/config.go index d6dab4c484..8118825482 100644 --- a/pkg/agent/config/config.go +++ b/pkg/agent/config/config.go @@ -9,7 +9,7 @@ import ( "encoding/pem" "fmt" "io/ioutil" - sysnet "net" + "net" "net/http" "net/url" "os" @@ -31,7 +31,6 @@ import ( "github.com/rancher/wrangler/pkg/slice" "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/util/json" - "k8s.io/apimachinery/pkg/util/net" ) const ( @@ -89,7 +88,7 @@ func Request(path string, info *clientaccess.Info, requester HTTPRequester) ([]b return requester(u.String(), clientaccess.GetHTTPClient(info.CACerts), info.Username, info.Password) } -func getNodeNamedCrt(nodeName string, nodeIPs []sysnet.IP, nodePasswordFile string) HTTPRequester { +func getNodeNamedCrt(nodeName string, nodeIPs []net.IP, nodePasswordFile string) HTTPRequester { return func(u string, client *http.Client, username, password string) ([]byte, error) { req, err := http.NewRequest(http.MethodGet, u, nil) if err != nil { @@ -169,7 +168,7 @@ func upgradeOldNodePasswordPath(oldNodePasswordFile, newNodePasswordFile string) } } -func getServingCert(nodeName string, nodeIPs []sysnet.IP, servingCertFile, servingKeyFile, nodePasswordFile string, info *clientaccess.Info) (*tls.Certificate, error) { +func getServingCert(nodeName string, nodeIPs []net.IP, servingCertFile, servingKeyFile, nodePasswordFile string, info *clientaccess.Info) (*tls.Certificate, error) { servingCert, err := Request("/v1-"+version.Program+"/serving-kubelet.crt", info, getNodeNamedCrt(nodeName, nodeIPs, nodePasswordFile)) if err != nil { return nil, err @@ -232,7 +231,7 @@ func splitCertKeyPEM(bytes []byte) (certPem []byte, keyPem []byte) { return } -func getNodeNamedHostFile(filename, keyFile, nodeName string, nodeIPs []sysnet.IP, nodePasswordFile string, info *clientaccess.Info) error { +func getNodeNamedHostFile(filename, keyFile, nodeName string, nodeIPs []net.IP, nodePasswordFile string, info *clientaccess.Info) error { basename := filepath.Base(filename) fileBytes, err := Request("/v1-"+version.Program+"/"+basename, info, getNodeNamedCrt(nodeName, nodeIPs, nodePasswordFile)) if err != nil { @@ -249,42 +248,6 @@ func getNodeNamedHostFile(filename, keyFile, nodeName string, nodeIPs []sysnet.I return nil } -func getHostnameAndIPs(info cmds.Agent) (string, []sysnet.IP, error) { - ips := []sysnet.IP{} - if len(info.NodeIP) == 0 { - hostIP, err := net.ChooseHostInterface() - if err != nil { - return "", nil, err - } - ips = append(ips, hostIP) - } else { - for _, hostIP := range info.NodeIP { - for _, v := range strings.Split(hostIP, ",") { - ip := sysnet.ParseIP(v) - if ip == nil { - return "", nil, fmt.Errorf("invalid node-ip %s", v) - } - ips = append(ips, ip) - } - } - } - - name := info.NodeName - if name == "" { - hostname, err := os.Hostname() - if err != nil { - return "", nil, err - } - name = hostname - } - - // Use lower case hostname to comply with kubernetes constraint: - // https://github.com/kubernetes/kubernetes/issues/71140 - name = strings.ToLower(name) - - return name, ips, nil -} - func isValidResolvConf(resolvConfFile string) bool { file, err := os.Open(resolvConfFile) if err != nil { @@ -297,7 +260,7 @@ func isValidResolvConf(resolvConfFile string) bool { for scanner.Scan() { ipMatch := nameserver.FindStringSubmatch(scanner.Text()) if len(ipMatch) == 2 { - ip := sysnet.ParseIP(ipMatch[1]) + ip := net.ParseIP(ipMatch[1]) if ip == nil || !ip.IsGlobalUnicast() { return false } @@ -350,9 +313,9 @@ func get(ctx context.Context, envInfo *cmds.Agent, proxy proxy.Proxy) (*config.N } } - var flannelIface *sysnet.Interface + var flannelIface *net.Interface if !envInfo.NoFlannel && len(envInfo.FlannelIface) > 0 { - flannelIface, err = sysnet.InterfaceByName(envInfo.FlannelIface) + flannelIface, err = net.InterfaceByName(envInfo.FlannelIface) if err != nil { return nil, errors.Wrapf(err, "unable to find interface") } @@ -384,7 +347,7 @@ func get(ctx context.Context, envInfo *cmds.Agent, proxy proxy.Proxy) (*config.N newNodePasswordFile := filepath.Join(nodeConfigPath, "password") upgradeOldNodePasswordPath(oldNodePasswordFile, newNodePasswordFile) - nodeName, nodeIPs, err := getHostnameAndIPs(*envInfo) + nodeName, nodeIPs, err := util.GetHostnameAndIPs(envInfo.NodeName, envInfo.NodeIP) if err != nil { return nil, err } @@ -498,7 +461,7 @@ func get(ctx context.Context, envInfo *cmds.Agent, proxy proxy.Proxy) (*config.N for _, externalIP := range envInfo.NodeExternalIP { for _, v := range strings.Split(externalIP, ",") { - ip := sysnet.ParseIP(v) + ip := net.ParseIP(v) if ip == nil { return nil, fmt.Errorf("invalid node-external-ip %s", v) } @@ -546,7 +509,7 @@ func get(ctx context.Context, envInfo *cmds.Agent, proxy proxy.Proxy) (*config.N if controlConfig.ClusterIPRange != nil { nodeConfig.AgentConfig.ClusterCIDR = controlConfig.ClusterIPRange - nodeConfig.AgentConfig.ClusterCIDRs = []*sysnet.IPNet{controlConfig.ClusterIPRange} + nodeConfig.AgentConfig.ClusterCIDRs = []*net.IPNet{controlConfig.ClusterIPRange} } if len(controlConfig.ClusterIPRanges) > 0 { @@ -555,7 +518,7 @@ func get(ctx context.Context, envInfo *cmds.Agent, proxy proxy.Proxy) (*config.N if controlConfig.ServiceIPRange != nil { nodeConfig.AgentConfig.ServiceCIDR = controlConfig.ServiceIPRange - nodeConfig.AgentConfig.ServiceCIDRs = []*sysnet.IPNet{controlConfig.ServiceIPRange} + nodeConfig.AgentConfig.ServiceCIDRs = []*net.IPNet{controlConfig.ServiceIPRange} } if len(controlConfig.ServiceIPRanges) > 0 { @@ -567,7 +530,7 @@ func get(ctx context.Context, envInfo *cmds.Agent, proxy proxy.Proxy) (*config.N } if len(controlConfig.ClusterDNSs) == 0 { - nodeConfig.AgentConfig.ClusterDNSs = []sysnet.IP{controlConfig.ClusterDNS} + nodeConfig.AgentConfig.ClusterDNSs = []net.IP{controlConfig.ClusterDNS} } else { nodeConfig.AgentConfig.ClusterDNSs = controlConfig.ClusterDNSs } diff --git a/pkg/cli/server/server.go b/pkg/cli/server/server.go index 8beb533b06..5fb2e313b1 100644 --- a/pkg/cli/server/server.go +++ b/pkg/cli/server/server.go @@ -100,7 +100,7 @@ func run(app *cli.Context, cfg *cmds.Server, leaderControllers server.CustomCont serverConfig.ControlConfig.KubeConfigOutput = cfg.KubeConfigOutput serverConfig.ControlConfig.KubeConfigMode = cfg.KubeConfigMode serverConfig.Rootless = cfg.Rootless - serverConfig.ControlConfig.SANs = knownIPs(cfg.TLSSan) + serverConfig.ControlConfig.SANs = cfg.TLSSan serverConfig.ControlConfig.BindAddress = cfg.BindAddress serverConfig.ControlConfig.SupervisorPort = cfg.SupervisorPort serverConfig.ControlConfig.HTTPSPort = cfg.HTTPSPort @@ -215,6 +215,18 @@ func run(app *cli.Context, cfg *cmds.Server, leaderControllers server.CustomCont serverConfig.ControlConfig.SANs = append(serverConfig.ControlConfig.SANs, serverConfig.ControlConfig.AdvertiseIP) } + // Ensure that we add the localhost name/ip and node name/ip to the SAN list. This list is shared by the + // certs for the supervisor, kube-apiserver cert, and etcd. DNS entries for the in-cluster kubernetes + // service endpoint are added later when the certificates are created. + nodeName, nodeIPs, err := util.GetHostnameAndIPs(cmds.AgentConfig.NodeName, cmds.AgentConfig.NodeIP) + if err != nil { + return err + } + serverConfig.ControlConfig.SANs = append(serverConfig.ControlConfig.SANs, "127.0.0.1", "localhost", nodeName) + for _, ip := range nodeIPs { + serverConfig.ControlConfig.SANs = append(serverConfig.ControlConfig.SANs, ip.String()) + } + // configure ClusterIPRanges if len(cmds.ServerConfig.ClusterCIDR) == 0 { cmds.ServerConfig.ClusterCIDR.Set("10.42.0.0/16") @@ -464,15 +476,6 @@ func validateNetworkConfiguration(serverConfig server.Config) error { return nil } -func knownIPs(ips []string) []string { - ips = append(ips, "127.0.0.1") - ip, err := utilnet.ChooseHostInterface() - if err == nil { - ips = append(ips, ip.String()) - } - return ips -} - func getArgValueFromList(searchArg string, argList []string) string { var value string for _, arg := range argList { diff --git a/pkg/cluster/https.go b/pkg/cluster/https.go index dc4a55af9b..23ac04f0a8 100644 --- a/pkg/cluster/https.go +++ b/pkg/cluster/https.go @@ -45,7 +45,7 @@ func (c *Cluster) newListener(ctx context.Context) (net.Listener, http.Handler, return dynamiclistener.NewListener(tcp, storage, cert, key, dynamiclistener.Config{ ExpirationDaysCheck: config.CertificateRenewDays, Organization: []string{version.Program}, - SANs: append(c.config.SANs, "localhost", "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc."+c.config.ClusterDomain), + SANs: append(c.config.SANs, "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc."+c.config.ClusterDomain), CN: version.Program, TLSConfig: &tls.Config{ ClientAuth: tls.RequestClientCert, diff --git a/pkg/daemons/control/deps/deps.go b/pkg/daemons/control/deps/deps.go index 16fc75f3f2..22bbeb93f9 100644 --- a/pkg/daemons/control/deps/deps.go +++ b/pkg/daemons/control/deps/deps.go @@ -25,7 +25,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/sets" apiserverconfigv1 "k8s.io/apiserver/pkg/apis/config/v1" - "k8s.io/kubernetes/pkg/controlplane" ) const ( @@ -370,14 +369,8 @@ func genServerCerts(config *config.Control, runtime *config.ControlRuntime) erro return err } - _, apiServerServiceIP, err := controlplane.ServiceIPRange(*config.ServiceIPRange) - if err != nil { - return err - } - altNames := &certutil.AltNames{ - DNSNames: []string{"localhost", "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc." + config.ClusterDomain}, - IPs: []net.IP{apiServerServiceIP}, + DNSNames: []string{"kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc." + config.ClusterDomain}, } addSANs(altNames, config.SANs) @@ -402,9 +395,7 @@ func genETCDCerts(config *config.Control, runtime *config.ControlRuntime) error return err } - altNames := &certutil.AltNames{ - DNSNames: []string{"localhost"}, - } + altNames := &certutil.AltNames{} addSANs(altNames, config.SANs) if _, err := createClientCertKey(regen, "etcd-server", nil, diff --git a/pkg/util/net.go b/pkg/util/net.go index 390575f44b..94a8742a0e 100644 --- a/pkg/util/net.go +++ b/pkg/util/net.go @@ -2,8 +2,13 @@ package util import ( "errors" + "fmt" "net" + "os" "strings" + + "github.com/urfave/cli" + apinet "k8s.io/apimachinery/pkg/util/net" ) // JoinIPs stringifies and joins a list of IP addresses with commas. @@ -85,3 +90,41 @@ func JoinIP6Nets(elems []*net.IPNet) string { } return strings.Join(strs, ",") } + +// GetHostnameAndIPs takes a node name and list of IPs, usually from CLI args. +// If set, these are used to return the node's name and addresses. If not set, +// the system hostname and primary interface address are returned instead. +func GetHostnameAndIPs(name string, nodeIPs cli.StringSlice) (string, []net.IP, error) { + ips := []net.IP{} + if len(nodeIPs) == 0 { + hostIP, err := apinet.ChooseHostInterface() + if err != nil { + return "", nil, err + } + ips = append(ips, hostIP) + } else { + for _, hostIP := range nodeIPs { + for _, v := range strings.Split(hostIP, ",") { + ip := net.ParseIP(v) + if ip == nil { + return "", nil, fmt.Errorf("invalid node-ip %s", v) + } + ips = append(ips, ip) + } + } + } + + if name == "" { + hostname, err := os.Hostname() + if err != nil { + return "", nil, err + } + name = hostname + } + + // Use lower case hostname to comply with kubernetes constraint: + // https://github.com/kubernetes/kubernetes/issues/71140 + name = strings.ToLower(name) + + return name, ips, nil +}