mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Merge pull request #108460 from Nordix/issue-72236
Prevent host access on VIP addresses in proxy-mode=ipvs
This commit is contained in:
		@@ -78,6 +78,9 @@ const (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	kubeHealthCheckNodePortSetComment = "Kubernetes health check node port"
 | 
						kubeHealthCheckNodePortSetComment = "Kubernetes health check node port"
 | 
				
			||||||
	kubeHealthCheckNodePortSet        = "KUBE-HEALTH-CHECK-NODE-PORT"
 | 
						kubeHealthCheckNodePortSet        = "KUBE-HEALTH-CHECK-NODE-PORT"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kubeIPVSSetComment = "Addresses on the ipvs interface"
 | 
				
			||||||
 | 
						kubeIPVSSet        = "KUBE-IPVS-IPS"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IPSetVersioner can query the current ipset version.
 | 
					// IPSetVersioner can query the current ipset version.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -84,6 +84,10 @@ const (
 | 
				
			|||||||
	// kubeLoadBalancerChain is the kubernetes chain for loadbalancer type service
 | 
						// kubeLoadBalancerChain is the kubernetes chain for loadbalancer type service
 | 
				
			||||||
	kubeLoadBalancerChain utiliptables.Chain = "KUBE-LOAD-BALANCER"
 | 
						kubeLoadBalancerChain utiliptables.Chain = "KUBE-LOAD-BALANCER"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// kubeIPVSFilterChain filters external access to main netns
 | 
				
			||||||
 | 
						// https://github.com/kubernetes/kubernetes/issues/72236
 | 
				
			||||||
 | 
						kubeIPVSFilterChain utiliptables.Chain = "KUBE-IPVS-FILTER"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// defaultScheduler is the default ipvs scheduler algorithm - round robin.
 | 
						// defaultScheduler is the default ipvs scheduler algorithm - round robin.
 | 
				
			||||||
	defaultScheduler = "rr"
 | 
						defaultScheduler = "rr"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -112,6 +116,7 @@ var iptablesJumpChain = []struct {
 | 
				
			|||||||
	{utiliptables.TableFilter, utiliptables.ChainInput, kubeNodePortChain, "kubernetes health check rules"},
 | 
						{utiliptables.TableFilter, utiliptables.ChainInput, kubeNodePortChain, "kubernetes health check rules"},
 | 
				
			||||||
	{utiliptables.TableFilter, utiliptables.ChainInput, kubeProxyFirewallChain, "kube-proxy firewall rules"},
 | 
						{utiliptables.TableFilter, utiliptables.ChainInput, kubeProxyFirewallChain, "kube-proxy firewall rules"},
 | 
				
			||||||
	{utiliptables.TableFilter, utiliptables.ChainForward, kubeProxyFirewallChain, "kube-proxy firewall rules"},
 | 
						{utiliptables.TableFilter, utiliptables.ChainForward, kubeProxyFirewallChain, "kube-proxy firewall rules"},
 | 
				
			||||||
 | 
						{utiliptables.TableFilter, utiliptables.ChainInput, kubeIPVSFilterChain, "kubernetes ipvs access filter"},
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var iptablesChains = []struct {
 | 
					var iptablesChains = []struct {
 | 
				
			||||||
@@ -127,6 +132,7 @@ var iptablesChains = []struct {
 | 
				
			|||||||
	{utiliptables.TableFilter, kubeNodePortChain},
 | 
						{utiliptables.TableFilter, kubeNodePortChain},
 | 
				
			||||||
	{utiliptables.TableFilter, kubeProxyFirewallChain},
 | 
						{utiliptables.TableFilter, kubeProxyFirewallChain},
 | 
				
			||||||
	{utiliptables.TableFilter, kubeSourceRangesFirewallChain},
 | 
						{utiliptables.TableFilter, kubeSourceRangesFirewallChain},
 | 
				
			||||||
 | 
						{utiliptables.TableFilter, kubeIPVSFilterChain},
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var iptablesCleanupChains = []struct {
 | 
					var iptablesCleanupChains = []struct {
 | 
				
			||||||
@@ -141,6 +147,7 @@ var iptablesCleanupChains = []struct {
 | 
				
			|||||||
	{utiliptables.TableFilter, kubeNodePortChain},
 | 
						{utiliptables.TableFilter, kubeNodePortChain},
 | 
				
			||||||
	{utiliptables.TableFilter, kubeProxyFirewallChain},
 | 
						{utiliptables.TableFilter, kubeProxyFirewallChain},
 | 
				
			||||||
	{utiliptables.TableFilter, kubeSourceRangesFirewallChain},
 | 
						{utiliptables.TableFilter, kubeSourceRangesFirewallChain},
 | 
				
			||||||
 | 
						{utiliptables.TableFilter, kubeIPVSFilterChain},
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ipsetInfo is all ipset we needed in ipvs proxier
 | 
					// ipsetInfo is all ipset we needed in ipvs proxier
 | 
				
			||||||
@@ -165,6 +172,7 @@ var ipsetInfo = []struct {
 | 
				
			|||||||
	{kubeNodePortSetSCTP, utilipset.HashIPPort, kubeNodePortSetSCTPComment},
 | 
						{kubeNodePortSetSCTP, utilipset.HashIPPort, kubeNodePortSetSCTPComment},
 | 
				
			||||||
	{kubeNodePortLocalSetSCTP, utilipset.HashIPPort, kubeNodePortLocalSetSCTPComment},
 | 
						{kubeNodePortLocalSetSCTP, utilipset.HashIPPort, kubeNodePortLocalSetSCTPComment},
 | 
				
			||||||
	{kubeHealthCheckNodePortSet, utilipset.BitmapPort, kubeHealthCheckNodePortSetComment},
 | 
						{kubeHealthCheckNodePortSet, utilipset.BitmapPort, kubeHealthCheckNodePortSetComment},
 | 
				
			||||||
 | 
						{kubeIPVSSet, utilipset.HashIP, kubeIPVSSetComment},
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ipsetWithIptablesChain is the ipsets list with iptables source chain and the chain jump to
 | 
					// ipsetWithIptablesChain is the ipsets list with iptables source chain and the chain jump to
 | 
				
			||||||
@@ -1549,6 +1557,9 @@ func (proxier *Proxier) syncProxyRules() {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Set the KUBE-IPVS-IPS set to the "activeBindAddrs"
 | 
				
			||||||
 | 
						proxier.ipsetList[kubeIPVSSet].activeEntries = sets.StringKeySet(activeBindAddrs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// sync ipset entries
 | 
						// sync ipset entries
 | 
				
			||||||
	for _, set := range proxier.ipsetList {
 | 
						for _, set := range proxier.ipsetList {
 | 
				
			||||||
		set.syncIPSetEntries()
 | 
							set.syncIPSetEntries()
 | 
				
			||||||
@@ -1792,6 +1803,22 @@ func (proxier *Proxier) writeIptablesRules() {
 | 
				
			|||||||
		"-j", "ACCEPT",
 | 
							"-j", "ACCEPT",
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Add rules to the filter/KUBE-IPVS-FILTER chain to prevent access to ports on the host through VIP addresses.
 | 
				
			||||||
 | 
						// https://github.com/kubernetes/kubernetes/issues/72236
 | 
				
			||||||
 | 
						proxier.filterRules.Write(
 | 
				
			||||||
 | 
							"-A", string(kubeIPVSFilterChain),
 | 
				
			||||||
 | 
							"-m", "set", "--match-set", proxier.ipsetList[kubeLoadBalancerSet].Name, "dst,dst", "-j", "ACCEPT")
 | 
				
			||||||
 | 
						proxier.filterRules.Write(
 | 
				
			||||||
 | 
							"-A", string(kubeIPVSFilterChain),
 | 
				
			||||||
 | 
							"-m", "set", "--match-set", proxier.ipsetList[kubeClusterIPSet].Name, "dst,dst", "-j", "ACCEPT")
 | 
				
			||||||
 | 
						proxier.filterRules.Write(
 | 
				
			||||||
 | 
							"-A", string(kubeIPVSFilterChain),
 | 
				
			||||||
 | 
							"-m", "set", "--match-set", proxier.ipsetList[kubeExternalIPSet].Name, "dst,dst", "-j", "ACCEPT")
 | 
				
			||||||
 | 
						proxier.filterRules.Write(
 | 
				
			||||||
 | 
							"-A", string(kubeIPVSFilterChain),
 | 
				
			||||||
 | 
							"-m", "conntrack", "--ctstate", "NEW",
 | 
				
			||||||
 | 
							"-m", "set", "--match-set", proxier.ipsetList[kubeIPVSSet].Name, "dst", "-j", "REJECT")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Install the kubernetes-specific postrouting rules. We use a whole chain for
 | 
						// Install the kubernetes-specific postrouting rules. We use a whole chain for
 | 
				
			||||||
	// this so that it is easier to flush and change, for example if the mark
 | 
						// this so that it is easier to flush and change, for example if the mark
 | 
				
			||||||
	// value should ever change.
 | 
						// value should ever change.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4772,6 +4772,7 @@ func TestCreateAndLinkKubeChain(t *testing.T) {
 | 
				
			|||||||
:KUBE-NODE-PORT - [0:0]
 | 
					:KUBE-NODE-PORT - [0:0]
 | 
				
			||||||
:KUBE-PROXY-FIREWALL - [0:0]
 | 
					:KUBE-PROXY-FIREWALL - [0:0]
 | 
				
			||||||
:KUBE-SOURCE-RANGES-FIREWALL - [0:0]
 | 
					:KUBE-SOURCE-RANGES-FIREWALL - [0:0]
 | 
				
			||||||
 | 
					:KUBE-IPVS-FILTER - [0:0]
 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
	assert.Equal(t, expectedNATChains, string(fp.natChains.Bytes()))
 | 
						assert.Equal(t, expectedNATChains, string(fp.natChains.Bytes()))
 | 
				
			||||||
	assert.Equal(t, expectedFilterChains, string(fp.filterChains.Bytes()))
 | 
						assert.Equal(t, expectedFilterChains, string(fp.filterChains.Bytes()))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -169,6 +169,11 @@ func (e *Entry) Validate(set *IPSet) bool {
 | 
				
			|||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	switch e.SetType {
 | 
						switch e.SetType {
 | 
				
			||||||
 | 
						case HashIP:
 | 
				
			||||||
 | 
							//check if IP of Entry is valid.
 | 
				
			||||||
 | 
							if valid := e.checkIP(set); !valid {
 | 
				
			||||||
 | 
								return false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	case HashIPPort:
 | 
						case HashIPPort:
 | 
				
			||||||
		//check if IP and Protocol of Entry is valid.
 | 
							//check if IP and Protocol of Entry is valid.
 | 
				
			||||||
		if valid := e.checkIPandProtocol(set); !valid {
 | 
							if valid := e.checkIPandProtocol(set); !valid {
 | 
				
			||||||
@@ -219,6 +224,9 @@ func (e *Entry) Validate(set *IPSet) bool {
 | 
				
			|||||||
// String returns the string format for ipset entry.
 | 
					// String returns the string format for ipset entry.
 | 
				
			||||||
func (e *Entry) String() string {
 | 
					func (e *Entry) String() string {
 | 
				
			||||||
	switch e.SetType {
 | 
						switch e.SetType {
 | 
				
			||||||
 | 
						case HashIP:
 | 
				
			||||||
 | 
							// Entry{192.168.1.1} -> 192.168.1.1
 | 
				
			||||||
 | 
							return fmt.Sprintf("%s", e.IP)
 | 
				
			||||||
	case HashIPPort:
 | 
						case HashIPPort:
 | 
				
			||||||
		// Entry{192.168.1.1, udp, 53} -> 192.168.1.1,udp:53
 | 
							// Entry{192.168.1.1, udp, 53} -> 192.168.1.1,udp:53
 | 
				
			||||||
		// Entry{192.168.1.2, tcp, 8080} -> 192.168.1.2,tcp:8080
 | 
							// Entry{192.168.1.2, tcp, 8080} -> 192.168.1.2,tcp:8080
 | 
				
			||||||
@@ -247,7 +255,11 @@ func (e *Entry) checkIPandProtocol(set *IPSet) bool {
 | 
				
			|||||||
	} else if !validateProtocol(e.Protocol) {
 | 
						} else if !validateProtocol(e.Protocol) {
 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return e.checkIP(set)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// checkIP checks if IP of Entry is valid.
 | 
				
			||||||
 | 
					func (e *Entry) checkIP(set *IPSet) bool {
 | 
				
			||||||
	if netutils.ParseIPSloppy(e.IP) == nil {
 | 
						if netutils.ParseIPSloppy(e.IP) == nil {
 | 
				
			||||||
		klog.Errorf("Error parsing entry %v ip address %v for ipset %v", e, e.IP, set)
 | 
							klog.Errorf("Error parsing entry %v ip address %v for ipset %v", e, e.IP, set)
 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
@@ -283,7 +295,7 @@ func (runner *runner) CreateSet(set *IPSet, ignoreExistErr bool) error {
 | 
				
			|||||||
// otherwise raised when the same set (setname and create parameters are identical) already exists.
 | 
					// otherwise raised when the same set (setname and create parameters are identical) already exists.
 | 
				
			||||||
func (runner *runner) createSet(set *IPSet, ignoreExistErr bool) error {
 | 
					func (runner *runner) createSet(set *IPSet, ignoreExistErr bool) error {
 | 
				
			||||||
	args := []string{"create", set.Name, string(set.SetType)}
 | 
						args := []string{"create", set.Name, string(set.SetType)}
 | 
				
			||||||
	if set.SetType == HashIPPortIP || set.SetType == HashIPPort || set.SetType == HashIPPortNet {
 | 
						if set.SetType == HashIPPortIP || set.SetType == HashIPPort || set.SetType == HashIPPortNet || set.SetType == HashIP {
 | 
				
			||||||
		args = append(args,
 | 
							args = append(args,
 | 
				
			||||||
			"family", set.HashFamily,
 | 
								"family", set.HashFamily,
 | 
				
			||||||
			"hashsize", strconv.Itoa(set.HashSize),
 | 
								"hashsize", strconv.Itoa(set.HashSize),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,6 +35,8 @@ const (
 | 
				
			|||||||
	// BitmapPort represents the `bitmap:port` type ipset.  The bitmap:port set type uses a memory range, where each bit
 | 
						// BitmapPort represents the `bitmap:port` type ipset.  The bitmap:port set type uses a memory range, where each bit
 | 
				
			||||||
	// represents one TCP/UDP port.  A bitmap:port type of set can store up to 65535 ports.
 | 
						// represents one TCP/UDP port.  A bitmap:port type of set can store up to 65535 ports.
 | 
				
			||||||
	BitmapPort Type = "bitmap:port"
 | 
						BitmapPort Type = "bitmap:port"
 | 
				
			||||||
 | 
						// HashIP represents the `hash:ip` type ipset.
 | 
				
			||||||
 | 
						HashIP Type = "hash:ip"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DefaultPortRange defines the default bitmap:port valid port range.
 | 
					// DefaultPortRange defines the default bitmap:port valid port range.
 | 
				
			||||||
@@ -59,4 +61,5 @@ var ValidIPSetTypes = []Type{
 | 
				
			|||||||
	HashIPPortIP,
 | 
						HashIPPortIP,
 | 
				
			||||||
	BitmapPort,
 | 
						BitmapPort,
 | 
				
			||||||
	HashIPPortNet,
 | 
						HashIPPortNet,
 | 
				
			||||||
 | 
						HashIP,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user