Merge pull request #99127 from danwinship/non-dual-stack-proxy

Make kube-proxy check if IPv6 is really supported before assuming dual-stack
This commit is contained in:
Kubernetes Prow Robot 2021-02-18 08:46:51 -08:00 committed by GitHub
commit 9fb1aa92f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 56 additions and 39 deletions

View File

@ -134,15 +134,7 @@ func newProxyServer(
} }
nodeIP := detectNodeIP(client, hostname, config.BindAddress) nodeIP := detectNodeIP(client, hostname, config.BindAddress)
protocol := utiliptables.ProtocolIPv4 klog.Infof("Detected node IP %s", nodeIP.String())
if utilsnet.IsIPv6(nodeIP) {
klog.V(0).Infof("kube-proxy node IP is an IPv6 address (%s), assume IPv6 operation", nodeIP.String())
protocol = utiliptables.ProtocolIPv6
} else {
klog.V(0).Infof("kube-proxy node IP is an IPv4 address (%s), assume IPv4 operation", nodeIP.String())
}
iptInterface = utiliptables.New(execer, protocol)
// Create event recorder // Create event recorder
eventBroadcaster := record.NewBroadcaster() eventBroadcaster := record.NewBroadcaster()
@ -181,6 +173,38 @@ func newProxyServer(
klog.V(2).Info("DetectLocalMode: '", string(detectLocalMode), "'") klog.V(2).Info("DetectLocalMode: '", string(detectLocalMode), "'")
primaryProtocol := utiliptables.ProtocolIPv4
if utilsnet.IsIPv6(nodeIP) {
primaryProtocol = utiliptables.ProtocolIPv6
}
iptInterface = utiliptables.New(execer, primaryProtocol)
var ipt [2]utiliptables.Interface
dualStack := utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) && proxyMode != proxyModeUserspace
if dualStack {
// Create iptables handlers for both families, one is already created
// Always ordered as IPv4, IPv6
if primaryProtocol == utiliptables.ProtocolIPv4 {
ipt[0] = iptInterface
ipt[1] = utiliptables.New(execer, utiliptables.ProtocolIPv6)
// Just because the feature gate is enabled doesn't mean the node
// actually supports dual-stack
if _, err := ipt[1].ChainExists(utiliptables.TableNAT, utiliptables.ChainPostrouting); err != nil {
klog.Warningf("No iptables support for IPv6: %v", err)
dualStack = false
}
} else {
ipt[0] = utiliptables.New(execer, utiliptables.ProtocolIPv4)
ipt[1] = iptInterface
}
}
if dualStack {
klog.V(0).Infof("kube-proxy running in dual-stack mode, %s-primary", iptInterface.Protocol())
} else {
klog.V(0).Infof("kube-proxy running in single-stack %s mode", iptInterface.Protocol())
}
if proxyMode == proxyModeIPTables { if proxyMode == proxyModeIPTables {
klog.V(0).Info("Using iptables Proxier.") klog.V(0).Info("Using iptables Proxier.")
if config.IPTables.MasqueradeBit == nil { if config.IPTables.MasqueradeBit == nil {
@ -188,20 +212,9 @@ func newProxyServer(
return nil, fmt.Errorf("unable to read IPTables MasqueradeBit from config") return nil, fmt.Errorf("unable to read IPTables MasqueradeBit from config")
} }
if utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) { if dualStack {
klog.V(0).Info("creating dualStackProxier for iptables.") klog.V(0).Info("creating dualStackProxier for iptables.")
// Create iptables handlers for both families, one is already created
// Always ordered as IPv4, IPv6
var ipt [2]utiliptables.Interface
if iptInterface.IsIPv6() {
ipt[1] = iptInterface
ipt[0] = utiliptables.New(execer, utiliptables.ProtocolIPv4)
} else {
ipt[0] = iptInterface
ipt[1] = utiliptables.New(execer, utiliptables.ProtocolIPv6)
}
// Always ordered to match []ipt // Always ordered to match []ipt
var localDetectors [2]proxyutiliptables.LocalTrafficDetector var localDetectors [2]proxyutiliptables.LocalTrafficDetector
localDetectors, err = getDualStackLocalDetectorTuple(detectLocalMode, config, ipt, nodeInfo) localDetectors, err = getDualStackLocalDetectorTuple(detectLocalMode, config, ipt, nodeInfo)
@ -256,20 +269,9 @@ func newProxyServer(
proxymetrics.RegisterMetrics() proxymetrics.RegisterMetrics()
} else if proxyMode == proxyModeIPVS { } else if proxyMode == proxyModeIPVS {
klog.V(0).Info("Using ipvs Proxier.") klog.V(0).Info("Using ipvs Proxier.")
if utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) { if dualStack {
klog.V(0).Info("creating dualStackProxier for ipvs.") klog.V(0).Info("creating dualStackProxier for ipvs.")
// Create iptables handlers for both families, one is already created
// Always ordered as IPv4, IPv6
var ipt [2]utiliptables.Interface
if iptInterface.IsIPv6() {
ipt[1] = iptInterface
ipt[0] = utiliptables.New(execer, utiliptables.ProtocolIPv4)
} else {
ipt[0] = iptInterface
ipt[1] = utiliptables.New(execer, utiliptables.ProtocolIPv6)
}
nodeIPs := nodeIPTuple(config.BindAddress) nodeIPs := nodeIPTuple(config.BindAddress)
// Always ordered to match []ipt // Always ordered to match []ipt

View File

@ -123,6 +123,14 @@ func (f *fakeIPTables) DeleteChain(tableName utiliptables.Table, chainName utili
return nil return nil
} }
func (f *fakeIPTables) ChainExists(tableName utiliptables.Table, chainName utiliptables.Chain) (bool, error) {
_, _, err := f.getChain(tableName, chainName)
if err != nil {
return false, err
}
return true, nil
}
// Returns index of rule in array; < 0 if rule is not found // Returns index of rule in array; < 0 if rule is not found
func findRule(chain *fakeChain, rule string) int { func findRule(chain *fakeChain, rule string) int {
for i, candidate := range chain.rules { for i, candidate := range chain.rules {

View File

@ -51,6 +51,9 @@ type Interface interface {
FlushChain(table Table, chain Chain) error FlushChain(table Table, chain Chain) error
// DeleteChain deletes the specified chain. If the chain did not exist, return error. // DeleteChain deletes the specified chain. If the chain did not exist, return error.
DeleteChain(table Table, chain Chain) error DeleteChain(table Table, chain Chain) error
// ChainExists tests whether the specified chain exists, returning an error if it
// does not, or if it is unable to check.
ChainExists(table Table, chain Chain) (bool, error)
// EnsureRule checks if the specified rule is present and, if not, creates it. If the rule existed, return true. // EnsureRule checks if the specified rule is present and, if not, creates it. If the rule existed, return true.
EnsureRule(position RulePosition, table Table, chain Chain, args ...string) (bool, error) EnsureRule(position RulePosition, table Table, chain Chain, args ...string) (bool, error)
// DeleteRule checks if the specified rule is present and, if so, deletes it. // DeleteRule checks if the specified rule is present and, if so, deletes it.
@ -570,7 +573,7 @@ func (runner *runner) Monitor(canary Chain, tables []Table, reloadFunc func(), i
// Poll until stopCh is closed or iptables is flushed // Poll until stopCh is closed or iptables is flushed
err := utilwait.PollUntil(interval, func() (bool, error) { err := utilwait.PollUntil(interval, func() (bool, error) {
if exists, err := runner.chainExists(tables[0], canary); exists { if exists, err := runner.ChainExists(tables[0], canary); exists {
return false, nil return false, nil
} else if isResourceError(err) { } else if isResourceError(err) {
klog.Warningf("Could not check for iptables canary %s/%s: %v", string(tables[0]), string(canary), err) klog.Warningf("Could not check for iptables canary %s/%s: %v", string(tables[0]), string(canary), err)
@ -582,7 +585,7 @@ func (runner *runner) Monitor(canary Chain, tables []Table, reloadFunc func(), i
// so we don't start reloading too soon. // so we don't start reloading too soon.
err := utilwait.PollImmediate(iptablesFlushPollTime, iptablesFlushTimeout, func() (bool, error) { err := utilwait.PollImmediate(iptablesFlushPollTime, iptablesFlushTimeout, func() (bool, error) {
for i := 1; i < len(tables); i++ { for i := 1; i < len(tables); i++ {
if exists, err := runner.chainExists(tables[i], canary); exists || isResourceError(err) { if exists, err := runner.ChainExists(tables[i], canary); exists || isResourceError(err) {
return false, nil return false, nil
} }
} }
@ -607,15 +610,14 @@ func (runner *runner) Monitor(canary Chain, tables []Table, reloadFunc func(), i
} }
} }
// chainExists is used internally by Monitor; none of the public Interface methods can be // ChainExists is part of Interface
// used to distinguish "chain exists" from "chain does not exist" with no side effects func (runner *runner) ChainExists(table Table, chain Chain) (bool, error) {
func (runner *runner) chainExists(table Table, chain Chain) (bool, error) {
fullArgs := makeFullArgs(table, chain) fullArgs := makeFullArgs(table, chain)
runner.mu.Lock() runner.mu.Lock()
defer runner.mu.Unlock() defer runner.mu.Unlock()
trace := utiltrace.New("iptables Monitor CANARY check") trace := utiltrace.New("iptables ChainExists")
defer trace.LogIfLong(2 * time.Second) defer trace.LogIfLong(2 * time.Second)
_, err := runner.run(opListChain, fullArgs) _, err := runner.run(opListChain, fullArgs)

View File

@ -93,6 +93,11 @@ func (*FakeIPTables) DeleteChain(table iptables.Table, chain iptables.Chain) err
return nil return nil
} }
// ChainExists is part of iptables.Interface
func (*FakeIPTables) ChainExists(table iptables.Table, chain iptables.Chain) (bool, error) {
return true, nil
}
// EnsureRule is part of iptables.Interface // EnsureRule is part of iptables.Interface
func (*FakeIPTables) 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