127.0.0.1-as-advertise-address

This commit is contained in:
fabriziopandini 2019-10-04 08:41:07 +02:00
parent 90f487190a
commit fd2c678b1c
4 changed files with 54 additions and 6 deletions

View File

@ -123,6 +123,14 @@ func VerifyAPIServerBindAddress(address string) error {
if ip == nil { if ip == nil {
return errors.Errorf("cannot parse IP address: %s", address) return errors.Errorf("cannot parse IP address: %s", address)
} }
// There are users with network setups where default routes are present, but network interfaces
// use only link-local addresses (e.g. as described in RFC5549).
// In many cases that matching global unicast IP address can be found on loopback interface,
// so kubeadm allows users to specify address=Loopback for handling supporting the scenario above.
// Nb. SetAPIEndpointDynamicDefaults will try to translate loopback to a valid address afterwards
if ip.IsLoopback() {
return nil
}
if !ip.IsGlobalUnicast() { if !ip.IsGlobalUnicast() {
return errors.Errorf("cannot use %q as the bind address for the API Server", address) return errors.Errorf("cannot use %q as the bind address for the API Server", address)
} }

View File

@ -161,13 +161,13 @@ func TestVerifyAPIServerBindAddress(t *testing.T) {
address: "2001:db8:85a3::8a2e:370:7334", address: "2001:db8:85a3::8a2e:370:7334",
}, },
{ {
name: "invalid address: not a global unicast 0.0.0.0", name: "valid address 127.0.0.1",
address: "0.0.0.0", address: "127.0.0.1",
expectedError: true, expectedError: false,
}, },
{ {
name: "invalid address: not a global unicast 127.0.0.1", name: "invalid address: not a global unicast 0.0.0.0",
address: "127.0.0.1", address: "0.0.0.0",
expectedError: true, expectedError: true,
}, },
{ {

View File

@ -26,9 +26,10 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"k8s.io/klog" "k8s.io/klog"
"k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
netutil "k8s.io/apimachinery/pkg/util/net"
bootstraputil "k8s.io/cluster-bootstrap/token/util" bootstraputil "k8s.io/cluster-bootstrap/token/util"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme" kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
@ -112,6 +113,22 @@ func SetAPIEndpointDynamicDefaults(cfg *kubeadmapi.APIEndpoint) error {
if addressIP == nil && cfg.AdvertiseAddress != "" { if addressIP == nil && cfg.AdvertiseAddress != "" {
return errors.Errorf("couldn't use \"%s\" as \"apiserver-advertise-address\", must be ipv4 or ipv6 address", cfg.AdvertiseAddress) return errors.Errorf("couldn't use \"%s\" as \"apiserver-advertise-address\", must be ipv4 or ipv6 address", cfg.AdvertiseAddress)
} }
// kubeadm allows users to specify address=Loopback as a selector for global unicast IP address that can be found on loopback interface.
// e.g. This is required for network setups where default routes are present, but network interfaces use only link-local addresses (e.g. as described in RFC5549).
if addressIP.IsLoopback() {
loopbackIP, err := netutil.ChooseBindAddressForInterface(netutil.LoopbackInterfaceName)
if err != nil {
return err
}
if loopbackIP != nil {
klog.V(4).Infof("Found active IP %v on loopback interface", loopbackIP.String())
cfg.AdvertiseAddress = loopbackIP.String()
return nil
}
return errors.New("unable to resolve link-local addresses")
}
// This is the same logic as the API Server uses, except that if no interface is found the address is set to 0.0.0.0, which is invalid and cannot be used // This is the same logic as the API Server uses, except that if no interface is found the address is set to 0.0.0.0, which is invalid and cannot be used
// for bootstrapping a cluster. // for bootstrapping a cluster.
ip, err := ChooseAPIServerBindAddress(addressIP) ip, err := ChooseAPIServerBindAddress(addressIP)

View File

@ -36,6 +36,11 @@ const (
familyIPv6 AddressFamily = 6 familyIPv6 AddressFamily = 6
) )
const (
// LoopbackInterfaceName is the default name of the loopback interface
LoopbackInterfaceName = "lo"
)
const ( const (
ipv4RouteFile = "/proc/net/route" ipv4RouteFile = "/proc/net/route"
ipv6RouteFile = "/proc/net/ipv6_route" ipv6RouteFile = "/proc/net/ipv6_route"
@ -414,3 +419,21 @@ func ChooseBindAddress(bindAddress net.IP) (net.IP, error) {
} }
return bindAddress, nil return bindAddress, nil
} }
// ChooseBindAddressForInterface choose a global IP for a specific interface, with priority given to IPv4.
// This is required in case of network setups where default routes are present, but network
// interfaces use only link-local addresses (e.g. as described in RFC5549).
// e.g when using BGP to announce a host IP over link-local ip addresses and this ip address is attached to the lo interface.
func ChooseBindAddressForInterface(intfName string) (net.IP, error) {
var nw networkInterfacer = networkInterface{}
for _, family := range []AddressFamily{familyIPv4, familyIPv6} {
ip, err := getIPFromInterface(intfName, family, nw)
if err != nil {
return nil, err
}
if ip != nil {
return ip, nil
}
}
return nil, fmt.Errorf("unable to select an IP from %s network interface", intfName)
}