Commit 5f216ca3 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #33587 from bprashanth/esipp-nodeport-2

Automatic merge from submit-queue OnlyLocal nodeports 90% unittests. Code changes: * Jump to XLB from nodePorts for OnlyLocal nodeports * Jump to services chain from XLB for clusterCIDR (partially fixes https://github.com/kubernetes/kubernetes/issues/33081) NodePorts still don't get firewalls: https://github.com/kubernetes/kubernetes/issues/33586
parents 56be1976 06cbb36a
...@@ -177,6 +177,7 @@ type Proxier struct { ...@@ -177,6 +177,7 @@ type Proxier struct {
clusterCIDR string clusterCIDR string
hostname string hostname string
nodeIP net.IP nodeIP net.IP
portMapper portOpener
} }
type localPort struct { type localPort struct {
...@@ -194,6 +195,20 @@ type closeable interface { ...@@ -194,6 +195,20 @@ type closeable interface {
Close() error Close() error
} }
// portOpener is an interface around port opening/closing.
// Abstracted out for testing.
type portOpener interface {
OpenLocalPort(lp *localPort) (closeable, error)
}
// listenPortOpener opens ports by calling bind() and listen().
type listenPortOpener struct{}
// OpenLocalPort holds the given local port open.
func (l *listenPortOpener) OpenLocalPort(lp *localPort) (closeable, error) {
return openLocalPort(lp)
}
// Proxier implements ProxyProvider // Proxier implements ProxyProvider
var _ proxy.ProxyProvider = &Proxier{} var _ proxy.ProxyProvider = &Proxier{}
...@@ -241,6 +256,7 @@ func NewProxier(ipt utiliptables.Interface, sysctl utilsysctl.Interface, exec ut ...@@ -241,6 +256,7 @@ func NewProxier(ipt utiliptables.Interface, sysctl utilsysctl.Interface, exec ut
clusterCIDR: clusterCIDR, clusterCIDR: clusterCIDR,
hostname: hostname, hostname: hostname,
nodeIP: nodeIP, nodeIP: nodeIP,
portMapper: &listenPortOpener{},
}, nil }, nil
} }
...@@ -941,7 +957,7 @@ func (proxier *Proxier) syncProxyRules() { ...@@ -941,7 +957,7 @@ func (proxier *Proxier) syncProxyRules() {
glog.V(4).Infof("Port %s was open before and is still needed", lp.String()) glog.V(4).Infof("Port %s was open before and is still needed", lp.String())
replacementPortsMap[lp] = proxier.portsMap[lp] replacementPortsMap[lp] = proxier.portsMap[lp]
} else { } else {
socket, err := openLocalPort(&lp) socket, err := proxier.portMapper.OpenLocalPort(&lp)
if err != nil { if err != nil {
glog.Errorf("can't open %s, skipping this externalIP: %v", lp.String(), err) glog.Errorf("can't open %s, skipping this externalIP: %v", lp.String(), err)
continue continue
...@@ -1056,7 +1072,7 @@ func (proxier *Proxier) syncProxyRules() { ...@@ -1056,7 +1072,7 @@ func (proxier *Proxier) syncProxyRules() {
glog.V(4).Infof("Port %s was open before and is still needed", lp.String()) glog.V(4).Infof("Port %s was open before and is still needed", lp.String())
replacementPortsMap[lp] = proxier.portsMap[lp] replacementPortsMap[lp] = proxier.portsMap[lp]
} else { } else {
socket, err := openLocalPort(&lp) socket, err := proxier.portMapper.OpenLocalPort(&lp)
if err != nil { if err != nil {
glog.Errorf("can't open %s, skipping this nodePort: %v", lp.String(), err) glog.Errorf("can't open %s, skipping this nodePort: %v", lp.String(), err)
continue continue
...@@ -1070,10 +1086,16 @@ func (proxier *Proxier) syncProxyRules() { ...@@ -1070,10 +1086,16 @@ func (proxier *Proxier) syncProxyRules() {
"-m", protocol, "-p", protocol, "-m", protocol, "-p", protocol,
"--dport", fmt.Sprintf("%d", svcInfo.nodePort), "--dport", fmt.Sprintf("%d", svcInfo.nodePort),
} }
// Nodeports need SNAT. if !svcInfo.onlyNodeLocalEndpoints {
writeLine(natRules, append(args, "-j", string(KubeMarkMasqChain))...) // Nodeports need SNAT, unless they're local.
// Jump to the service chain. writeLine(natRules, append(args, "-j", string(KubeMarkMasqChain))...)
writeLine(natRules, append(args, "-j", string(svcChain))...) // Jump to the service chain.
writeLine(natRules, append(args, "-j", string(svcChain))...)
} else {
// TODO: Make all nodePorts jump to the firewall chain.
// Currently we only create it for loadbalancers (#33586).
writeLine(natRules, append(args, "-j", string(svcXlbChain))...)
}
} }
// If the service has no endpoints then reject packets. // If the service has no endpoints then reject packets.
...@@ -1173,6 +1195,16 @@ func (proxier *Proxier) syncProxyRules() { ...@@ -1173,6 +1195,16 @@ func (proxier *Proxier) syncProxyRules() {
localEndpointChains = append(localEndpointChains, endpointChains[i]) localEndpointChains = append(localEndpointChains, endpointChains[i])
} }
} }
// First rule in the chain redirects all pod -> external vip traffic to the
// Service's ClusterIP instead. This happens whether or not we have local
// endpoints.
args = []string{
"-A", string(svcXlbChain),
"-m", "comment", "--comment",
fmt.Sprintf(`"Redirect pods trying to reach external loadbalancer VIP to clusterIP"`),
}
writeLine(natRules, append(args, "-s", proxier.clusterCIDR, "-j", string(svcChain))...)
numLocalEndpoints := len(localEndpointChains) numLocalEndpoints := len(localEndpointChains)
if numLocalEndpoints == 0 { if numLocalEndpoints == 0 {
// Blackhole all traffic since there are no local endpoints // Blackhole all traffic since there are no local endpoints
......
...@@ -16,60 +16,107 @@ limitations under the License. ...@@ -16,60 +16,107 @@ limitations under the License.
package testing package testing
import "k8s.io/kubernetes/pkg/util/iptables" import (
"fmt"
"strings"
"k8s.io/kubernetes/pkg/util/iptables"
)
const (
Destination = "-d "
Source = "-s "
DPort = "--dport "
Protocol = "-p "
Jump = "-j "
Reject = "REJECT"
ToDest = "--to-destination "
)
type Rule map[string]string
// no-op implementation of iptables Interface // no-op implementation of iptables Interface
type fake struct{} type FakeIPTables struct {
Lines []byte
}
func NewFake() *fake { func NewFake() *FakeIPTables {
return &fake{} return &FakeIPTables{}
} }
func (*fake) GetVersion() (string, error) { func (*FakeIPTables) GetVersion() (string, error) {
return "0.0.0", nil return "0.0.0", nil
} }
func (*fake) EnsureChain(table iptables.Table, chain iptables.Chain) (bool, error) { func (*FakeIPTables) EnsureChain(table iptables.Table, chain iptables.Chain) (bool, error) {
return true, nil return true, nil
} }
func (*fake) FlushChain(table iptables.Table, chain iptables.Chain) error { func (*FakeIPTables) FlushChain(table iptables.Table, chain iptables.Chain) error {
return nil return nil
} }
func (*fake) DeleteChain(table iptables.Table, chain iptables.Chain) error { func (*FakeIPTables) DeleteChain(table iptables.Table, chain iptables.Chain) error {
return nil return nil
} }
func (*fake) EnsureRule(position iptables.RulePosition, table iptables.Table, chain iptables.Chain, args ...string) (bool, error) { func (*FakeIPTables) EnsureRule(position iptables.RulePosition, table iptables.Table, chain iptables.Chain, args ...string) (bool, error) {
return true, nil return true, nil
} }
func (*fake) DeleteRule(table iptables.Table, chain iptables.Chain, args ...string) error { func (*FakeIPTables) DeleteRule(table iptables.Table, chain iptables.Chain, args ...string) error {
return nil return nil
} }
func (*fake) IsIpv6() bool { func (*FakeIPTables) IsIpv6() bool {
return false return false
} }
func (*fake) Save(table iptables.Table) ([]byte, error) { func (*FakeIPTables) Save(table iptables.Table) ([]byte, error) {
return make([]byte, 0), nil return make([]byte, 0), nil
} }
func (*fake) SaveAll() ([]byte, error) { func (*FakeIPTables) SaveAll() ([]byte, error) {
return make([]byte, 0), nil return make([]byte, 0), nil
} }
func (*fake) Restore(table iptables.Table, data []byte, flush iptables.FlushFlag, counters iptables.RestoreCountersFlag) error { func (*FakeIPTables) Restore(table iptables.Table, data []byte, flush iptables.FlushFlag, counters iptables.RestoreCountersFlag) error {
return nil return nil
} }
func (*fake) RestoreAll(data []byte, flush iptables.FlushFlag, counters iptables.RestoreCountersFlag) error { func (f *FakeIPTables) RestoreAll(data []byte, flush iptables.FlushFlag, counters iptables.RestoreCountersFlag) error {
f.Lines = data
return nil return nil
} }
func (*fake) AddReloadFunc(reloadFunc func()) {} func (*FakeIPTables) AddReloadFunc(reloadFunc func()) {}
func (*fake) Destroy() {} func (*FakeIPTables) Destroy() {}
func getToken(line, seperator string) string {
tokens := strings.Split(line, seperator)
if len(tokens) == 2 {
return strings.Split(tokens[1], " ")[0]
}
return ""
}
// GetChain returns a list of rules for the givne chain.
// The chain name must match exactly.
// The matching is pretty dumb, don't rely on it for anything but testing.
func (f *FakeIPTables) GetRules(chainName string) (rules []Rule) {
for _, l := range strings.Split(string(f.Lines), "\n") {
if strings.Contains(l, fmt.Sprintf("-A %v", chainName)) {
newRule := Rule(map[string]string{})
for _, arg := range []string{Destination, Source, DPort, Protocol, Jump, ToDest} {
tok := getToken(l, arg)
if tok != "" {
newRule[arg] = tok
}
}
rules = append(rules, newRule)
}
}
return
}
var _ = iptables.Interface(&fake{}) var _ = iptables.Interface(&FakeIPTables{})
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment