Skip to content

Commit 5a4c6c2

Browse files
authored
Merge pull request kubernetes#83475 from fabriziopandini/127.0.0.1-as-advertise-address
Kubeadm: allow users to use 127.0.0.1 as advertise address
2 parents 386a27f + fd2c678 commit 5a4c6c2

File tree

4 files changed

+54
-6
lines changed

4 files changed

+54
-6
lines changed

cmd/kubeadm/app/util/config/common.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,14 @@ func VerifyAPIServerBindAddress(address string) error {
123123
if ip == nil {
124124
return errors.Errorf("cannot parse IP address: %s", address)
125125
}
126+
// There are users with network setups where default routes are present, but network interfaces
127+
// use only link-local addresses (e.g. as described in RFC5549).
128+
// In many cases that matching global unicast IP address can be found on loopback interface,
129+
// so kubeadm allows users to specify address=Loopback for handling supporting the scenario above.
130+
// Nb. SetAPIEndpointDynamicDefaults will try to translate loopback to a valid address afterwards
131+
if ip.IsLoopback() {
132+
return nil
133+
}
126134
if !ip.IsGlobalUnicast() {
127135
return errors.Errorf("cannot use %q as the bind address for the API Server", address)
128136
}

cmd/kubeadm/app/util/config/common_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,13 +161,13 @@ func TestVerifyAPIServerBindAddress(t *testing.T) {
161161
address: "2001:db8:85a3::8a2e:370:7334",
162162
},
163163
{
164-
name: "invalid address: not a global unicast 0.0.0.0",
165-
address: "0.0.0.0",
166-
expectedError: true,
164+
name: "valid address 127.0.0.1",
165+
address: "127.0.0.1",
166+
expectedError: false,
167167
},
168168
{
169-
name: "invalid address: not a global unicast 127.0.0.1",
170-
address: "127.0.0.1",
169+
name: "invalid address: not a global unicast 0.0.0.0",
170+
address: "0.0.0.0",
171171
expectedError: true,
172172
},
173173
{

cmd/kubeadm/app/util/config/initconfiguration.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@ import (
2626
"github.com/pkg/errors"
2727
"k8s.io/klog"
2828

29-
"k8s.io/api/core/v1"
29+
v1 "k8s.io/api/core/v1"
3030
"k8s.io/apimachinery/pkg/runtime"
3131
"k8s.io/apimachinery/pkg/runtime/schema"
32+
netutil "k8s.io/apimachinery/pkg/util/net"
3233
bootstraputil "k8s.io/cluster-bootstrap/token/util"
3334
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
3435
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
@@ -112,6 +113,22 @@ func SetAPIEndpointDynamicDefaults(cfg *kubeadmapi.APIEndpoint) error {
112113
if addressIP == nil && cfg.AdvertiseAddress != "" {
113114
return errors.Errorf("couldn't use \"%s\" as \"apiserver-advertise-address\", must be ipv4 or ipv6 address", cfg.AdvertiseAddress)
114115
}
116+
117+
// kubeadm allows users to specify address=Loopback as a selector for global unicast IP address that can be found on loopback interface.
118+
// 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).
119+
if addressIP.IsLoopback() {
120+
loopbackIP, err := netutil.ChooseBindAddressForInterface(netutil.LoopbackInterfaceName)
121+
if err != nil {
122+
return err
123+
}
124+
if loopbackIP != nil {
125+
klog.V(4).Infof("Found active IP %v on loopback interface", loopbackIP.String())
126+
cfg.AdvertiseAddress = loopbackIP.String()
127+
return nil
128+
}
129+
return errors.New("unable to resolve link-local addresses")
130+
}
131+
115132
// 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
116133
// for bootstrapping a cluster.
117134
ip, err := ChooseAPIServerBindAddress(addressIP)

staging/src/k8s.io/apimachinery/pkg/util/net/interface.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ const (
3636
familyIPv6 AddressFamily = 6
3737
)
3838

39+
const (
40+
// LoopbackInterfaceName is the default name of the loopback interface
41+
LoopbackInterfaceName = "lo"
42+
)
43+
3944
const (
4045
ipv4RouteFile = "/proc/net/route"
4146
ipv6RouteFile = "/proc/net/ipv6_route"
@@ -414,3 +419,21 @@ func ChooseBindAddress(bindAddress net.IP) (net.IP, error) {
414419
}
415420
return bindAddress, nil
416421
}
422+
423+
// ChooseBindAddressForInterface choose a global IP for a specific interface, with priority given to IPv4.
424+
// This is required in case of network setups where default routes are present, but network
425+
// interfaces use only link-local addresses (e.g. as described in RFC5549).
426+
// 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.
427+
func ChooseBindAddressForInterface(intfName string) (net.IP, error) {
428+
var nw networkInterfacer = networkInterface{}
429+
for _, family := range []AddressFamily{familyIPv4, familyIPv6} {
430+
ip, err := getIPFromInterface(intfName, family, nw)
431+
if err != nil {
432+
return nil, err
433+
}
434+
if ip != nil {
435+
return ip, nil
436+
}
437+
}
438+
return nil, fmt.Errorf("unable to select an IP from %s network interface", intfName)
439+
}

0 commit comments

Comments
 (0)