Change:
} else { clusterEndpoints, localEndpoints, _, _ := proxy.CategorizeEndpoints(endpoints, svcInfo, proxier.nodeLabels) if onlyNodeLocalEndpoints { <del> endpoints = localEndpoints </del> <ins> if len(localEndpoints) > 0 { endpoints = localEndpoints } else { // https://github.com/kubernetes/kubernetes/pull/97081 // Allow access from local PODs even if no local endpoints exist. // Traffic from an external source will be routed but the reply // will have the POD address and will be discarded. endpoints = clusterEndpoints } </ins> } else { endpoints = clusterEndpoints }

Discussion:
<!-- Please only use this template for submitting enhancement requests --What would you like to be : In-cluster traffic for a Service loadbalancer IP with externalTrafficPolicy=Local should be masqueraded and routed to cluster-wide endpoints. A similar patch was to the iptables proxier in v1.15 in Why is this needed: Today, the LB IP traffic for the ipvs proxier is not differentiated from "Local" and "External" traffic, so in the case where a Service is using and traffic is originating in-cluster, the traffic will be black-holed if there are no local endpoints there.
/assign /sig network
/area ipvs
/triage unresolved Comment when the issue is assessed and confirmed. I am a bot run by vllry. ‍
/remove-triage unresolved
Patching kube-proxy with solves this issue.
Issues go stale after 90d of inactivity. Mark the issue as fresh with . Stale issues rot after an additional 30d of inactivity and eventually close. If this issue is safe to close now please do so with . Send feedback to sig-testing, kubernetes/test-infra and/or . /lifecycle stale
/remove-lifecycle stale
I tried the proposed fix in From within a POD on a node where no server PODs are executing it works fine, with the source (POD) address preserved. From an external machine where incoming packets hit a node without server PODs (where it usually is black-holed) packets goes like this; On the k8s-node without server DNAT happens VIP -POD-address. The problem is that the reply takes a short-cut (much like a DSR) but the source is now the POD address (where it should have been nat'ed back to the VIP). The reply goes all the way back to the sender who have no idea about any connection with the POD-address as source and drops it. But if the external traffic setup is correct then external traffic should never hit any nodes where the server PODs are not executing. So IMHO this fix is OK.

Here is a trace from an external source showing the SYN to the VIP address (10.0.0.90) but the SYN-ACK has the POD address (11.0.4.3) as source:
Traffic from the main netns (e.g hostNetwork:True) from a k8s-node without server fails for a different reason than traffic from an external machine. Traffic from main netns on a k8s-node will get the VIP as source! This because the assign of the address to dev sets up a local route as; SYN is sent OK (e.g 10.0.0.90: -10.0.0.90:80) but the reply to the VIP address fails. This can be "fixed" by changing the route and set the node-ip as src; Then it works.
Is it possible to add the proposed solution in in kube-proxy? It would solve the problem for all users except for PODs with and have no significant draw-backs as far as I can tell.
I will take a look at that PR soon
Would you mind if I create a PR with your update described in ? Or would you prefer to do it yourself?
Hello. To solve this problem, I developed a kubernetes controller called node-network-manager. node-network-manager works on all kubernetes nodes and adds iptables DNAT rules from CLUSTER-IP to Load Balancer IP. Please try this and give me feedback. Thanks.
/area kube-proxy
Issues go stale after 90d of inactivity. Mark the issue as fresh with . Stale issues rot after an additional 30d of inactivity and eventually close. If this issue is safe to close now please do so with . Send feedback to sig-contributor-experience at . /lifecycle stale
/remove-lifecycle stale There is a pending PR
The Kubernetes project currently lacks enough contributors to adequately respond to all issues and PRs.

This bot triages issues and PRs according to the following rules: After 90d of inactivity, is applied After 30d of inactivity since was applied, is applied After 30d of inactivity since was applied, the issue is closed You can: Mark this issue or PR as fresh with Mark this issue or PR as rotten with Close this issue or PR with Offer to help out with [Issue Triage][1] Please send feedback to sig-contributor-experience at . /lifecycle stale [1]:
/remove-lifecycle stale
The Kubernetes project currently lacks enough contributors to adequately respond to all issues and PRs. This bot triages issues and PRs according to the following rules: After 90d of inactivity, is applied After 30d of inactivity since was applied, is applied After 30d of inactivity since was applied, the issue is closed You can: Mark this issue or PR as fresh with Mark this issue or PR as rotten with Close this issue or PR with Offer to help out with [Issue Triage][1] Please send feedback to sig-contributor-experience at . /lifecycle stale [1]:
/remove-lifecycle stale There is a pending PR
The Kubernetes project currently lacks enough contributors to adequately respond to all issues and PRs. This bot triages issues and PRs according to the following rules: After 90d of inactivity, is applied After 30d of inactivity since was applied, is applied After 30d of inactivity since was applied, the issue is closed You can: Mark this issue or PR as fresh with Mark this issue or PR as rotten with Close this issue or PR with Offer to help out with [Issue Triage][1] Please send feedback to sig-contributor-experience at . /lifecycle stale [1]:
hello. Please tell me. Did you manage to reach external-ip from host node ? Without a way to remove or replace the route and with src ip ? I have the same problems. When accessed from POD, it works ok. But when accessing from the host node to external-ip, it does not work. Replacing or removing the route from external-ip on the kube-ipvs0 interface helps. Is there any working solution for this?
No, sorry.

I think that the route must be replaced to make it work, as described in But that is a much more intrusive (i.e dangerous) update than the tiny fix in