mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
kube-proxy: only set route_localnet if required
kube-proxy sets the sysctl net.ipv4.conf.all.route_localnet=1 so NodePort services can be accessed on the loopback addresses in IPv4, but this may present security issues. Leverage the --nodeport-addresses flag to opt-out of this feature, if the list is not empty and none of the IP ranges contains an IPv4 loopback address this sysctl is not set. In addition, add a warning to inform users about this behavior.
This commit is contained in:
parent
c175418281
commit
8b5fa408e0
@ -264,9 +264,12 @@ func NewProxier(ipt utiliptables.Interface,
|
||||
healthzServer healthcheck.ProxierHealthUpdater,
|
||||
nodePortAddresses []string,
|
||||
) (*Proxier, error) {
|
||||
// Set the route_localnet sysctl we need for
|
||||
if err := utilproxy.EnsureSysctl(sysctl, sysctlRouteLocalnet, 1); err != nil {
|
||||
return nil, err
|
||||
if utilproxy.ContainsIPv4Loopback(nodePortAddresses) {
|
||||
// Set the route_localnet sysctl we need for exposing NodePorts on loopback addresses
|
||||
klog.InfoS("Setting route_localnet=1, use nodePortAddresses to filter loopback addresses for NodePorts to skip it https://issues.k8s.io/90259")
|
||||
if err := utilproxy.EnsureSysctl(sysctl, sysctlRouteLocalnet, 1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Proxy needs br_netfilter and bridge-nf-call-iptables=1 when containers
|
||||
|
@ -78,6 +78,37 @@ func BuildPortsToEndpointsMap(endpoints *v1.Endpoints) map[string][]string {
|
||||
return portsToEndpoints
|
||||
}
|
||||
|
||||
// ContainsIPv4Loopback returns true if the input is empty or one of the CIDR contains an IPv4 loopback address.
|
||||
func ContainsIPv4Loopback(cidrStrings []string) bool {
|
||||
if len(cidrStrings) == 0 {
|
||||
return true
|
||||
}
|
||||
// RFC 5735 127.0.0.0/8 - This block is assigned for use as the Internet host loopback address
|
||||
ipv4LoopbackStart := netutils.ParseIPSloppy("127.0.0.0")
|
||||
for _, cidr := range cidrStrings {
|
||||
if IsZeroCIDR(cidr) {
|
||||
return true
|
||||
}
|
||||
|
||||
ip, ipnet, err := netutils.ParseCIDRSloppy(cidr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if netutils.IsIPv6CIDR(ipnet) {
|
||||
continue
|
||||
}
|
||||
|
||||
if ip.IsLoopback() {
|
||||
return true
|
||||
}
|
||||
if ipnet.Contains(ipv4LoopbackStart) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsZeroCIDR checks whether the input CIDR string is either
|
||||
// the IPv4 or IPv6 zero CIDR
|
||||
func IsZeroCIDR(cidr string) bool {
|
||||
|
@ -1401,3 +1401,68 @@ func TestAddressSet(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainsIPv4Loopback(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
cidrStrings []string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "all zeros ipv4",
|
||||
cidrStrings: []string{"224.0.0.0/24", "192.168.0.0/16", "fd00:1:d::/64", "0.0.0.0/0"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "all zeros ipv4 and invalid cidr",
|
||||
cidrStrings: []string{"invalid.cidr", "192.168.0.0/16", "fd00:1:d::/64", "0.0.0.0/0"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "all zeros ipv6", // interpret all zeros equal for IPv4 and IPv6 as Golang stdlib
|
||||
cidrStrings: []string{"224.0.0.0/24", "192.168.0.0/16", "fd00:1:d::/64", "::/0"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "ipv4 loopback",
|
||||
cidrStrings: []string{"224.0.0.0/24", "192.168.0.0/16", "fd00:1:d::/64", "127.0.0.0/8"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "ipv6 loopback",
|
||||
cidrStrings: []string{"224.0.0.0/24", "192.168.0.0/16", "fd00:1:d::/64", "::1/128"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "ipv4 loopback smaller range",
|
||||
cidrStrings: []string{"224.0.0.0/24", "192.168.0.0/16", "fd00:1:d::/64", "127.0.2.0/28"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "ipv4 loopback within larger range",
|
||||
cidrStrings: []string{"224.0.0.0/24", "192.168.0.0/16", "fd00:1:d::/64", "64.0.0.0/2"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "non loop loopback",
|
||||
cidrStrings: []string{"128.0.2.0/28", "224.0.0.0/24", "192.168.0.0/16", "fd00:1:d::/64"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "invalid cidr",
|
||||
cidrStrings: []string{"invalid.ip/invalid.mask"},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := ContainsIPv4Loopback(tt.cidrStrings); got != tt.want {
|
||||
t.Errorf("ContainLoopback() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user