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 <brad.davidson@rancher.com>
This commit is contained in:
Brad Davidson 2021-08-31 23:50:23 -07:00 committed by Brad Davidson
parent 74196acaea
commit cf12a13175
5 changed files with 71 additions and 71 deletions

View File

@ -9,7 +9,7 @@ import (
"encoding/pem" "encoding/pem"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
sysnet "net" "net"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
@ -31,7 +31,6 @@ import (
"github.com/rancher/wrangler/pkg/slice" "github.com/rancher/wrangler/pkg/slice"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/util/json" "k8s.io/apimachinery/pkg/util/json"
"k8s.io/apimachinery/pkg/util/net"
) )
const ( 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) 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) { return func(u string, client *http.Client, username, password string) ([]byte, error) {
req, err := http.NewRequest(http.MethodGet, u, nil) req, err := http.NewRequest(http.MethodGet, u, nil)
if err != 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)) servingCert, err := Request("/v1-"+version.Program+"/serving-kubelet.crt", info, getNodeNamedCrt(nodeName, nodeIPs, nodePasswordFile))
if err != nil { if err != nil {
return nil, err return nil, err
@ -232,7 +231,7 @@ func splitCertKeyPEM(bytes []byte) (certPem []byte, keyPem []byte) {
return 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) basename := filepath.Base(filename)
fileBytes, err := Request("/v1-"+version.Program+"/"+basename, info, getNodeNamedCrt(nodeName, nodeIPs, nodePasswordFile)) fileBytes, err := Request("/v1-"+version.Program+"/"+basename, info, getNodeNamedCrt(nodeName, nodeIPs, nodePasswordFile))
if err != nil { if err != nil {
@ -249,42 +248,6 @@ func getNodeNamedHostFile(filename, keyFile, nodeName string, nodeIPs []sysnet.I
return nil 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 { func isValidResolvConf(resolvConfFile string) bool {
file, err := os.Open(resolvConfFile) file, err := os.Open(resolvConfFile)
if err != nil { if err != nil {
@ -297,7 +260,7 @@ func isValidResolvConf(resolvConfFile string) bool {
for scanner.Scan() { for scanner.Scan() {
ipMatch := nameserver.FindStringSubmatch(scanner.Text()) ipMatch := nameserver.FindStringSubmatch(scanner.Text())
if len(ipMatch) == 2 { if len(ipMatch) == 2 {
ip := sysnet.ParseIP(ipMatch[1]) ip := net.ParseIP(ipMatch[1])
if ip == nil || !ip.IsGlobalUnicast() { if ip == nil || !ip.IsGlobalUnicast() {
return false 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 { if !envInfo.NoFlannel && len(envInfo.FlannelIface) > 0 {
flannelIface, err = sysnet.InterfaceByName(envInfo.FlannelIface) flannelIface, err = net.InterfaceByName(envInfo.FlannelIface)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "unable to find interface") 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") newNodePasswordFile := filepath.Join(nodeConfigPath, "password")
upgradeOldNodePasswordPath(oldNodePasswordFile, newNodePasswordFile) upgradeOldNodePasswordPath(oldNodePasswordFile, newNodePasswordFile)
nodeName, nodeIPs, err := getHostnameAndIPs(*envInfo) nodeName, nodeIPs, err := util.GetHostnameAndIPs(envInfo.NodeName, envInfo.NodeIP)
if err != nil { if err != nil {
return nil, err 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 _, externalIP := range envInfo.NodeExternalIP {
for _, v := range strings.Split(externalIP, ",") { for _, v := range strings.Split(externalIP, ",") {
ip := sysnet.ParseIP(v) ip := net.ParseIP(v)
if ip == nil { if ip == nil {
return nil, fmt.Errorf("invalid node-external-ip %s", v) 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 { if controlConfig.ClusterIPRange != nil {
nodeConfig.AgentConfig.ClusterCIDR = controlConfig.ClusterIPRange nodeConfig.AgentConfig.ClusterCIDR = controlConfig.ClusterIPRange
nodeConfig.AgentConfig.ClusterCIDRs = []*sysnet.IPNet{controlConfig.ClusterIPRange} nodeConfig.AgentConfig.ClusterCIDRs = []*net.IPNet{controlConfig.ClusterIPRange}
} }
if len(controlConfig.ClusterIPRanges) > 0 { 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 { if controlConfig.ServiceIPRange != nil {
nodeConfig.AgentConfig.ServiceCIDR = controlConfig.ServiceIPRange nodeConfig.AgentConfig.ServiceCIDR = controlConfig.ServiceIPRange
nodeConfig.AgentConfig.ServiceCIDRs = []*sysnet.IPNet{controlConfig.ServiceIPRange} nodeConfig.AgentConfig.ServiceCIDRs = []*net.IPNet{controlConfig.ServiceIPRange}
} }
if len(controlConfig.ServiceIPRanges) > 0 { 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 { if len(controlConfig.ClusterDNSs) == 0 {
nodeConfig.AgentConfig.ClusterDNSs = []sysnet.IP{controlConfig.ClusterDNS} nodeConfig.AgentConfig.ClusterDNSs = []net.IP{controlConfig.ClusterDNS}
} else { } else {
nodeConfig.AgentConfig.ClusterDNSs = controlConfig.ClusterDNSs nodeConfig.AgentConfig.ClusterDNSs = controlConfig.ClusterDNSs
} }

View File

@ -100,7 +100,7 @@ func run(app *cli.Context, cfg *cmds.Server, leaderControllers server.CustomCont
serverConfig.ControlConfig.KubeConfigOutput = cfg.KubeConfigOutput serverConfig.ControlConfig.KubeConfigOutput = cfg.KubeConfigOutput
serverConfig.ControlConfig.KubeConfigMode = cfg.KubeConfigMode serverConfig.ControlConfig.KubeConfigMode = cfg.KubeConfigMode
serverConfig.Rootless = cfg.Rootless serverConfig.Rootless = cfg.Rootless
serverConfig.ControlConfig.SANs = knownIPs(cfg.TLSSan) serverConfig.ControlConfig.SANs = cfg.TLSSan
serverConfig.ControlConfig.BindAddress = cfg.BindAddress serverConfig.ControlConfig.BindAddress = cfg.BindAddress
serverConfig.ControlConfig.SupervisorPort = cfg.SupervisorPort serverConfig.ControlConfig.SupervisorPort = cfg.SupervisorPort
serverConfig.ControlConfig.HTTPSPort = cfg.HTTPSPort 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) 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 // configure ClusterIPRanges
if len(cmds.ServerConfig.ClusterCIDR) == 0 { if len(cmds.ServerConfig.ClusterCIDR) == 0 {
cmds.ServerConfig.ClusterCIDR.Set("10.42.0.0/16") cmds.ServerConfig.ClusterCIDR.Set("10.42.0.0/16")
@ -464,15 +476,6 @@ func validateNetworkConfiguration(serverConfig server.Config) error {
return nil 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 { func getArgValueFromList(searchArg string, argList []string) string {
var value string var value string
for _, arg := range argList { for _, arg := range argList {

View File

@ -45,7 +45,7 @@ func (c *Cluster) newListener(ctx context.Context) (net.Listener, http.Handler,
return dynamiclistener.NewListener(tcp, storage, cert, key, dynamiclistener.Config{ return dynamiclistener.NewListener(tcp, storage, cert, key, dynamiclistener.Config{
ExpirationDaysCheck: config.CertificateRenewDays, ExpirationDaysCheck: config.CertificateRenewDays,
Organization: []string{version.Program}, 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, CN: version.Program,
TLSConfig: &tls.Config{ TLSConfig: &tls.Config{
ClientAuth: tls.RequestClientCert, ClientAuth: tls.RequestClientCert,

View File

@ -25,7 +25,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
apiserverconfigv1 "k8s.io/apiserver/pkg/apis/config/v1" apiserverconfigv1 "k8s.io/apiserver/pkg/apis/config/v1"
"k8s.io/kubernetes/pkg/controlplane"
) )
const ( const (
@ -370,14 +369,8 @@ func genServerCerts(config *config.Control, runtime *config.ControlRuntime) erro
return err return err
} }
_, apiServerServiceIP, err := controlplane.ServiceIPRange(*config.ServiceIPRange)
if err != nil {
return err
}
altNames := &certutil.AltNames{ altNames := &certutil.AltNames{
DNSNames: []string{"localhost", "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc." + config.ClusterDomain}, DNSNames: []string{"kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc." + config.ClusterDomain},
IPs: []net.IP{apiServerServiceIP},
} }
addSANs(altNames, config.SANs) addSANs(altNames, config.SANs)
@ -402,9 +395,7 @@ func genETCDCerts(config *config.Control, runtime *config.ControlRuntime) error
return err return err
} }
altNames := &certutil.AltNames{ altNames := &certutil.AltNames{}
DNSNames: []string{"localhost"},
}
addSANs(altNames, config.SANs) addSANs(altNames, config.SANs)
if _, err := createClientCertKey(regen, "etcd-server", nil, if _, err := createClientCertKey(regen, "etcd-server", nil,

View File

@ -2,8 +2,13 @@ package util
import ( import (
"errors" "errors"
"fmt"
"net" "net"
"os"
"strings" "strings"
"github.com/urfave/cli"
apinet "k8s.io/apimachinery/pkg/util/net"
) )
// JoinIPs stringifies and joins a list of IP addresses with commas. // 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, ",") 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
}