Change:
Just similar to , for service with type nodeport , iptable proxy should add IsLoopback check for nodeAddress. Line 1104-1106 check nodeAddress is localhost address or not for ipvs mode proxy but for iptable mode proxy , when deal with nodePort type service, there is no similar check for nodeAddress since the use of localhost addresses for nodeports is the only know reason to require (), Just for the purpose of security ,this requirement shall be removed for proxy-mode=itable. Start a cluster with proxy-mode=iptable and some services Do; iptable -Ln | grep 127.0.0.1 just as says : ipv6+iptables , ::1 is not work for Nodeport all supporting proxy-mode=iptable any any /sig network /kind cleanup /area iptable
/sig network
The label(s) cannot be applied, because the repository doesn't have them. <detailsIn response to : /area iptable Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/kind cleanup
maybe I'm missing something, but this was discussed in this issue and iptables needs to use localhost for nodeports, if we remove that we can break users. ipvs is different and it never needed loopback for nodeports
set sysctlRouteLocalnet when running in IPtable mode may still face a risk from CVE-2020-8558, so why not disable it in the future version?
because there are users that depend on that behavior, I can't remember the details , the referenced issue has them
I support finding a way to get to the end-state of not supporting nodeports on localhost, but I don't know the path.
/assign
it seems that localhost;nodeport is also used in blogs and forums the only tool we have for these situations is to add a feature gate and rollout the change in several releases, users can always set the sysctl value by themselves to recover previous behavior. should I create a small KEP to deprecate the behavior of setting the TTL? since is inconsistent with the other kube-proxy modes, there is a security risk and the workaround is quite simple, it seems a reasonable decision
I don't think we have a clear path to changing a defsult behavior, even one that is ill-advised. As much as I'd like it to be the default, I think the only way to get there is to add a flag with compatible default behavior and encourage people to turn it on. e.g. add a kube-proxy config like defaulting to true, and encourage people/tools to set it to false.
I've found a better way ;)
/triage accepted

Discussion:
healthzServer healthcheck.ProxierHealthUpdater, nodePortAddresses []string, ) (*Proxier, error) { <del> // Set the route_localnet sysctl we need for if err := utilproxy.EnsureSysctl(sysctl, sysctlRouteLocalnet, 1); err != nil { return nil, err </del> <ins> 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 } </ins> } // Proxy needs br_netfilter and bridge-nf-call-iptables=1 when containers

return portsToEndpoints } <ins> // 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 } </ins> // IsZeroCIDR checks whether the input CIDR string is either // the IPv4 or IPv6 zero CIDR func IsZeroCIDR(cidr string) bool {

} } } <ins> 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) } }) } } </ins>