mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 20:24:09 +00:00
Merge pull request #84727 from danwinship/ipv6-bind
fix apiserver to advertise IPv6 endpoints if bound to IPv6
This commit is contained in:
commit
49a9b6cadf
@ -139,10 +139,10 @@ func VerifyAPIServerBindAddress(address string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChooseAPIServerBindAddress is a wrapper for netutil.ChooseBindAddress that also handles
|
// ChooseAPIServerBindAddress is a wrapper for netutil.ResolveBindAddress that also handles
|
||||||
// the case where no default routes were found and an IP for the API server could not be obtained.
|
// the case where no default routes were found and an IP for the API server could not be obtained.
|
||||||
func ChooseAPIServerBindAddress(bindAddress net.IP) (net.IP, error) {
|
func ChooseAPIServerBindAddress(bindAddress net.IP) (net.IP, error) {
|
||||||
ip, err := netutil.ChooseBindAddress(bindAddress)
|
ip, err := netutil.ResolveBindAddress(bindAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if netutil.IsNoRoutesError(err) {
|
if netutil.IsNoRoutesError(err) {
|
||||||
klog.Warningf("WARNING: could not obtain a bind address for the API Server: %v; using: %s", err, constants.DefaultAPIServerBindAddress)
|
klog.Warningf("WARNING: could not obtain a bind address for the API Server: %v; using: %s", err, constants.DefaultAPIServerBindAddress)
|
||||||
|
@ -60,7 +60,7 @@ func DefaultAdvertiseAddress(s *genericoptions.ServerRunOptions, insecure *gener
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s.AdvertiseAddress == nil || s.AdvertiseAddress.IsUnspecified() {
|
if s.AdvertiseAddress == nil || s.AdvertiseAddress.IsUnspecified() {
|
||||||
hostIP, err := utilnet.ChooseBindAddress(insecure.BindAddress)
|
hostIP, err := utilnet.ResolveBindAddress(insecure.BindAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to find suitable network address.error='%v'. "+
|
return fmt.Errorf("unable to find suitable network address.error='%v'. "+
|
||||||
"Try to set the AdvertiseAddress directly or provide a valid BindAddress to fix this", err)
|
"Try to set the AdvertiseAddress directly or provide a valid BindAddress to fix this", err)
|
||||||
|
@ -36,6 +36,13 @@ const (
|
|||||||
familyIPv6 AddressFamily = 6
|
familyIPv6 AddressFamily = 6
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type AddressFamilyPreference []AddressFamily
|
||||||
|
|
||||||
|
var (
|
||||||
|
preferIPv4 = AddressFamilyPreference{familyIPv4, familyIPv6}
|
||||||
|
preferIPv6 = AddressFamilyPreference{familyIPv6, familyIPv4}
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// LoopbackInterfaceName is the default name of the loopback interface
|
// LoopbackInterfaceName is the default name of the loopback interface
|
||||||
LoopbackInterfaceName = "lo"
|
LoopbackInterfaceName = "lo"
|
||||||
@ -58,7 +65,7 @@ type RouteFile struct {
|
|||||||
parse func(input io.Reader) ([]Route, error)
|
parse func(input io.Reader) ([]Route, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// noRoutesError can be returned by ChooseBindAddress() in case of no routes
|
// noRoutesError can be returned in case of no routes
|
||||||
type noRoutesError struct {
|
type noRoutesError struct {
|
||||||
message string
|
message string
|
||||||
}
|
}
|
||||||
@ -259,7 +266,7 @@ func getIPFromInterface(intfName string, forFamily AddressFamily, nw networkInte
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// memberOF tells if the IP is of the desired family. Used for checking interface addresses.
|
// memberOf tells if the IP is of the desired family. Used for checking interface addresses.
|
||||||
func memberOf(ip net.IP, family AddressFamily) bool {
|
func memberOf(ip net.IP, family AddressFamily) bool {
|
||||||
if ip.To4() != nil {
|
if ip.To4() != nil {
|
||||||
return family == familyIPv4
|
return family == familyIPv4
|
||||||
@ -270,8 +277,8 @@ func memberOf(ip net.IP, family AddressFamily) bool {
|
|||||||
|
|
||||||
// chooseIPFromHostInterfaces looks at all system interfaces, trying to find one that is up that
|
// chooseIPFromHostInterfaces looks at all system interfaces, trying to find one that is up that
|
||||||
// has a global unicast address (non-loopback, non-link local, non-point2point), and returns the IP.
|
// has a global unicast address (non-loopback, non-link local, non-point2point), and returns the IP.
|
||||||
// Searches for IPv4 addresses, and then IPv6 addresses.
|
// addressFamilies determines whether it prefers IPv4 or IPv6
|
||||||
func chooseIPFromHostInterfaces(nw networkInterfacer) (net.IP, error) {
|
func chooseIPFromHostInterfaces(nw networkInterfacer, addressFamilies AddressFamilyPreference) (net.IP, error) {
|
||||||
intfs, err := nw.Interfaces()
|
intfs, err := nw.Interfaces()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -279,7 +286,7 @@ func chooseIPFromHostInterfaces(nw networkInterfacer) (net.IP, error) {
|
|||||||
if len(intfs) == 0 {
|
if len(intfs) == 0 {
|
||||||
return nil, fmt.Errorf("no interfaces found on host.")
|
return nil, fmt.Errorf("no interfaces found on host.")
|
||||||
}
|
}
|
||||||
for _, family := range []AddressFamily{familyIPv4, familyIPv6} {
|
for _, family := range addressFamilies {
|
||||||
klog.V(4).Infof("Looking for system interface with a global IPv%d address", uint(family))
|
klog.V(4).Infof("Looking for system interface with a global IPv%d address", uint(family))
|
||||||
for _, intf := range intfs {
|
for _, intf := range intfs {
|
||||||
if !isInterfaceUp(&intf) {
|
if !isInterfaceUp(&intf) {
|
||||||
@ -326,15 +333,19 @@ func chooseIPFromHostInterfaces(nw networkInterfacer) (net.IP, error) {
|
|||||||
// IP of the interface with a gateway on it (with priority given to IPv4). For a node
|
// IP of the interface with a gateway on it (with priority given to IPv4). For a node
|
||||||
// with no internet connection, it returns error.
|
// with no internet connection, it returns error.
|
||||||
func ChooseHostInterface() (net.IP, error) {
|
func ChooseHostInterface() (net.IP, error) {
|
||||||
|
return chooseHostInterface(preferIPv4)
|
||||||
|
}
|
||||||
|
|
||||||
|
func chooseHostInterface(addressFamilies AddressFamilyPreference) (net.IP, error) {
|
||||||
var nw networkInterfacer = networkInterface{}
|
var nw networkInterfacer = networkInterface{}
|
||||||
if _, err := os.Stat(ipv4RouteFile); os.IsNotExist(err) {
|
if _, err := os.Stat(ipv4RouteFile); os.IsNotExist(err) {
|
||||||
return chooseIPFromHostInterfaces(nw)
|
return chooseIPFromHostInterfaces(nw, addressFamilies)
|
||||||
}
|
}
|
||||||
routes, err := getAllDefaultRoutes()
|
routes, err := getAllDefaultRoutes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return chooseHostInterfaceFromRoute(routes, nw)
|
return chooseHostInterfaceFromRoute(routes, nw, addressFamilies)
|
||||||
}
|
}
|
||||||
|
|
||||||
// networkInterfacer defines an interface for several net library functions. Production
|
// networkInterfacer defines an interface for several net library functions. Production
|
||||||
@ -382,10 +393,10 @@ func getAllDefaultRoutes() ([]Route, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// chooseHostInterfaceFromRoute cycles through each default route provided, looking for a
|
// chooseHostInterfaceFromRoute cycles through each default route provided, looking for a
|
||||||
// global IP address from the interface for the route. Will first look all each IPv4 route for
|
// global IP address from the interface for the route. addressFamilies determines whether it
|
||||||
// an IPv4 IP, and then will look at each IPv6 route for an IPv6 IP.
|
// prefers IPv4 or IPv6
|
||||||
func chooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer) (net.IP, error) {
|
func chooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer, addressFamilies AddressFamilyPreference) (net.IP, error) {
|
||||||
for _, family := range []AddressFamily{familyIPv4, familyIPv6} {
|
for _, family := range addressFamilies {
|
||||||
klog.V(4).Infof("Looking for default routes with IPv%d addresses", uint(family))
|
klog.V(4).Infof("Looking for default routes with IPv%d addresses", uint(family))
|
||||||
for _, route := range routes {
|
for _, route := range routes {
|
||||||
if route.Family != family {
|
if route.Family != family {
|
||||||
@ -406,12 +417,19 @@ func chooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer) (net.IP,
|
|||||||
return nil, fmt.Errorf("unable to select an IP from default routes.")
|
return nil, fmt.Errorf("unable to select an IP from default routes.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// If bind-address is usable, return it directly
|
// ResolveBindAddress returns the IP address of a daemon, based on the given bindAddress:
|
||||||
// If bind-address is not usable (unset, 0.0.0.0, or loopback), we will use the host's default
|
// If bindAddress is unset, it returns the host's default IP, as with ChooseHostInterface().
|
||||||
// interface.
|
// If bindAddress is unspecified or loopback, it returns the default IP of the same
|
||||||
func ChooseBindAddress(bindAddress net.IP) (net.IP, error) {
|
// address family as bindAddress.
|
||||||
|
// Otherwise, it just returns bindAddress.
|
||||||
|
func ResolveBindAddress(bindAddress net.IP) (net.IP, error) {
|
||||||
|
addressFamilies := preferIPv4
|
||||||
|
if bindAddress != nil && memberOf(bindAddress, familyIPv6) {
|
||||||
|
addressFamilies = preferIPv6
|
||||||
|
}
|
||||||
|
|
||||||
if bindAddress == nil || bindAddress.IsUnspecified() || bindAddress.IsLoopback() {
|
if bindAddress == nil || bindAddress.IsUnspecified() || bindAddress.IsLoopback() {
|
||||||
hostIP, err := ChooseHostInterface()
|
hostIP, err := chooseHostInterface(addressFamilies)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -426,7 +444,7 @@ func ChooseBindAddress(bindAddress net.IP) (net.IP, error) {
|
|||||||
// 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.
|
// 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) {
|
func ChooseBindAddressForInterface(intfName string) (net.IP, error) {
|
||||||
var nw networkInterfacer = networkInterface{}
|
var nw networkInterfacer = networkInterface{}
|
||||||
for _, family := range []AddressFamily{familyIPv4, familyIPv6} {
|
for _, family := range preferIPv4 {
|
||||||
ip, err := getIPFromInterface(intfName, family, nw)
|
ip, err := getIPFromInterface(intfName, family, nw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -535,17 +535,21 @@ func TestChooseHostInterfaceFromRoute(t *testing.T) {
|
|||||||
tcase string
|
tcase string
|
||||||
routes []Route
|
routes []Route
|
||||||
nw networkInterfacer
|
nw networkInterfacer
|
||||||
|
order AddressFamilyPreference
|
||||||
expected net.IP
|
expected net.IP
|
||||||
}{
|
}{
|
||||||
{"ipv4", routeV4, validNetworkInterface{}, net.ParseIP("10.254.71.145")},
|
{"single-stack ipv4", routeV4, validNetworkInterface{}, preferIPv4, net.ParseIP("10.254.71.145")},
|
||||||
{"ipv6", routeV6, ipv6NetworkInterface{}, net.ParseIP("2001::200")},
|
{"single-stack ipv4, prefer v6", routeV4, validNetworkInterface{}, preferIPv6, net.ParseIP("10.254.71.145")},
|
||||||
{"prefer ipv4", bothRoutes, v4v6NetworkInterface{}, net.ParseIP("10.254.71.145")},
|
{"single-stack ipv6", routeV6, ipv6NetworkInterface{}, preferIPv4, net.ParseIP("2001::200")},
|
||||||
{"all LLA", routeV4, networkInterfaceWithOnlyLinkLocals{}, nil},
|
{"single-stack ipv6, prefer v6", routeV6, ipv6NetworkInterface{}, preferIPv6, net.ParseIP("2001::200")},
|
||||||
{"no routes", noRoutes, validNetworkInterface{}, nil},
|
{"dual stack", bothRoutes, v4v6NetworkInterface{}, preferIPv4, net.ParseIP("10.254.71.145")},
|
||||||
{"fail get IP", routeV4, networkInterfaceFailGetAddrs{}, nil},
|
{"dual stack, prefer v6", bothRoutes, v4v6NetworkInterface{}, preferIPv6, net.ParseIP("2001::10")},
|
||||||
|
{"all LLA", routeV4, networkInterfaceWithOnlyLinkLocals{}, preferIPv4, nil},
|
||||||
|
{"no routes", noRoutes, validNetworkInterface{}, preferIPv4, nil},
|
||||||
|
{"fail get IP", routeV4, networkInterfaceFailGetAddrs{}, preferIPv4, nil},
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
ip, err := chooseHostInterfaceFromRoute(tc.routes, tc.nw)
|
ip, err := chooseHostInterfaceFromRoute(tc.routes, tc.nw, tc.order)
|
||||||
if !ip.Equal(tc.expected) {
|
if !ip.Equal(tc.expected) {
|
||||||
t.Errorf("case[%v]: expected %v, got %+v .err : %v", tc.tcase, tc.expected, ip, err)
|
t.Errorf("case[%v]: expected %v, got %+v .err : %v", tc.tcase, tc.expected, ip, err)
|
||||||
}
|
}
|
||||||
@ -575,24 +579,29 @@ func TestGetIPFromHostInterfaces(t *testing.T) {
|
|||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
tcase string
|
tcase string
|
||||||
nw networkInterfacer
|
nw networkInterfacer
|
||||||
|
order AddressFamilyPreference
|
||||||
expected net.IP
|
expected net.IP
|
||||||
errStrFrag string
|
errStrFrag string
|
||||||
}{
|
}{
|
||||||
{"fail get I/Fs", failGettingNetworkInterface{}, nil, "failed getting all interfaces"},
|
{"fail get I/Fs", failGettingNetworkInterface{}, preferIPv4, nil, "failed getting all interfaces"},
|
||||||
{"no interfaces", noNetworkInterface{}, nil, "no interfaces"},
|
{"no interfaces", noNetworkInterface{}, preferIPv4, nil, "no interfaces"},
|
||||||
{"I/F not up", downNetworkInterface{}, nil, "no acceptable"},
|
{"I/F not up", downNetworkInterface{}, preferIPv4, nil, "no acceptable"},
|
||||||
{"loopback only", loopbackNetworkInterface{}, nil, "no acceptable"},
|
{"loopback only", loopbackNetworkInterface{}, preferIPv4, nil, "no acceptable"},
|
||||||
{"P2P I/F only", p2pNetworkInterface{}, nil, "no acceptable"},
|
{"P2P I/F only", p2pNetworkInterface{}, preferIPv4, nil, "no acceptable"},
|
||||||
{"fail get addrs", networkInterfaceFailGetAddrs{}, nil, "unable to get Addrs"},
|
{"fail get addrs", networkInterfaceFailGetAddrs{}, preferIPv4, nil, "unable to get Addrs"},
|
||||||
{"no addresses", networkInterfaceWithNoAddrs{}, nil, "no acceptable"},
|
{"no addresses", networkInterfaceWithNoAddrs{}, preferIPv4, nil, "no acceptable"},
|
||||||
{"invalid addr", networkInterfaceWithInvalidAddr{}, nil, "invalid CIDR"},
|
{"invalid addr", networkInterfaceWithInvalidAddr{}, preferIPv4, nil, "invalid CIDR"},
|
||||||
{"no matches", networkInterfaceWithOnlyLinkLocals{}, nil, "no acceptable"},
|
{"no matches", networkInterfaceWithOnlyLinkLocals{}, preferIPv4, nil, "no acceptable"},
|
||||||
{"ipv4", validNetworkInterface{}, net.ParseIP("10.254.71.145"), ""},
|
{"single-stack ipv4", validNetworkInterface{}, preferIPv4, net.ParseIP("10.254.71.145"), ""},
|
||||||
{"ipv6", ipv6NetworkInterface{}, net.ParseIP("2001::200"), ""},
|
{"single-stack ipv4, prefer ipv6", validNetworkInterface{}, preferIPv6, net.ParseIP("10.254.71.145"), ""},
|
||||||
|
{"single-stack ipv6", ipv6NetworkInterface{}, preferIPv4, net.ParseIP("2001::200"), ""},
|
||||||
|
{"single-stack ipv6, prefer ipv6", ipv6NetworkInterface{}, preferIPv6, net.ParseIP("2001::200"), ""},
|
||||||
|
{"dual stack", v4v6NetworkInterface{}, preferIPv4, net.ParseIP("10.254.71.145"), ""},
|
||||||
|
{"dual stack, prefer ipv6", v4v6NetworkInterface{}, preferIPv6, net.ParseIP("2001::10"), ""},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
ip, err := chooseIPFromHostInterfaces(tc.nw)
|
ip, err := chooseIPFromHostInterfaces(tc.nw, tc.order)
|
||||||
if !ip.Equal(tc.expected) {
|
if !ip.Equal(tc.expected) {
|
||||||
t.Errorf("case[%s]: expected %+v, got %+v with err : %v", tc.tcase, tc.expected, ip, err)
|
t.Errorf("case[%s]: expected %+v, got %+v with err : %v", tc.tcase, tc.expected, ip, err)
|
||||||
}
|
}
|
||||||
|
@ -109,10 +109,10 @@ func NewSecureServingOptions() *SecureServingOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *SecureServingOptions) DefaultExternalAddress() (net.IP, error) {
|
func (s *SecureServingOptions) DefaultExternalAddress() (net.IP, error) {
|
||||||
if !s.ExternalAddress.IsUnspecified() {
|
if s.ExternalAddress != nil && !s.ExternalAddress.IsUnspecified() {
|
||||||
return s.ExternalAddress, nil
|
return s.ExternalAddress, nil
|
||||||
}
|
}
|
||||||
return utilnet.ChooseBindAddress(s.BindAddress)
|
return utilnet.ResolveBindAddress(s.BindAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SecureServingOptions) Validate() []error {
|
func (s *SecureServingOptions) Validate() []error {
|
||||||
|
Loading…
Reference in New Issue
Block a user