From 2ec87999a97eb68557fc7fdc9f42e615f6b61f9e Mon Sep 17 00:00:00 2001 From: Tim Hockin Date: Mon, 20 Mar 2017 15:54:43 -0700 Subject: [PATCH] Install a REJECT rule for nodeport with no backend Rather than actually accepting the connection, REJECT. This will avoid CLOSE_WAIT. --- pkg/proxy/iptables/proxier.go | 17 +++++++++++++++++ pkg/proxy/iptables/proxier_test.go | 19 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/pkg/proxy/iptables/proxier.go b/pkg/proxy/iptables/proxier.go index e77f77e861..b2f6c0b012 100644 --- a/pkg/proxy/iptables/proxier.go +++ b/pkg/proxy/iptables/proxier.go @@ -1108,6 +1108,21 @@ func (proxier *Proxier) syncProxyRules() { // Currently we only create it for loadbalancers (#33586). writeLine(natRules, append(args, "-j", string(svcXlbChain))...) } + + // If the service has no endpoints then reject packets. The filter + // table doesn't currently have the same per-service structure that + // the nat table does, so we just stick this into the kube-services + // chain. + if len(proxier.endpointsMap[svcName]) == 0 { + writeLine(filterRules, + "-A", string(kubeServicesChain), + "-m", "comment", "--comment", fmt.Sprintf(`"%s has no endpoints"`, svcName.String()), + "-m", "addrtype", "--dst-type", "LOCAL", + "-m", protocol, "-p", protocol, + "--dport", fmt.Sprintf("%d", svcInfo.nodePort), + "-j", "REJECT", + ) + } } // If the service has no endpoints then reject packets. @@ -1123,6 +1138,8 @@ func (proxier *Proxier) syncProxyRules() { continue } + // From here on, we assume there are active endpoints. + // Generate the per-endpoint chains. We do this in multiple passes so we // can group rules together. // These two slices parallel each other - keep in sync diff --git a/pkg/proxy/iptables/proxier_test.go b/pkg/proxy/iptables/proxier_test.go index fb409f1270..b3ac6390db 100644 --- a/pkg/proxy/iptables/proxier_test.go +++ b/pkg/proxy/iptables/proxier_test.go @@ -694,6 +694,25 @@ func TestNodePort(t *testing.T) { } } +func TestNodePortReject(t *testing.T) { + ipt := iptablestest.NewFake() + fp := NewFakeProxier(ipt) + svcName := "svc1" + svcIP := net.IPv4(10, 20, 30, 41) + + svc := proxy.ServicePortName{NamespacedName: types.NamespacedName{Namespace: "ns1", Name: svcName}, Port: "p80"} + svcInfo := newFakeServiceInfo(svc, svcIP, 80, api.ProtocolTCP, false) + svcInfo.nodePort = 3001 + fp.serviceMap[svc] = svcInfo + + fp.syncProxyRules() + + kubeSvcRules := ipt.GetRules(string(kubeServicesChain)) + if !hasJump(kubeSvcRules, iptablestest.Reject, svcIP.String(), 3001) { + errorf(fmt.Sprintf("Failed to find a %v rule for service %v with no endpoints", iptablestest.Reject, svcName), kubeSvcRules, t) + } +} + func strPtr(s string) *string { return &s }