Change:
When using loadBalancerSourceRanges to allow access only from CIDRs outside the cluster segment, access from the K8s nodes (or pods with hostNetwork:true) should be rejected, but for proxy-mode=ipvs it works. /sig network /area kube-proxy /area ipvs Access from K8s nodes should be rejected in the same way as for proxy-mode=iptables Define a service with loadBalancerSourceRanges that allows access from a cidr outside the cluster, e.g. The K8s nodes have addresses on 192.168.1.0/24 and fd00::c0a8:100/120. Make sure a loadBalancerIP is assigned (in this example 10.0.0.0/32) and try to access it from a K8s node: Same for both ipv4 and ipv6. This is related to since a fix requires that the passed to the is correct. Please see v1.27.4 and master None Linux 6.4.4 None N/A N/A
wanted to work on this
Great! But please coordinate with I hope that one get accepted, and then ~the s (both families) passed to the proxiers should be usable.~ No, they are not, please see
Discussed this one in the SIG Network meeting today: we agree this is something we'd like to implement, however we weren't sure exactly how to do it with ipvs during the meeting. Whoever takes this on needs to come up with and communicate a clear plan of how it will be accomplished. /triage accepted
My comment is not feasible. For proxy-mode=ipvs the src and dst is both loadBalancerIP. So the nodeIP will not help :disappointed:
I did a small experimented with kind + metallb. I created a loadbalancer service backed by . loadBalancerIP: 172.18.255.200 podIP: 10.244.1.3 conntrack picks the src and dst both as loadBalancerIP. Tried to create an iptables rules matching source and destination with loadbalancerIP to drop packets but it didn't work. After some exploration I found and with the following rules I was able to prevent access to lbIP from k8s nodes / host network. The solution looks hacky though, we only need this rule if source ranges are defined for the service and node ip is not covered by any of them. Will try to program this in the ipvs proxier.
Good find! :+1: I had missed the ipvs extension. I started to check nodeIP and the (flawed) option to experiment with setting a nodeIP as "src" for some routes to dodge the lbip-both-as-src-and-dst problem, and an old "vision" I have to not assign clusterIPs to at all, but have one rule to replace them all, like: It can be done, and in a cluster with 1000's of clusterIP's it would simplify

Discussion:
// https://github.com/kubernetes/kubernetes/issues/72236 kubeIPVSFilterChain utiliptables.Chain = "KUBE-IPVS-FILTER" <ins> // kubeIPVSOutFilterChain filters access to load balancer services from node. // https://github.com/kubernetes/kubernetes/issues/119656 kubeIPVSOutFilterChain utiliptables.Chain = "KUBE-IPVS-OUT-FILTER" </ins> // defaultScheduler is the default ipvs scheduler algorithm - round robin. defaultScheduler = "rr"

{utiliptables.TableFilter, utiliptables.ChainInput, kubeProxyFirewallChain, "kube-proxy firewall rules"}, {utiliptables.TableFilter, utiliptables.ChainForward, kubeProxyFirewallChain, "kube-proxy firewall rules"}, {utiliptables.TableFilter, utiliptables.ChainInput, kubeIPVSFilterChain, "kubernetes ipvs access filter"}, <ins> {utiliptables.TableFilter, utiliptables.ChainOutput, kubeIPVSOutFilterChain, "kubernetes ipvs access filter"}, </ins> } var iptablesChains = []struct {

{utiliptables.TableFilter, kubeProxyFirewallChain}, {utiliptables.TableFilter, kubeSourceRangesFirewallChain}, {utiliptables.TableFilter, kubeIPVSFilterChain}, <ins> {utiliptables.TableFilter, kubeIPVSOutFilterChain}, </ins> } var iptablesCleanupChains = []struct {

{utiliptables.TableFilter, kubeProxyFirewallChain}, {utiliptables.TableFilter, kubeSourceRangesFirewallChain}, {utiliptables.TableFilter, kubeIPVSFilterChain}, <ins> {utiliptables.TableFilter, kubeIPVSOutFilterChain}, </ins> } // ipsetInfo is all ipset we needed in ipvs proxier

// A Set is used here since we end up calculating endpoint topology multiple times for the same Service // if it has multiple ports but each Service should only be counted once. serviceNoLocalEndpointsExternal sets.Set[string] <ins> // lbNoNodeAccessIPPortProtocolEntries represents the set of loadBalancers IP + Port + Protocol that should not be accessible from K8s nodes // We cannot directly restrict LB access from node using LoadBalancerSourceRanges, we need to install // additional iptables rules. // (ref: https://github.com/kubernetes/kubernetes/issues/119656) lbNoNodeAccessIPPortProtocolEntries []*utilipset.Entry </ins> } // Proxier implements proxy.Provider

proxier.serviceNoLocalEndpointsInternal = sets.New[string]() proxier.serviceNoLocalEndpointsExternal = sets.New[string]() <ins> proxier.lbNoNodeAccessIPPortProtocolEntries = make([]*utilipset.Entry, 0) </ins> // Begin install iptables // Reset all buffers used later.

continue } proxier.ipsetList[kubeLoadBalancerSourceIPSet].activeEntries.Insert(entry.String()) <ins> } else { // since nodeIP is not covered in any of SourceRange we need to explicitly block the lbIP access from k8s nodes. proxier.lbNoNodeAccessIPPortProtocolEntries = append(proxier.lbNoNodeAccessIPPortProtocolEntries, entry) </ins> } } // ipvs call

"-j", "DROP", ) <ins> // disable LB access from node // for IPVS src and dst both would be lbIP for _, entry := range proxier.lbNoNodeAccessIPPortProtocolEntries { proxier.filterRules.Write( "-A", string(kubeIPVSOutFilterChain), "-s", entry.IP, "-m", "ipvs", "--vaddr", entry.IP, "--vproto", entry.Protocol, "--vport", strconv.Itoa(entry.Port), "-j", "DROP", ) } </ins> // Accept all traffic with destination of ipvs virtual service, in case other iptables rules // block the traffic, that may result in ipvs rules invalid. // Those rules must be in the end of KUBE-SERVICE chain

:KUBE-PROXY-FIREWALL - [0:0] :KUBE-SOURCE-RANGES-FIREWALL - [0:0] :KUBE-IPVS-FILTER - [0:0] <ins> :KUBE-IPVS-OUT-FILTER - [0:0] </ins> ` assert.Equal(t, expectedNATChains, fp.natChains.String()) assert.Equal(t, expectedFilterChains, fp.filterChains.String())