Merge pull request #108250 from cyclinder/add_flag_in_proxy

kube-proxy:  add a flag  to  disable nodePortOnLocalhost
This commit is contained in:
Kubernetes Prow Robot 2022-11-03 17:10:13 -07:00 committed by GitHub
commit d86c013b0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 578 additions and 22 deletions

View File

@ -199,6 +199,7 @@ func (o *Options) AddFlags(fs *pflag.FlagSet) {
fs.BoolVar(&o.config.IPVS.StrictARP, "ipvs-strict-arp", o.config.IPVS.StrictARP, "Enable strict ARP by setting arp_ignore to 1 and arp_announce to 2") fs.BoolVar(&o.config.IPVS.StrictARP, "ipvs-strict-arp", o.config.IPVS.StrictARP, "Enable strict ARP by setting arp_ignore to 1 and arp_announce to 2")
fs.BoolVar(&o.config.IPTables.MasqueradeAll, "masquerade-all", o.config.IPTables.MasqueradeAll, "If using the pure iptables proxy, SNAT all traffic sent via Service cluster IPs (this not commonly needed)") fs.BoolVar(&o.config.IPTables.MasqueradeAll, "masquerade-all", o.config.IPTables.MasqueradeAll, "If using the pure iptables proxy, SNAT all traffic sent via Service cluster IPs (this not commonly needed)")
fs.BoolVar(o.config.IPTables.LocalhostNodePorts, "iptables-localhost-nodeports", pointer.BoolDeref(o.config.IPTables.LocalhostNodePorts, true), "If false Kube-proxy will disable the legacy behavior of allowing NodePort services to be accessed via localhost, This only applies to iptables mode and ipv4.")
fs.BoolVar(&o.config.EnableProfiling, "profiling", o.config.EnableProfiling, "If true enables profiling via web interface on /debug/pprof handler. This parameter is ignored if a config file is specified by --config.") fs.BoolVar(&o.config.EnableProfiling, "profiling", o.config.EnableProfiling, "If true enables profiling via web interface on /debug/pprof handler. This parameter is ignored if a config file is specified by --config.")
fs.Float32Var(&o.config.ClientConnection.QPS, "kube-api-qps", o.config.ClientConnection.QPS, "QPS to use while talking with kubernetes apiserver") fs.Float32Var(&o.config.ClientConnection.QPS, "kube-api-qps", o.config.ClientConnection.QPS, "QPS to use while talking with kubernetes apiserver")

View File

@ -197,6 +197,7 @@ func newProxyServer(
config.IPTables.SyncPeriod.Duration, config.IPTables.SyncPeriod.Duration,
config.IPTables.MinSyncPeriod.Duration, config.IPTables.MinSyncPeriod.Duration,
config.IPTables.MasqueradeAll, config.IPTables.MasqueradeAll,
*config.IPTables.LocalhostNodePorts,
int(*config.IPTables.MasqueradeBit), int(*config.IPTables.MasqueradeBit),
localDetectors, localDetectors,
hostname, hostname,
@ -221,6 +222,7 @@ func newProxyServer(
config.IPTables.SyncPeriod.Duration, config.IPTables.SyncPeriod.Duration,
config.IPTables.MinSyncPeriod.Duration, config.IPTables.MinSyncPeriod.Duration,
config.IPTables.MasqueradeAll, config.IPTables.MasqueradeAll,
*config.IPTables.LocalhostNodePorts,
int(*config.IPTables.MasqueradeBit), int(*config.IPTables.MasqueradeBit),
localDetector, localDetector,
hostname, hostname,

View File

@ -107,6 +107,7 @@ iptables:
masqueradeBit: 17 masqueradeBit: 17
minSyncPeriod: 10s minSyncPeriod: 10s
syncPeriod: 60s syncPeriod: 60s
localhostNodePorts: true
ipvs: ipvs:
minSyncPeriod: 10s minSyncPeriod: 10s
syncPeriod: 60s syncPeriod: 60s
@ -248,6 +249,7 @@ nodePortAddresses:
IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
MasqueradeAll: true, MasqueradeAll: true,
MasqueradeBit: pointer.Int32(17), MasqueradeBit: pointer.Int32(17),
LocalhostNodePorts: pointer.Bool(true),
MinSyncPeriod: metav1.Duration{Duration: 10 * time.Second}, MinSyncPeriod: metav1.Duration{Duration: 10 * time.Second},
SyncPeriod: metav1.Duration{Duration: 60 * time.Second}, SyncPeriod: metav1.Duration{Duration: 60 * time.Second},
}, },

View File

@ -52001,6 +52001,13 @@ func schema_k8sio_kube_proxy_config_v1alpha1_KubeProxyIPTablesConfiguration(ref
Format: "", Format: "",
}, },
}, },
"localhostNodePorts": {
SchemaProps: spec.SchemaProps{
Description: "LocalhostNodePorts tells kube-proxy to allow service NodePorts to be accessed via localhost (iptables mode only)",
Type: []string{"boolean"},
Format: "",
},
},
"syncPeriod": { "syncPeriod": {
SchemaProps: spec.SchemaProps{ SchemaProps: spec.SchemaProps{
Description: "syncPeriod is the period that iptables rules are refreshed (e.g. '5s', '1m', '2h22m'). Must be greater than 0.", Description: "syncPeriod is the period that iptables rules are refreshed (e.g. '5s', '1m', '2h22m'). Must be greater than 0.",
@ -52016,7 +52023,7 @@ func schema_k8sio_kube_proxy_config_v1alpha1_KubeProxyIPTablesConfiguration(ref
}, },
}, },
}, },
Required: []string{"masqueradeBit", "masqueradeAll", "syncPeriod", "minSyncPeriod"}, Required: []string{"masqueradeBit", "masqueradeAll", "localhostNodePorts", "syncPeriod", "minSyncPeriod"},
}, },
}, },
Dependencies: []string{ Dependencies: []string{

View File

@ -94,6 +94,7 @@ func NewHollowProxyOrDie(
proxierSyncPeriod, proxierSyncPeriod,
proxierMinSyncPeriod, proxierMinSyncPeriod,
false, false,
false,
0, 0,
proxyutiliptables.NewNoOpLocalDetector(), proxyutiliptables.NewNoOpLocalDetector(),
nodeName, nodeName,

View File

@ -42,6 +42,7 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
obj.FeatureGates = map[string]bool{c.RandString(): true} obj.FeatureGates = map[string]bool{c.RandString(): true}
obj.HealthzBindAddress = fmt.Sprintf("%d.%d.%d.%d:%d", c.Intn(256), c.Intn(256), c.Intn(256), c.Intn(256), c.Intn(65536)) obj.HealthzBindAddress = fmt.Sprintf("%d.%d.%d.%d:%d", c.Intn(256), c.Intn(256), c.Intn(256), c.Intn(256), c.Intn(65536))
obj.IPTables.MasqueradeBit = pointer.Int32(c.Int31()) obj.IPTables.MasqueradeBit = pointer.Int32(c.Int31())
obj.IPTables.LocalhostNodePorts = pointer.Bool(c.RandBool())
obj.MetricsBindAddress = fmt.Sprintf("%d.%d.%d.%d:%d", c.Intn(256), c.Intn(256), c.Intn(256), c.Intn(256), c.Intn(65536)) obj.MetricsBindAddress = fmt.Sprintf("%d.%d.%d.%d:%d", c.Intn(256), c.Intn(256), c.Intn(256), c.Intn(256), c.Intn(65536))
obj.OOMScoreAdj = pointer.Int32(c.Int31()) obj.OOMScoreAdj = pointer.Int32(c.Int31())
obj.ClientConnection.ContentType = "bar" obj.ClientConnection.ContentType = "bar"

View File

@ -22,6 +22,7 @@ enableProfiling: false
healthzBindAddress: 0.0.0.0:10256 healthzBindAddress: 0.0.0.0:10256
hostnameOverride: "" hostnameOverride: ""
iptables: iptables:
localhostNodePorts: true
masqueradeAll: false masqueradeAll: false
masqueradeBit: 14 masqueradeBit: 14
minSyncPeriod: 1s minSyncPeriod: 1s

View File

@ -22,6 +22,7 @@ enableProfiling: false
healthzBindAddress: 0.0.0.0:10256 healthzBindAddress: 0.0.0.0:10256
hostnameOverride: "" hostnameOverride: ""
iptables: iptables:
localhostNodePorts: true
masqueradeAll: false masqueradeAll: false
masqueradeBit: 14 masqueradeBit: 14
minSyncPeriod: 1s minSyncPeriod: 1s

View File

@ -33,6 +33,9 @@ type KubeProxyIPTablesConfiguration struct {
MasqueradeBit *int32 MasqueradeBit *int32
// masqueradeAll tells kube-proxy to SNAT everything if using the pure iptables proxy mode. // masqueradeAll tells kube-proxy to SNAT everything if using the pure iptables proxy mode.
MasqueradeAll bool MasqueradeAll bool
// LocalhostNodePorts tells kube-proxy to allow service NodePorts to be accessed via
// localhost (iptables mode only)
LocalhostNodePorts *bool
// syncPeriod is the period that iptables rules are refreshed (e.g. '5s', '1m', // syncPeriod is the period that iptables rules are refreshed (e.g. '5s', '1m',
// '2h22m'). Must be greater than 0. // '2h22m'). Must be greater than 0.
SyncPeriod metav1.Duration SyncPeriod metav1.Duration

View File

@ -64,6 +64,9 @@ func SetDefaults_KubeProxyConfiguration(obj *kubeproxyconfigv1alpha1.KubeProxyCo
if obj.IPTables.MinSyncPeriod.Duration == 0 { if obj.IPTables.MinSyncPeriod.Duration == 0 {
obj.IPTables.MinSyncPeriod = metav1.Duration{Duration: 1 * time.Second} obj.IPTables.MinSyncPeriod = metav1.Duration{Duration: 1 * time.Second}
} }
if obj.IPTables.LocalhostNodePorts == nil {
obj.IPTables.LocalhostNodePorts = pointer.Bool(true)
}
if obj.IPVS.SyncPeriod.Duration == 0 { if obj.IPVS.SyncPeriod.Duration == 0 {
obj.IPVS.SyncPeriod = metav1.Duration{Duration: 30 * time.Second} obj.IPVS.SyncPeriod = metav1.Duration{Duration: 30 * time.Second}
} }

View File

@ -17,6 +17,7 @@ limitations under the License.
package v1alpha1 package v1alpha1
import ( import (
"k8s.io/utils/pointer"
"testing" "testing"
"time" "time"
@ -27,7 +28,6 @@ import (
) )
func TestDefaultsKubeProxyConfiguration(t *testing.T) { func TestDefaultsKubeProxyConfiguration(t *testing.T) {
masqBit := int32(14)
oomScore := int32(-999) oomScore := int32(-999)
ctMaxPerCore := int32(32768) ctMaxPerCore := int32(32768)
ctMin := int32(131072) ctMin := int32(131072)
@ -50,8 +50,9 @@ func TestDefaultsKubeProxyConfiguration(t *testing.T) {
Burst: 10, Burst: 10,
}, },
IPTables: kubeproxyconfigv1alpha1.KubeProxyIPTablesConfiguration{ IPTables: kubeproxyconfigv1alpha1.KubeProxyIPTablesConfiguration{
MasqueradeBit: &masqBit, MasqueradeBit: pointer.Int32(14),
MasqueradeAll: false, MasqueradeAll: false,
LocalhostNodePorts: pointer.Bool(true),
SyncPeriod: metav1.Duration{Duration: 30 * time.Second}, SyncPeriod: metav1.Duration{Duration: 30 * time.Second},
MinSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, MinSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
}, },
@ -85,8 +86,9 @@ func TestDefaultsKubeProxyConfiguration(t *testing.T) {
Burst: 10, Burst: 10,
}, },
IPTables: kubeproxyconfigv1alpha1.KubeProxyIPTablesConfiguration{ IPTables: kubeproxyconfigv1alpha1.KubeProxyIPTablesConfiguration{
MasqueradeBit: &masqBit, MasqueradeBit: pointer.Int32(14),
MasqueradeAll: false, MasqueradeAll: false,
LocalhostNodePorts: pointer.Bool(true),
SyncPeriod: metav1.Duration{Duration: 30 * time.Second}, SyncPeriod: metav1.Duration{Duration: 30 * time.Second},
MinSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, MinSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
}, },

View File

@ -237,6 +237,7 @@ func Convert_config_KubeProxyConntrackConfiguration_To_v1alpha1_KubeProxyConntra
func autoConvert_v1alpha1_KubeProxyIPTablesConfiguration_To_config_KubeProxyIPTablesConfiguration(in *v1alpha1.KubeProxyIPTablesConfiguration, out *config.KubeProxyIPTablesConfiguration, s conversion.Scope) error { func autoConvert_v1alpha1_KubeProxyIPTablesConfiguration_To_config_KubeProxyIPTablesConfiguration(in *v1alpha1.KubeProxyIPTablesConfiguration, out *config.KubeProxyIPTablesConfiguration, s conversion.Scope) error {
out.MasqueradeBit = (*int32)(unsafe.Pointer(in.MasqueradeBit)) out.MasqueradeBit = (*int32)(unsafe.Pointer(in.MasqueradeBit))
out.MasqueradeAll = in.MasqueradeAll out.MasqueradeAll = in.MasqueradeAll
out.LocalhostNodePorts = (*bool)(unsafe.Pointer(in.LocalhostNodePorts))
out.SyncPeriod = in.SyncPeriod out.SyncPeriod = in.SyncPeriod
out.MinSyncPeriod = in.MinSyncPeriod out.MinSyncPeriod = in.MinSyncPeriod
return nil return nil
@ -250,6 +251,7 @@ func Convert_v1alpha1_KubeProxyIPTablesConfiguration_To_config_KubeProxyIPTables
func autoConvert_config_KubeProxyIPTablesConfiguration_To_v1alpha1_KubeProxyIPTablesConfiguration(in *config.KubeProxyIPTablesConfiguration, out *v1alpha1.KubeProxyIPTablesConfiguration, s conversion.Scope) error { func autoConvert_config_KubeProxyIPTablesConfiguration_To_v1alpha1_KubeProxyIPTablesConfiguration(in *config.KubeProxyIPTablesConfiguration, out *v1alpha1.KubeProxyIPTablesConfiguration, s conversion.Scope) error {
out.MasqueradeBit = (*int32)(unsafe.Pointer(in.MasqueradeBit)) out.MasqueradeBit = (*int32)(unsafe.Pointer(in.MasqueradeBit))
out.MasqueradeAll = in.MasqueradeAll out.MasqueradeAll = in.MasqueradeAll
out.LocalhostNodePorts = (*bool)(unsafe.Pointer(in.LocalhostNodePorts))
out.SyncPeriod = in.SyncPeriod out.SyncPeriod = in.SyncPeriod
out.MinSyncPeriod = in.MinSyncPeriod out.MinSyncPeriod = in.MinSyncPeriod
return nil return nil

View File

@ -157,6 +157,11 @@ func (in *KubeProxyIPTablesConfiguration) DeepCopyInto(out *KubeProxyIPTablesCon
*out = new(int32) *out = new(int32)
**out = **in **out = **in
} }
if in.LocalhostNodePorts != nil {
in, out := &in.LocalhostNodePorts, &out.LocalhostNodePorts
*out = new(bool)
**out = **in
}
out.SyncPeriod = in.SyncPeriod out.SyncPeriod = in.SyncPeriod
out.MinSyncPeriod = in.MinSyncPeriod out.MinSyncPeriod = in.MinSyncPeriod
return return

View File

@ -203,7 +203,10 @@ type Proxier struct {
// optimize for performance over debuggability. // optimize for performance over debuggability.
largeClusterMode bool largeClusterMode bool
// Values are as a parameter to select the interfaces where nodeport works. // localhostNodePorts indicates whether to generate iptables rules that
// disable NodePort services to be accessed via localhost.
localhostNodePorts bool
// Values are as a parameter to select the interfaces where nodePort works.
nodePortAddresses []string nodePortAddresses []string
// networkInterfacer defines an interface for several net library functions. // networkInterfacer defines an interface for several net library functions.
// Inject for test purpose. // Inject for test purpose.
@ -224,6 +227,7 @@ func NewProxier(ipt utiliptables.Interface,
syncPeriod time.Duration, syncPeriod time.Duration,
minSyncPeriod time.Duration, minSyncPeriod time.Duration,
masqueradeAll bool, masqueradeAll bool,
localhostNodePorts bool,
masqueradeBit int, masqueradeBit int,
localDetector proxyutiliptables.LocalTrafficDetector, localDetector proxyutiliptables.LocalTrafficDetector,
hostname string, hostname string,
@ -232,9 +236,10 @@ func NewProxier(ipt utiliptables.Interface,
healthzServer healthcheck.ProxierHealthUpdater, healthzServer healthcheck.ProxierHealthUpdater,
nodePortAddresses []string, nodePortAddresses []string,
) (*Proxier, error) { ) (*Proxier, error) {
if utilproxy.ContainsIPv4Loopback(nodePortAddresses) { if localhostNodePorts && utilproxy.ContainsIPv4Loopback(nodePortAddresses) {
// Set the route_localnet sysctl we need for exposing NodePorts on loopback addresses // Set the route_localnet sysctl we need for exposing NodePorts on loopback addresses
klog.InfoS("Setting route_localnet=1, use nodePortAddresses to filter loopback addresses for NodePorts to skip it https://issues.k8s.io/90259") // Refer to https://issues.k8s.io/90259
klog.InfoS("Setting route_localnet=1 to allows nodePort services can be accessed via localhost. You can set flag '--iptables-localhost-nodeports' to false or use nodePortAddresses (--nodeport-addresses) to filter loopback addresses to change this")
if err := utilproxy.EnsureSysctl(sysctl, sysctlRouteLocalnet, 1); err != nil { if err := utilproxy.EnsureSysctl(sysctl, sysctlRouteLocalnet, 1); err != nil {
return nil, err return nil, err
} }
@ -289,6 +294,7 @@ func NewProxier(ipt utiliptables.Interface,
filterRules: utilproxy.LineBuffer{}, filterRules: utilproxy.LineBuffer{},
natChains: utilproxy.LineBuffer{}, natChains: utilproxy.LineBuffer{},
natRules: utilproxy.LineBuffer{}, natRules: utilproxy.LineBuffer{},
localhostNodePorts: localhostNodePorts,
nodePortAddresses: nodePortAddresses, nodePortAddresses: nodePortAddresses,
networkInterfacer: utilproxy.RealNetwork{}, networkInterfacer: utilproxy.RealNetwork{},
} }
@ -320,6 +326,7 @@ func NewDualStackProxier(
syncPeriod time.Duration, syncPeriod time.Duration,
minSyncPeriod time.Duration, minSyncPeriod time.Duration,
masqueradeAll bool, masqueradeAll bool,
localhostNodePorts bool,
masqueradeBit int, masqueradeBit int,
localDetectors [2]proxyutiliptables.LocalTrafficDetector, localDetectors [2]proxyutiliptables.LocalTrafficDetector,
hostname string, hostname string,
@ -331,14 +338,14 @@ func NewDualStackProxier(
// Create an ipv4 instance of the single-stack proxier // Create an ipv4 instance of the single-stack proxier
ipFamilyMap := utilproxy.MapCIDRsByIPFamily(nodePortAddresses) ipFamilyMap := utilproxy.MapCIDRsByIPFamily(nodePortAddresses)
ipv4Proxier, err := NewProxier(ipt[0], sysctl, ipv4Proxier, err := NewProxier(ipt[0], sysctl,
exec, syncPeriod, minSyncPeriod, masqueradeAll, masqueradeBit, localDetectors[0], hostname, exec, syncPeriod, minSyncPeriod, masqueradeAll, localhostNodePorts, masqueradeBit, localDetectors[0], hostname,
nodeIP[0], recorder, healthzServer, ipFamilyMap[v1.IPv4Protocol]) nodeIP[0], recorder, healthzServer, ipFamilyMap[v1.IPv4Protocol])
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to create ipv4 proxier: %v", err) return nil, fmt.Errorf("unable to create ipv4 proxier: %v", err)
} }
ipv6Proxier, err := NewProxier(ipt[1], sysctl, ipv6Proxier, err := NewProxier(ipt[1], sysctl,
exec, syncPeriod, minSyncPeriod, masqueradeAll, masqueradeBit, localDetectors[1], hostname, exec, syncPeriod, minSyncPeriod, masqueradeAll, false, masqueradeBit, localDetectors[1], hostname,
nodeIP[1], recorder, healthzServer, ipFamilyMap[v1.IPv6Protocol]) nodeIP[1], recorder, healthzServer, ipFamilyMap[v1.IPv6Protocol])
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to create ipv6 proxier: %v", err) return nil, fmt.Errorf("unable to create ipv6 proxier: %v", err)
@ -1421,23 +1428,52 @@ func (proxier *Proxier) syncProxyRules() {
} }
} }
// Finally, tail-call to the nodeports chain. This needs to be after all // Finally, tail-call to the nodePorts chain. This needs to be after all
// other service portal rules. // other service portal rules.
for address := range nodeAddresses { for address := range nodeAddresses {
if utilproxy.IsZeroCIDR(address) { if utilproxy.IsZeroCIDR(address) {
destinations := []string{"-m", "addrtype", "--dst-type", "LOCAL"}
if isIPv6 {
// For IPv6, Regardless of the value of localhostNodePorts is true
// or false, we should disable access to the nodePort via localhost. Since it never works and always
// cause kernel warnings.
destinations = append(destinations, "!", "-d", "::1/128")
}
if !proxier.localhostNodePorts && !isIPv6 {
// If set localhostNodePorts to "false"(route_localnet=0), We should generate iptables rules that
// disable NodePort services to be accessed via localhost. Since it doesn't work and causes
// the kernel to log warnings if anyone tries.
destinations = append(destinations, "!", "-d", "127.0.0.0/8")
}
proxier.natRules.Write( proxier.natRules.Write(
"-A", string(kubeServicesChain), "-A", string(kubeServicesChain),
"-m", "comment", "--comment", `"kubernetes service nodeports; NOTE: this must be the last rule in this chain"`, "-m", "comment", "--comment", `"kubernetes service nodeports; NOTE: this must be the last rule in this chain"`,
"-m", "addrtype", "--dst-type", "LOCAL", destinations,
"-j", string(kubeNodePortsChain)) "-j", string(kubeNodePortsChain))
// Nothing else matters after the zero CIDR.
break break
} }
// Ignore IP addresses with incorrect version // Ignore IP addresses with incorrect version
if isIPv6 && !netutils.IsIPv6String(address) || !isIPv6 && netutils.IsIPv6String(address) { if isIPv6 && !netutils.IsIPv6String(address) || !isIPv6 && netutils.IsIPv6String(address) {
klog.ErrorS(nil, "IP has incorrect IP version", "IP", address) klog.ErrorS(nil, "IP has incorrect IP version", "IP", address)
continue continue
} }
// For ipv6, Regardless of the value of localhostNodePorts is true or false, we should disallow access
// to the nodePort via lookBack address.
if isIPv6 && utilproxy.IsLoopBack(address) {
klog.ErrorS(nil, "disallow nodePort services to be accessed via ipv6 localhost address", "IP", address)
continue
}
// For ipv4, When localhostNodePorts is set to false, Ignore ipv4 lookBack address
if !isIPv6 && utilproxy.IsLoopBack(address) && !proxier.localhostNodePorts {
klog.ErrorS(nil, "disallow nodePort services to be accessed via ipv4 localhost address", "IP", address)
continue
}
// create nodeport rules for each IP one by one // create nodeport rules for each IP one by one
proxier.natRules.Write( proxier.natRules.Write(
"-A", string(kubeServicesChain), "-A", string(kubeServicesChain),

View File

@ -421,6 +421,7 @@ func NewFakeProxier(ipt utiliptables.Interface) *Proxier {
natChains: utilproxy.LineBuffer{}, natChains: utilproxy.LineBuffer{},
natRules: utilproxy.LineBuffer{}, natRules: utilproxy.LineBuffer{},
nodeIP: netutils.ParseIPSloppy(testNodeIP), nodeIP: netutils.ParseIPSloppy(testNodeIP),
localhostNodePorts: true,
nodePortAddresses: make([]string, 0), nodePortAddresses: make([]string, 0),
networkInterfacer: networkInterfacer, networkInterfacer: networkInterfacer,
} }
@ -3253,6 +3254,477 @@ func TestOnlyLocalLoadBalancing(t *testing.T) {
}) })
} }
func TestEnableLocalhostNodePortsIPv4(t *testing.T) {
ipt := iptablestest.NewFake()
fp := NewFakeProxier(ipt)
fp.localDetector = proxyutiliptables.NewNoOpLocalDetector()
fp.localhostNodePorts = true
expected := dedent.Dedent(`
*filter
:KUBE-NODEPORTS - [0:0]
:KUBE-SERVICES - [0:0]
:KUBE-EXTERNAL-SERVICES - [0:0]
:KUBE-FORWARD - [0:0]
:KUBE-PROXY-FIREWALL - [0:0]
-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
COMMIT
*nat
:KUBE-NODEPORTS - [0:0]
:KUBE-SERVICES - [0:0]
:KUBE-EXT-XPGD46QRK7WJZT7O - [0:0]
:KUBE-MARK-MASQ - [0:0]
:KUBE-POSTROUTING - [0:0]
:KUBE-SEP-6KG6DFHVBKBK53RU - [0:0]
:KUBE-SEP-KDGX2M2ONE25PSWH - [0:0]
:KUBE-SVC-XPGD46QRK7WJZT7O - [0:0]
:KUBE-SVL-XPGD46QRK7WJZT7O - [0:0]
-A KUBE-NODEPORTS -m comment --comment ns1/svc1:p80 -m tcp -p tcp --dport 30001 -j KUBE-EXT-XPGD46QRK7WJZT7O
-A KUBE-SERVICES -m comment --comment "ns1/svc1:p80 cluster IP" -m tcp -p tcp -d 10.69.0.10 --dport 80 -j KUBE-SVC-XPGD46QRK7WJZT7O
-A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS
-A KUBE-EXT-XPGD46QRK7WJZT7O -m comment --comment "masquerade LOCAL traffic for ns1/svc1:p80 external destinations" -m addrtype --src-type LOCAL -j KUBE-MARK-MASQ
-A KUBE-EXT-XPGD46QRK7WJZT7O -m comment --comment "route LOCAL traffic for ns1/svc1:p80 external destinations" -m addrtype --src-type LOCAL -j KUBE-SVC-XPGD46QRK7WJZT7O
-A KUBE-EXT-XPGD46QRK7WJZT7O -j KUBE-SVL-XPGD46QRK7WJZT7O
-A KUBE-MARK-MASQ -j MARK --or-mark 0x4000
-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
-A KUBE-POSTROUTING -j MARK --xor-mark 0x4000
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE
-A KUBE-SEP-6KG6DFHVBKBK53RU -m comment --comment ns1/svc1:p80 -s 10.244.0.1 -j KUBE-MARK-MASQ
-A KUBE-SEP-6KG6DFHVBKBK53RU -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination 10.244.0.1:80
-A KUBE-SEP-KDGX2M2ONE25PSWH -m comment --comment ns1/svc1:p80 -s 10.244.2.1 -j KUBE-MARK-MASQ
-A KUBE-SEP-KDGX2M2ONE25PSWH -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination 10.244.2.1:80
-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 -> 10.244.0.1:80" -m statistic --mode random --probability 0.5000000000 -j KUBE-SEP-6KG6DFHVBKBK53RU
-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 -> 10.244.2.1:80" -j KUBE-SEP-KDGX2M2ONE25PSWH
-A KUBE-SVL-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 -> 10.244.2.1:80" -j KUBE-SEP-KDGX2M2ONE25PSWH
COMMIT
`)
svcIP := "10.69.0.10"
svcPort := 80
svcNodePort := 30001
svcPortName := proxy.ServicePortName{
NamespacedName: makeNSN("ns1", "svc1"),
Port: "p80",
Protocol: v1.ProtocolTCP,
}
makeServiceMap(fp,
makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
svc.Spec.Type = "NodePort"
svc.Spec.ClusterIP = svcIP
svc.Spec.Ports = []v1.ServicePort{{
Name: svcPortName.Port,
Port: int32(svcPort),
Protocol: v1.ProtocolTCP,
NodePort: int32(svcNodePort),
}}
svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyTypeLocal
}),
)
epIP1 := "10.244.0.1"
epIP2 := "10.244.2.1"
tcpProtocol := v1.ProtocolTCP
populateEndpointSlices(fp,
makeTestEndpointSlice(svcPortName.Namespace, svcPortName.Name, 1, func(eps *discovery.EndpointSlice) {
eps.AddressType = discovery.AddressTypeIPv4
eps.Endpoints = []discovery.Endpoint{{
Addresses: []string{epIP1},
NodeName: nil,
}, {
Addresses: []string{epIP2},
NodeName: pointer.String(testHostname),
}}
eps.Ports = []discovery.EndpointPort{{
Name: pointer.String(svcPortName.Port),
Port: pointer.Int32(int32(svcPort)),
Protocol: &tcpProtocol,
}}
}),
)
fp.syncProxyRules()
assertIPTablesRulesEqual(t, getLine(), true, expected, fp.iptablesData.String())
}
func TestDisableLocalhostNodePortsIPv4(t *testing.T) {
ipt := iptablestest.NewFake()
fp := NewFakeProxier(ipt)
fp.localDetector = proxyutiliptables.NewNoOpLocalDetector()
fp.localhostNodePorts = false
expected := dedent.Dedent(`
*filter
:KUBE-NODEPORTS - [0:0]
:KUBE-SERVICES - [0:0]
:KUBE-EXTERNAL-SERVICES - [0:0]
:KUBE-FORWARD - [0:0]
:KUBE-PROXY-FIREWALL - [0:0]
-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
COMMIT
*nat
:KUBE-NODEPORTS - [0:0]
:KUBE-SERVICES - [0:0]
:KUBE-EXT-XPGD46QRK7WJZT7O - [0:0]
:KUBE-MARK-MASQ - [0:0]
:KUBE-POSTROUTING - [0:0]
:KUBE-SEP-6KG6DFHVBKBK53RU - [0:0]
:KUBE-SEP-KDGX2M2ONE25PSWH - [0:0]
:KUBE-SVC-XPGD46QRK7WJZT7O - [0:0]
:KUBE-SVL-XPGD46QRK7WJZT7O - [0:0]
-A KUBE-NODEPORTS -m comment --comment ns1/svc1:p80 -m tcp -p tcp --dport 30001 -j KUBE-EXT-XPGD46QRK7WJZT7O
-A KUBE-SERVICES -m comment --comment "ns1/svc1:p80 cluster IP" -m tcp -p tcp -d 10.69.0.10 --dport 80 -j KUBE-SVC-XPGD46QRK7WJZT7O
-A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL ! -d 127.0.0.0/8 -j KUBE-NODEPORTS
-A KUBE-EXT-XPGD46QRK7WJZT7O -m comment --comment "masquerade LOCAL traffic for ns1/svc1:p80 external destinations" -m addrtype --src-type LOCAL -j KUBE-MARK-MASQ
-A KUBE-EXT-XPGD46QRK7WJZT7O -m comment --comment "route LOCAL traffic for ns1/svc1:p80 external destinations" -m addrtype --src-type LOCAL -j KUBE-SVC-XPGD46QRK7WJZT7O
-A KUBE-EXT-XPGD46QRK7WJZT7O -j KUBE-SVL-XPGD46QRK7WJZT7O
-A KUBE-MARK-MASQ -j MARK --or-mark 0x4000
-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
-A KUBE-POSTROUTING -j MARK --xor-mark 0x4000
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE
-A KUBE-SEP-6KG6DFHVBKBK53RU -m comment --comment ns1/svc1:p80 -s 10.244.0.1 -j KUBE-MARK-MASQ
-A KUBE-SEP-6KG6DFHVBKBK53RU -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination 10.244.0.1:80
-A KUBE-SEP-KDGX2M2ONE25PSWH -m comment --comment ns1/svc1:p80 -s 10.244.2.1 -j KUBE-MARK-MASQ
-A KUBE-SEP-KDGX2M2ONE25PSWH -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination 10.244.2.1:80
-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 -> 10.244.0.1:80" -m statistic --mode random --probability 0.5000000000 -j KUBE-SEP-6KG6DFHVBKBK53RU
-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 -> 10.244.2.1:80" -j KUBE-SEP-KDGX2M2ONE25PSWH
-A KUBE-SVL-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 -> 10.244.2.1:80" -j KUBE-SEP-KDGX2M2ONE25PSWH
COMMIT
`)
svcIP := "10.69.0.10"
svcPort := 80
svcNodePort := 30001
svcPortName := proxy.ServicePortName{
NamespacedName: makeNSN("ns1", "svc1"),
Port: "p80",
Protocol: v1.ProtocolTCP,
}
makeServiceMap(fp,
makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
svc.Spec.Type = "NodePort"
svc.Spec.ClusterIP = svcIP
svc.Spec.Ports = []v1.ServicePort{{
Name: svcPortName.Port,
Port: int32(svcPort),
Protocol: v1.ProtocolTCP,
NodePort: int32(svcNodePort),
}}
svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyTypeLocal
}),
)
epIP1 := "10.244.0.1"
epIP2 := "10.244.2.1"
tcpProtocol := v1.ProtocolTCP
populateEndpointSlices(fp,
makeTestEndpointSlice(svcPortName.Namespace, svcPortName.Name, 1, func(eps *discovery.EndpointSlice) {
eps.AddressType = discovery.AddressTypeIPv4
eps.Endpoints = []discovery.Endpoint{{
Addresses: []string{epIP1},
NodeName: nil,
}, {
Addresses: []string{epIP2},
NodeName: pointer.String(testHostname),
}}
eps.Ports = []discovery.EndpointPort{{
Name: pointer.String(svcPortName.Port),
Port: pointer.Int32(int32(svcPort)),
Protocol: &tcpProtocol,
}}
}),
)
fp.syncProxyRules()
assertIPTablesRulesEqual(t, getLine(), true, expected, fp.iptablesData.String())
}
func TestDisableLocalhostNodePortsIPv4WithNodeAddress(t *testing.T) {
ipt := iptablestest.NewFake()
fp := NewFakeProxier(ipt)
fp.localDetector = proxyutiliptables.NewNoOpLocalDetector()
fp.localhostNodePorts = false
fp.networkInterfacer.InterfaceAddrs()
fp.nodePortAddresses = []string{"127.0.0.0/8"}
expected := dedent.Dedent(`
*filter
:KUBE-NODEPORTS - [0:0]
:KUBE-SERVICES - [0:0]
:KUBE-EXTERNAL-SERVICES - [0:0]
:KUBE-FORWARD - [0:0]
:KUBE-PROXY-FIREWALL - [0:0]
-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
COMMIT
*nat
:KUBE-NODEPORTS - [0:0]
:KUBE-SERVICES - [0:0]
:KUBE-EXT-XPGD46QRK7WJZT7O - [0:0]
:KUBE-MARK-MASQ - [0:0]
:KUBE-POSTROUTING - [0:0]
:KUBE-SEP-6KG6DFHVBKBK53RU - [0:0]
:KUBE-SEP-KDGX2M2ONE25PSWH - [0:0]
:KUBE-SVC-XPGD46QRK7WJZT7O - [0:0]
:KUBE-SVL-XPGD46QRK7WJZT7O - [0:0]
-A KUBE-NODEPORTS -m comment --comment ns1/svc1:p80 -m tcp -p tcp --dport 30001 -j KUBE-EXT-XPGD46QRK7WJZT7O
-A KUBE-SERVICES -m comment --comment "ns1/svc1:p80 cluster IP" -m tcp -p tcp -d 10.69.0.10 --dport 80 -j KUBE-SVC-XPGD46QRK7WJZT7O
-A KUBE-EXT-XPGD46QRK7WJZT7O -m comment --comment "masquerade LOCAL traffic for ns1/svc1:p80 external destinations" -m addrtype --src-type LOCAL -j KUBE-MARK-MASQ
-A KUBE-EXT-XPGD46QRK7WJZT7O -m comment --comment "route LOCAL traffic for ns1/svc1:p80 external destinations" -m addrtype --src-type LOCAL -j KUBE-SVC-XPGD46QRK7WJZT7O
-A KUBE-EXT-XPGD46QRK7WJZT7O -j KUBE-SVL-XPGD46QRK7WJZT7O
-A KUBE-MARK-MASQ -j MARK --or-mark 0x4000
-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
-A KUBE-POSTROUTING -j MARK --xor-mark 0x4000
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE
-A KUBE-SEP-6KG6DFHVBKBK53RU -m comment --comment ns1/svc1:p80 -s 10.244.0.1 -j KUBE-MARK-MASQ
-A KUBE-SEP-6KG6DFHVBKBK53RU -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination 10.244.0.1:80
-A KUBE-SEP-KDGX2M2ONE25PSWH -m comment --comment ns1/svc1:p80 -s 10.244.2.1 -j KUBE-MARK-MASQ
-A KUBE-SEP-KDGX2M2ONE25PSWH -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination 10.244.2.1:80
-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 -> 10.244.0.1:80" -m statistic --mode random --probability 0.5000000000 -j KUBE-SEP-6KG6DFHVBKBK53RU
-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 -> 10.244.2.1:80" -j KUBE-SEP-KDGX2M2ONE25PSWH
-A KUBE-SVL-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 -> 10.244.2.1:80" -j KUBE-SEP-KDGX2M2ONE25PSWH
COMMIT
`)
svcIP := "10.69.0.10"
svcPort := 80
svcNodePort := 30001
svcPortName := proxy.ServicePortName{
NamespacedName: makeNSN("ns1", "svc1"),
Port: "p80",
Protocol: v1.ProtocolTCP,
}
makeServiceMap(fp,
makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
svc.Spec.Type = "NodePort"
svc.Spec.ClusterIP = svcIP
svc.Spec.Ports = []v1.ServicePort{{
Name: svcPortName.Port,
Port: int32(svcPort),
Protocol: v1.ProtocolTCP,
NodePort: int32(svcNodePort),
}}
svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyTypeLocal
}),
)
epIP1 := "10.244.0.1"
epIP2 := "10.244.2.1"
tcpProtocol := v1.ProtocolTCP
populateEndpointSlices(fp,
makeTestEndpointSlice(svcPortName.Namespace, svcPortName.Name, 1, func(eps *discovery.EndpointSlice) {
eps.AddressType = discovery.AddressTypeIPv4
eps.Endpoints = []discovery.Endpoint{{
Addresses: []string{epIP1},
NodeName: nil,
}, {
Addresses: []string{epIP2},
NodeName: pointer.String(testHostname),
}}
eps.Ports = []discovery.EndpointPort{{
Name: pointer.String(svcPortName.Port),
Port: pointer.Int32(int32(svcPort)),
Protocol: &tcpProtocol,
}}
}),
)
fp.syncProxyRules()
assertIPTablesRulesEqual(t, getLine(), true, expected, fp.iptablesData.String())
}
func TestEnableLocalhostNodePortsIPv6(t *testing.T) {
ipt := iptablestest.NewIPv6Fake()
fp := NewFakeProxier(ipt)
fp.localDetector = proxyutiliptables.NewNoOpLocalDetector()
fp.localhostNodePorts = true
expected := dedent.Dedent(`
*filter
:KUBE-NODEPORTS - [0:0]
:KUBE-SERVICES - [0:0]
:KUBE-EXTERNAL-SERVICES - [0:0]
:KUBE-FORWARD - [0:0]
:KUBE-PROXY-FIREWALL - [0:0]
-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
COMMIT
*nat
:KUBE-NODEPORTS - [0:0]
:KUBE-SERVICES - [0:0]
:KUBE-EXT-XPGD46QRK7WJZT7O - [0:0]
:KUBE-MARK-MASQ - [0:0]
:KUBE-POSTROUTING - [0:0]
:KUBE-SEP-LIGRYQQLSZN4UWQ5 - [0:0]
:KUBE-SEP-XJJ5QXWGJG344QDZ - [0:0]
:KUBE-SVC-XPGD46QRK7WJZT7O - [0:0]
:KUBE-SVL-XPGD46QRK7WJZT7O - [0:0]
-A KUBE-NODEPORTS -m comment --comment ns1/svc1:p80 -m tcp -p tcp --dport 30001 -j KUBE-EXT-XPGD46QRK7WJZT7O
-A KUBE-SERVICES -m comment --comment "ns1/svc1:p80 cluster IP" -m tcp -p tcp -d fd00:ab34::20 --dport 80 -j KUBE-SVC-XPGD46QRK7WJZT7O
-A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL ! -d ::1/128 -j KUBE-NODEPORTS
-A KUBE-EXT-XPGD46QRK7WJZT7O -m comment --comment "masquerade LOCAL traffic for ns1/svc1:p80 external destinations" -m addrtype --src-type LOCAL -j KUBE-MARK-MASQ
-A KUBE-EXT-XPGD46QRK7WJZT7O -m comment --comment "route LOCAL traffic for ns1/svc1:p80 external destinations" -m addrtype --src-type LOCAL -j KUBE-SVC-XPGD46QRK7WJZT7O
-A KUBE-EXT-XPGD46QRK7WJZT7O -j KUBE-SVL-XPGD46QRK7WJZT7O
-A KUBE-MARK-MASQ -j MARK --or-mark 0x4000
-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
-A KUBE-POSTROUTING -j MARK --xor-mark 0x4000
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE
-A KUBE-SEP-LIGRYQQLSZN4UWQ5 -m comment --comment ns1/svc1:p80 -s ff06::c1 -j KUBE-MARK-MASQ
-A KUBE-SEP-LIGRYQQLSZN4UWQ5 -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination [ff06::c1]:80
-A KUBE-SEP-XJJ5QXWGJG344QDZ -m comment --comment ns1/svc1:p80 -s ff06::c2 -j KUBE-MARK-MASQ
-A KUBE-SEP-XJJ5QXWGJG344QDZ -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination [ff06::c2]:80
-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 -> [ff06::c1]:80" -m statistic --mode random --probability 0.5000000000 -j KUBE-SEP-LIGRYQQLSZN4UWQ5
-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 -> [ff06::c2]:80" -j KUBE-SEP-XJJ5QXWGJG344QDZ
-A KUBE-SVL-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 -> [ff06::c2]:80" -j KUBE-SEP-XJJ5QXWGJG344QDZ
COMMIT
`)
svcIP := "fd00:ab34::20"
svcPort := 80
svcNodePort := 30001
svcPortName := proxy.ServicePortName{
NamespacedName: makeNSN("ns1", "svc1"),
Port: "p80",
Protocol: v1.ProtocolTCP,
}
makeServiceMap(fp,
makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
svc.Spec.Type = "NodePort"
svc.Spec.ClusterIP = svcIP
svc.Spec.Ports = []v1.ServicePort{{
Name: svcPortName.Port,
Port: int32(svcPort),
Protocol: v1.ProtocolTCP,
NodePort: int32(svcNodePort),
}}
svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyTypeLocal
}),
)
epIP1 := "ff06::c1"
epIP2 := "ff06::c2"
tcpProtocol := v1.ProtocolTCP
populateEndpointSlices(fp,
makeTestEndpointSlice(svcPortName.Namespace, svcPortName.Name, 1, func(eps *discovery.EndpointSlice) {
eps.AddressType = discovery.AddressTypeIPv6
eps.Endpoints = []discovery.Endpoint{{
Addresses: []string{epIP1},
NodeName: nil,
}, {
Addresses: []string{epIP2},
NodeName: pointer.String(testHostname),
}}
eps.Ports = []discovery.EndpointPort{{
Name: pointer.String(svcPortName.Port),
Port: pointer.Int32(int32(svcPort)),
Protocol: &tcpProtocol,
}}
}),
)
fp.syncProxyRules()
assertIPTablesRulesEqual(t, getLine(), true, expected, fp.iptablesData.String())
}
func TestDisableLocalhostNodePortsIPv6(t *testing.T) {
ipt := iptablestest.NewIPv6Fake()
fp := NewFakeProxier(ipt)
fp.localDetector = proxyutiliptables.NewNoOpLocalDetector()
fp.localhostNodePorts = false
expected := dedent.Dedent(`
*filter
:KUBE-NODEPORTS - [0:0]
:KUBE-SERVICES - [0:0]
:KUBE-EXTERNAL-SERVICES - [0:0]
:KUBE-FORWARD - [0:0]
:KUBE-PROXY-FIREWALL - [0:0]
-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
COMMIT
*nat
:KUBE-NODEPORTS - [0:0]
:KUBE-SERVICES - [0:0]
:KUBE-EXT-XPGD46QRK7WJZT7O - [0:0]
:KUBE-MARK-MASQ - [0:0]
:KUBE-POSTROUTING - [0:0]
:KUBE-SEP-LIGRYQQLSZN4UWQ5 - [0:0]
:KUBE-SEP-XJJ5QXWGJG344QDZ - [0:0]
:KUBE-SVC-XPGD46QRK7WJZT7O - [0:0]
:KUBE-SVL-XPGD46QRK7WJZT7O - [0:0]
-A KUBE-NODEPORTS -m comment --comment ns1/svc1:p80 -m tcp -p tcp --dport 30001 -j KUBE-EXT-XPGD46QRK7WJZT7O
-A KUBE-SERVICES -m comment --comment "ns1/svc1:p80 cluster IP" -m tcp -p tcp -d fd00:ab34::20 --dport 80 -j KUBE-SVC-XPGD46QRK7WJZT7O
-A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL ! -d ::1/128 -j KUBE-NODEPORTS
-A KUBE-EXT-XPGD46QRK7WJZT7O -m comment --comment "masquerade LOCAL traffic for ns1/svc1:p80 external destinations" -m addrtype --src-type LOCAL -j KUBE-MARK-MASQ
-A KUBE-EXT-XPGD46QRK7WJZT7O -m comment --comment "route LOCAL traffic for ns1/svc1:p80 external destinations" -m addrtype --src-type LOCAL -j KUBE-SVC-XPGD46QRK7WJZT7O
-A KUBE-EXT-XPGD46QRK7WJZT7O -j KUBE-SVL-XPGD46QRK7WJZT7O
-A KUBE-MARK-MASQ -j MARK --or-mark 0x4000
-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
-A KUBE-POSTROUTING -j MARK --xor-mark 0x4000
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE
-A KUBE-SEP-LIGRYQQLSZN4UWQ5 -m comment --comment ns1/svc1:p80 -s ff06::c1 -j KUBE-MARK-MASQ
-A KUBE-SEP-LIGRYQQLSZN4UWQ5 -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination [ff06::c1]:80
-A KUBE-SEP-XJJ5QXWGJG344QDZ -m comment --comment ns1/svc1:p80 -s ff06::c2 -j KUBE-MARK-MASQ
-A KUBE-SEP-XJJ5QXWGJG344QDZ -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination [ff06::c2]:80
-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 -> [ff06::c1]:80" -m statistic --mode random --probability 0.5000000000 -j KUBE-SEP-LIGRYQQLSZN4UWQ5
-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 -> [ff06::c2]:80" -j KUBE-SEP-XJJ5QXWGJG344QDZ
-A KUBE-SVL-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 -> [ff06::c2]:80" -j KUBE-SEP-XJJ5QXWGJG344QDZ
COMMIT
`)
svcIP := "fd00:ab34::20"
svcPort := 80
svcNodePort := 30001
svcPortName := proxy.ServicePortName{
NamespacedName: makeNSN("ns1", "svc1"),
Port: "p80",
Protocol: v1.ProtocolTCP,
}
makeServiceMap(fp,
makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
svc.Spec.Type = "NodePort"
svc.Spec.ClusterIP = svcIP
svc.Spec.Ports = []v1.ServicePort{{
Name: svcPortName.Port,
Port: int32(svcPort),
Protocol: v1.ProtocolTCP,
NodePort: int32(svcNodePort),
}}
svc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyTypeLocal
}),
)
epIP1 := "ff06::c1"
epIP2 := "ff06::c2"
tcpProtocol := v1.ProtocolTCP
populateEndpointSlices(fp,
makeTestEndpointSlice(svcPortName.Namespace, svcPortName.Name, 1, func(eps *discovery.EndpointSlice) {
eps.AddressType = discovery.AddressTypeIPv6
eps.Endpoints = []discovery.Endpoint{{
Addresses: []string{epIP1},
NodeName: nil,
}, {
Addresses: []string{epIP2},
NodeName: pointer.String(testHostname),
}}
eps.Ports = []discovery.EndpointPort{{
Name: pointer.String(svcPortName.Port),
Port: pointer.Int32(int32(svcPort)),
Protocol: &tcpProtocol,
}}
}),
)
fp.syncProxyRules()
assertIPTablesRulesEqual(t, getLine(), true, expected, fp.iptablesData.String())
}
func TestOnlyLocalNodePortsNoClusterCIDR(t *testing.T) { func TestOnlyLocalNodePortsNoClusterCIDR(t *testing.T) {
ipt := iptablestest.NewFake() ipt := iptablestest.NewFake()
fp := NewFakeProxier(ipt) fp := NewFakeProxier(ipt)

View File

@ -118,6 +118,15 @@ func IsZeroCIDR(cidr string) bool {
return false return false
} }
// IsLoopBack checks if a given IP address is a loopback address.
func IsLoopBack(ip string) bool {
netIP := netutils.ParseIPSloppy(ip)
if netIP != nil {
return netIP.IsLoopback()
}
return false
}
// IsProxyableIP checks if a given IP address is permitted to be proxied // IsProxyableIP checks if a given IP address is permitted to be proxied
func IsProxyableIP(ip string) error { func IsProxyableIP(ip string) error {
netIP := netutils.ParseIPSloppy(ip) netIP := netutils.ParseIPSloppy(ip)

View File

@ -29,6 +29,9 @@ type KubeProxyIPTablesConfiguration struct {
MasqueradeBit *int32 `json:"masqueradeBit"` MasqueradeBit *int32 `json:"masqueradeBit"`
// masqueradeAll tells kube-proxy to SNAT everything if using the pure iptables proxy mode. // masqueradeAll tells kube-proxy to SNAT everything if using the pure iptables proxy mode.
MasqueradeAll bool `json:"masqueradeAll"` MasqueradeAll bool `json:"masqueradeAll"`
// LocalhostNodePorts tells kube-proxy to allow service NodePorts to be accessed via
// localhost (iptables mode only)
LocalhostNodePorts *bool `json:"localhostNodePorts"`
// syncPeriod is the period that iptables rules are refreshed (e.g. '5s', '1m', // syncPeriod is the period that iptables rules are refreshed (e.g. '5s', '1m',
// '2h22m'). Must be greater than 0. // '2h22m'). Must be greater than 0.
SyncPeriod metav1.Duration `json:"syncPeriod"` SyncPeriod metav1.Duration `json:"syncPeriod"`

View File

@ -135,6 +135,11 @@ func (in *KubeProxyIPTablesConfiguration) DeepCopyInto(out *KubeProxyIPTablesCon
*out = new(int32) *out = new(int32)
**out = **in **out = **in
} }
if in.LocalhostNodePorts != nil {
in, out := &in.LocalhostNodePorts, &out.LocalhostNodePorts
*out = new(bool)
**out = **in
}
out.SyncPeriod = in.SyncPeriod out.SyncPeriod = in.SyncPeriod
out.MinSyncPeriod = in.MinSyncPeriod out.MinSyncPeriod = in.MinSyncPeriod
return return