mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	kube-proxy: add a flag to disables the allowing NodePort services to be accessed via localhost
This commit is contained in:
		@@ -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.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.Float32Var(&o.config.ClientConnection.QPS, "kube-api-qps", o.config.ClientConnection.QPS, "QPS to use while talking with kubernetes apiserver")
 | 
			
		||||
 
 | 
			
		||||
@@ -197,6 +197,7 @@ func newProxyServer(
 | 
			
		||||
				config.IPTables.SyncPeriod.Duration,
 | 
			
		||||
				config.IPTables.MinSyncPeriod.Duration,
 | 
			
		||||
				config.IPTables.MasqueradeAll,
 | 
			
		||||
				*config.IPTables.LocalhostNodePorts,
 | 
			
		||||
				int(*config.IPTables.MasqueradeBit),
 | 
			
		||||
				localDetectors,
 | 
			
		||||
				hostname,
 | 
			
		||||
@@ -221,6 +222,7 @@ func newProxyServer(
 | 
			
		||||
				config.IPTables.SyncPeriod.Duration,
 | 
			
		||||
				config.IPTables.MinSyncPeriod.Duration,
 | 
			
		||||
				config.IPTables.MasqueradeAll,
 | 
			
		||||
				*config.IPTables.LocalhostNodePorts,
 | 
			
		||||
				int(*config.IPTables.MasqueradeBit),
 | 
			
		||||
				localDetector,
 | 
			
		||||
				hostname,
 | 
			
		||||
 
 | 
			
		||||
@@ -107,6 +107,7 @@ iptables:
 | 
			
		||||
  masqueradeBit: 17
 | 
			
		||||
  minSyncPeriod: 10s
 | 
			
		||||
  syncPeriod: 60s
 | 
			
		||||
  localhostNodePorts: true
 | 
			
		||||
ipvs:
 | 
			
		||||
  minSyncPeriod: 10s
 | 
			
		||||
  syncPeriod: 60s
 | 
			
		||||
@@ -246,10 +247,11 @@ nodePortAddresses:
 | 
			
		||||
			HealthzBindAddress: tc.healthzBindAddress,
 | 
			
		||||
			HostnameOverride:   "foo",
 | 
			
		||||
			IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
 | 
			
		||||
				MasqueradeAll: true,
 | 
			
		||||
				MasqueradeBit: pointer.Int32(17),
 | 
			
		||||
				MinSyncPeriod: metav1.Duration{Duration: 10 * time.Second},
 | 
			
		||||
				SyncPeriod:    metav1.Duration{Duration: 60 * time.Second},
 | 
			
		||||
				MasqueradeAll:      true,
 | 
			
		||||
				MasqueradeBit:      pointer.Int32(17),
 | 
			
		||||
				LocalhostNodePorts: pointer.Bool(true),
 | 
			
		||||
				MinSyncPeriod:      metav1.Duration{Duration: 10 * time.Second},
 | 
			
		||||
				SyncPeriod:         metav1.Duration{Duration: 60 * time.Second},
 | 
			
		||||
			},
 | 
			
		||||
			IPVS: kubeproxyconfig.KubeProxyIPVSConfiguration{
 | 
			
		||||
				MinSyncPeriod: metav1.Duration{Duration: 10 * time.Second},
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								pkg/generated/openapi/zz_generated.openapi.go
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										9
									
								
								pkg/generated/openapi/zz_generated.openapi.go
									
									
									
										generated
									
									
									
								
							@@ -51650,6 +51650,13 @@ func schema_k8sio_kube_proxy_config_v1alpha1_KubeProxyIPTablesConfiguration(ref
 | 
			
		||||
							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": {
 | 
			
		||||
						SchemaProps: spec.SchemaProps{
 | 
			
		||||
							Description: "syncPeriod is the period that iptables rules are refreshed (e.g. '5s', '1m', '2h22m').  Must be greater than 0.",
 | 
			
		||||
@@ -51665,7 +51672,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{
 | 
			
		||||
 
 | 
			
		||||
@@ -94,6 +94,7 @@ func NewHollowProxyOrDie(
 | 
			
		||||
			proxierSyncPeriod,
 | 
			
		||||
			proxierMinSyncPeriod,
 | 
			
		||||
			false,
 | 
			
		||||
			false,
 | 
			
		||||
			0,
 | 
			
		||||
			proxyutiliptables.NewNoOpLocalDetector(),
 | 
			
		||||
			nodeName,
 | 
			
		||||
 
 | 
			
		||||
@@ -42,6 +42,7 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
 | 
			
		||||
			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.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.OOMScoreAdj = pointer.Int32(c.Int31())
 | 
			
		||||
			obj.ClientConnection.ContentType = "bar"
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@ enableProfiling: false
 | 
			
		||||
healthzBindAddress: 0.0.0.0:10256
 | 
			
		||||
hostnameOverride: ""
 | 
			
		||||
iptables:
 | 
			
		||||
  localhostNodePorts: true
 | 
			
		||||
  masqueradeAll: false
 | 
			
		||||
  masqueradeBit: 14
 | 
			
		||||
  minSyncPeriod: 1s
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@ enableProfiling: false
 | 
			
		||||
healthzBindAddress: 0.0.0.0:10256
 | 
			
		||||
hostnameOverride: ""
 | 
			
		||||
iptables:
 | 
			
		||||
  localhostNodePorts: true
 | 
			
		||||
  masqueradeAll: false
 | 
			
		||||
  masqueradeBit: 14
 | 
			
		||||
  minSyncPeriod: 1s
 | 
			
		||||
 
 | 
			
		||||
@@ -33,6 +33,9 @@ type KubeProxyIPTablesConfiguration struct {
 | 
			
		||||
	MasqueradeBit *int32
 | 
			
		||||
	// masqueradeAll tells kube-proxy to SNAT everything if using the pure iptables proxy mode.
 | 
			
		||||
	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',
 | 
			
		||||
	// '2h22m').  Must be greater than 0.
 | 
			
		||||
	SyncPeriod metav1.Duration
 | 
			
		||||
 
 | 
			
		||||
@@ -64,6 +64,9 @@ func SetDefaults_KubeProxyConfiguration(obj *kubeproxyconfigv1alpha1.KubeProxyCo
 | 
			
		||||
	if obj.IPTables.MinSyncPeriod.Duration == 0 {
 | 
			
		||||
		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 {
 | 
			
		||||
		obj.IPVS.SyncPeriod = metav1.Duration{Duration: 30 * time.Second}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@ limitations under the License.
 | 
			
		||||
package v1alpha1
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"k8s.io/utils/pointer"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
@@ -27,7 +28,6 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestDefaultsKubeProxyConfiguration(t *testing.T) {
 | 
			
		||||
	masqBit := int32(14)
 | 
			
		||||
	oomScore := int32(-999)
 | 
			
		||||
	ctMaxPerCore := int32(32768)
 | 
			
		||||
	ctMin := int32(131072)
 | 
			
		||||
@@ -50,10 +50,11 @@ func TestDefaultsKubeProxyConfiguration(t *testing.T) {
 | 
			
		||||
					Burst:       10,
 | 
			
		||||
				},
 | 
			
		||||
				IPTables: kubeproxyconfigv1alpha1.KubeProxyIPTablesConfiguration{
 | 
			
		||||
					MasqueradeBit: &masqBit,
 | 
			
		||||
					MasqueradeAll: false,
 | 
			
		||||
					SyncPeriod:    metav1.Duration{Duration: 30 * time.Second},
 | 
			
		||||
					MinSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
 | 
			
		||||
					MasqueradeBit:      pointer.Int32(14),
 | 
			
		||||
					MasqueradeAll:      false,
 | 
			
		||||
					LocalhostNodePorts: pointer.Bool(true),
 | 
			
		||||
					SyncPeriod:         metav1.Duration{Duration: 30 * time.Second},
 | 
			
		||||
					MinSyncPeriod:      metav1.Duration{Duration: 1 * time.Second},
 | 
			
		||||
				},
 | 
			
		||||
				IPVS: kubeproxyconfigv1alpha1.KubeProxyIPVSConfiguration{
 | 
			
		||||
					SyncPeriod: metav1.Duration{Duration: 30 * time.Second},
 | 
			
		||||
@@ -85,10 +86,11 @@ func TestDefaultsKubeProxyConfiguration(t *testing.T) {
 | 
			
		||||
					Burst:       10,
 | 
			
		||||
				},
 | 
			
		||||
				IPTables: kubeproxyconfigv1alpha1.KubeProxyIPTablesConfiguration{
 | 
			
		||||
					MasqueradeBit: &masqBit,
 | 
			
		||||
					MasqueradeAll: false,
 | 
			
		||||
					SyncPeriod:    metav1.Duration{Duration: 30 * time.Second},
 | 
			
		||||
					MinSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
 | 
			
		||||
					MasqueradeBit:      pointer.Int32(14),
 | 
			
		||||
					MasqueradeAll:      false,
 | 
			
		||||
					LocalhostNodePorts: pointer.Bool(true),
 | 
			
		||||
					SyncPeriod:         metav1.Duration{Duration: 30 * time.Second},
 | 
			
		||||
					MinSyncPeriod:      metav1.Duration{Duration: 1 * time.Second},
 | 
			
		||||
				},
 | 
			
		||||
				IPVS: kubeproxyconfigv1alpha1.KubeProxyIPVSConfiguration{
 | 
			
		||||
					SyncPeriod: metav1.Duration{Duration: 30 * time.Second},
 | 
			
		||||
 
 | 
			
		||||
@@ -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 {
 | 
			
		||||
	out.MasqueradeBit = (*int32)(unsafe.Pointer(in.MasqueradeBit))
 | 
			
		||||
	out.MasqueradeAll = in.MasqueradeAll
 | 
			
		||||
	out.LocalhostNodePorts = (*bool)(unsafe.Pointer(in.LocalhostNodePorts))
 | 
			
		||||
	out.SyncPeriod = in.SyncPeriod
 | 
			
		||||
	out.MinSyncPeriod = in.MinSyncPeriod
 | 
			
		||||
	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 {
 | 
			
		||||
	out.MasqueradeBit = (*int32)(unsafe.Pointer(in.MasqueradeBit))
 | 
			
		||||
	out.MasqueradeAll = in.MasqueradeAll
 | 
			
		||||
	out.LocalhostNodePorts = (*bool)(unsafe.Pointer(in.LocalhostNodePorts))
 | 
			
		||||
	out.SyncPeriod = in.SyncPeriod
 | 
			
		||||
	out.MinSyncPeriod = in.MinSyncPeriod
 | 
			
		||||
	return nil
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								pkg/proxy/apis/config/zz_generated.deepcopy.go
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								pkg/proxy/apis/config/zz_generated.deepcopy.go
									
									
									
										generated
									
									
									
								
							@@ -157,6 +157,11 @@ func (in *KubeProxyIPTablesConfiguration) DeepCopyInto(out *KubeProxyIPTablesCon
 | 
			
		||||
		*out = new(int32)
 | 
			
		||||
		**out = **in
 | 
			
		||||
	}
 | 
			
		||||
	if in.LocalhostNodePorts != nil {
 | 
			
		||||
		in, out := &in.LocalhostNodePorts, &out.LocalhostNodePorts
 | 
			
		||||
		*out = new(bool)
 | 
			
		||||
		**out = **in
 | 
			
		||||
	}
 | 
			
		||||
	out.SyncPeriod = in.SyncPeriod
 | 
			
		||||
	out.MinSyncPeriod = in.MinSyncPeriod
 | 
			
		||||
	return
 | 
			
		||||
 
 | 
			
		||||
@@ -203,7 +203,10 @@ type Proxier struct {
 | 
			
		||||
	// optimize for performance over debuggability.
 | 
			
		||||
	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
 | 
			
		||||
	// networkInterfacer defines an interface for several net library functions.
 | 
			
		||||
	// Inject for test purpose.
 | 
			
		||||
@@ -224,6 +227,7 @@ func NewProxier(ipt utiliptables.Interface,
 | 
			
		||||
	syncPeriod time.Duration,
 | 
			
		||||
	minSyncPeriod time.Duration,
 | 
			
		||||
	masqueradeAll bool,
 | 
			
		||||
	localhostNodePorts bool,
 | 
			
		||||
	masqueradeBit int,
 | 
			
		||||
	localDetector proxyutiliptables.LocalTrafficDetector,
 | 
			
		||||
	hostname string,
 | 
			
		||||
@@ -232,9 +236,10 @@ func NewProxier(ipt utiliptables.Interface,
 | 
			
		||||
	healthzServer healthcheck.ProxierHealthUpdater,
 | 
			
		||||
	nodePortAddresses []string,
 | 
			
		||||
) (*Proxier, error) {
 | 
			
		||||
	if utilproxy.ContainsIPv4Loopback(nodePortAddresses) {
 | 
			
		||||
	if localhostNodePorts && utilproxy.ContainsIPv4Loopback(nodePortAddresses) {
 | 
			
		||||
		// 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 {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
@@ -289,6 +294,7 @@ func NewProxier(ipt utiliptables.Interface,
 | 
			
		||||
		filterRules:              utilproxy.LineBuffer{},
 | 
			
		||||
		natChains:                utilproxy.LineBuffer{},
 | 
			
		||||
		natRules:                 utilproxy.LineBuffer{},
 | 
			
		||||
		localhostNodePorts:       localhostNodePorts,
 | 
			
		||||
		nodePortAddresses:        nodePortAddresses,
 | 
			
		||||
		networkInterfacer:        utilproxy.RealNetwork{},
 | 
			
		||||
	}
 | 
			
		||||
@@ -320,6 +326,7 @@ func NewDualStackProxier(
 | 
			
		||||
	syncPeriod time.Duration,
 | 
			
		||||
	minSyncPeriod time.Duration,
 | 
			
		||||
	masqueradeAll bool,
 | 
			
		||||
	localhostNodePorts bool,
 | 
			
		||||
	masqueradeBit int,
 | 
			
		||||
	localDetectors [2]proxyutiliptables.LocalTrafficDetector,
 | 
			
		||||
	hostname string,
 | 
			
		||||
@@ -331,14 +338,14 @@ func NewDualStackProxier(
 | 
			
		||||
	// Create an ipv4 instance of the single-stack proxier
 | 
			
		||||
	ipFamilyMap := utilproxy.MapCIDRsByIPFamily(nodePortAddresses)
 | 
			
		||||
	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])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("unable to create ipv4 proxier: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		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.
 | 
			
		||||
	for address := range nodeAddresses {
 | 
			
		||||
		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(
 | 
			
		||||
				"-A", string(kubeServicesChain),
 | 
			
		||||
				"-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))
 | 
			
		||||
			// Nothing else matters after the zero CIDR.
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Ignore IP addresses with incorrect version
 | 
			
		||||
		if isIPv6 && !netutils.IsIPv6String(address) || !isIPv6 && netutils.IsIPv6String(address) {
 | 
			
		||||
			klog.ErrorS(nil, "IP has incorrect IP version", "IP", address)
 | 
			
		||||
			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
 | 
			
		||||
		proxier.natRules.Write(
 | 
			
		||||
			"-A", string(kubeServicesChain),
 | 
			
		||||
 
 | 
			
		||||
@@ -421,6 +421,7 @@ func NewFakeProxier(ipt utiliptables.Interface) *Proxier {
 | 
			
		||||
		natChains:                utilproxy.LineBuffer{},
 | 
			
		||||
		natRules:                 utilproxy.LineBuffer{},
 | 
			
		||||
		nodeIP:                   netutils.ParseIPSloppy(testNodeIP),
 | 
			
		||||
		localhostNodePorts:       true,
 | 
			
		||||
		nodePortAddresses:        make([]string, 0),
 | 
			
		||||
		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) {
 | 
			
		||||
	ipt := iptablestest.NewFake()
 | 
			
		||||
	fp := NewFakeProxier(ipt)
 | 
			
		||||
 
 | 
			
		||||
@@ -118,6 +118,15 @@ func IsZeroCIDR(cidr string) bool {
 | 
			
		||||
	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
 | 
			
		||||
func IsProxyableIP(ip string) error {
 | 
			
		||||
	netIP := netutils.ParseIPSloppy(ip)
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,9 @@ type KubeProxyIPTablesConfiguration struct {
 | 
			
		||||
	MasqueradeBit *int32 `json:"masqueradeBit"`
 | 
			
		||||
	// masqueradeAll tells kube-proxy to SNAT everything if using the pure iptables proxy mode.
 | 
			
		||||
	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',
 | 
			
		||||
	// '2h22m').  Must be greater than 0.
 | 
			
		||||
	SyncPeriod metav1.Duration `json:"syncPeriod"`
 | 
			
		||||
 
 | 
			
		||||
@@ -135,6 +135,11 @@ func (in *KubeProxyIPTablesConfiguration) DeepCopyInto(out *KubeProxyIPTablesCon
 | 
			
		||||
		*out = new(int32)
 | 
			
		||||
		**out = **in
 | 
			
		||||
	}
 | 
			
		||||
	if in.LocalhostNodePorts != nil {
 | 
			
		||||
		in, out := &in.LocalhostNodePorts, &out.LocalhostNodePorts
 | 
			
		||||
		*out = new(bool)
 | 
			
		||||
		**out = **in
 | 
			
		||||
	}
 | 
			
		||||
	out.SyncPeriod = in.SyncPeriod
 | 
			
		||||
	out.MinSyncPeriod = in.MinSyncPeriod
 | 
			
		||||
	return
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user