Skip to content

Commit 9924814

Browse files
authored
Merge pull request #108460 from Nordix/issue-72236
Prevent host access on VIP addresses in proxy-mode=ipvs
2 parents 3710cc8 + c1e5a9e commit 9924814

File tree

5 files changed

+47
-1
lines changed

5 files changed

+47
-1
lines changed

pkg/proxy/ipvs/ipset.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ const (
7878

7979
kubeHealthCheckNodePortSetComment = "Kubernetes health check node port"
8080
kubeHealthCheckNodePortSet = "KUBE-HEALTH-CHECK-NODE-PORT"
81+
82+
kubeIPVSSetComment = "Addresses on the ipvs interface"
83+
kubeIPVSSet = "KUBE-IPVS-IPS"
8184
)
8285

8386
// IPSetVersioner can query the current ipset version.

pkg/proxy/ipvs/proxier.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ const (
8484
// kubeLoadBalancerChain is the kubernetes chain for loadbalancer type service
8585
kubeLoadBalancerChain utiliptables.Chain = "KUBE-LOAD-BALANCER"
8686

87+
// kubeIPVSFilterChain filters external access to main netns
88+
// https://github.com/kubernetes/kubernetes/issues/72236
89+
kubeIPVSFilterChain utiliptables.Chain = "KUBE-IPVS-FILTER"
90+
8791
// defaultScheduler is the default ipvs scheduler algorithm - round robin.
8892
defaultScheduler = "rr"
8993

@@ -112,6 +116,7 @@ var iptablesJumpChain = []struct {
112116
{utiliptables.TableFilter, utiliptables.ChainInput, kubeNodePortChain, "kubernetes health check rules"},
113117
{utiliptables.TableFilter, utiliptables.ChainInput, kubeProxyFirewallChain, "kube-proxy firewall rules"},
114118
{utiliptables.TableFilter, utiliptables.ChainForward, kubeProxyFirewallChain, "kube-proxy firewall rules"},
119+
{utiliptables.TableFilter, utiliptables.ChainInput, kubeIPVSFilterChain, "kubernetes ipvs access filter"},
115120
}
116121

117122
var iptablesChains = []struct {
@@ -127,6 +132,7 @@ var iptablesChains = []struct {
127132
{utiliptables.TableFilter, kubeNodePortChain},
128133
{utiliptables.TableFilter, kubeProxyFirewallChain},
129134
{utiliptables.TableFilter, kubeSourceRangesFirewallChain},
135+
{utiliptables.TableFilter, kubeIPVSFilterChain},
130136
}
131137

132138
var iptablesCleanupChains = []struct {
@@ -141,6 +147,7 @@ var iptablesCleanupChains = []struct {
141147
{utiliptables.TableFilter, kubeNodePortChain},
142148
{utiliptables.TableFilter, kubeProxyFirewallChain},
143149
{utiliptables.TableFilter, kubeSourceRangesFirewallChain},
150+
{utiliptables.TableFilter, kubeIPVSFilterChain},
144151
}
145152

146153
// ipsetInfo is all ipset we needed in ipvs proxier
@@ -165,6 +172,7 @@ var ipsetInfo = []struct {
165172
{kubeNodePortSetSCTP, utilipset.HashIPPort, kubeNodePortSetSCTPComment},
166173
{kubeNodePortLocalSetSCTP, utilipset.HashIPPort, kubeNodePortLocalSetSCTPComment},
167174
{kubeHealthCheckNodePortSet, utilipset.BitmapPort, kubeHealthCheckNodePortSetComment},
175+
{kubeIPVSSet, utilipset.HashIP, kubeIPVSSetComment},
168176
}
169177

170178
// ipsetWithIptablesChain is the ipsets list with iptables source chain and the chain jump to
@@ -1549,6 +1557,9 @@ func (proxier *Proxier) syncProxyRules() {
15491557
}
15501558
}
15511559

1560+
// Set the KUBE-IPVS-IPS set to the "activeBindAddrs"
1561+
proxier.ipsetList[kubeIPVSSet].activeEntries = sets.StringKeySet(activeBindAddrs)
1562+
15521563
// sync ipset entries
15531564
for _, set := range proxier.ipsetList {
15541565
set.syncIPSetEntries()
@@ -1792,6 +1803,22 @@ func (proxier *Proxier) writeIptablesRules() {
17921803
"-j", "ACCEPT",
17931804
)
17941805

1806+
// Add rules to the filter/KUBE-IPVS-FILTER chain to prevent access to ports on the host through VIP addresses.
1807+
// https://github.com/kubernetes/kubernetes/issues/72236
1808+
proxier.filterRules.Write(
1809+
"-A", string(kubeIPVSFilterChain),
1810+
"-m", "set", "--match-set", proxier.ipsetList[kubeLoadBalancerSet].Name, "dst,dst", "-j", "ACCEPT")
1811+
proxier.filterRules.Write(
1812+
"-A", string(kubeIPVSFilterChain),
1813+
"-m", "set", "--match-set", proxier.ipsetList[kubeClusterIPSet].Name, "dst,dst", "-j", "ACCEPT")
1814+
proxier.filterRules.Write(
1815+
"-A", string(kubeIPVSFilterChain),
1816+
"-m", "set", "--match-set", proxier.ipsetList[kubeExternalIPSet].Name, "dst,dst", "-j", "ACCEPT")
1817+
proxier.filterRules.Write(
1818+
"-A", string(kubeIPVSFilterChain),
1819+
"-m", "conntrack", "--ctstate", "NEW",
1820+
"-m", "set", "--match-set", proxier.ipsetList[kubeIPVSSet].Name, "dst", "-j", "REJECT")
1821+
17951822
// Install the kubernetes-specific postrouting rules. We use a whole chain for
17961823
// this so that it is easier to flush and change, for example if the mark
17971824
// value should ever change.

pkg/proxy/ipvs/proxier_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4772,6 +4772,7 @@ func TestCreateAndLinkKubeChain(t *testing.T) {
47724772
:KUBE-NODE-PORT - [0:0]
47734773
:KUBE-PROXY-FIREWALL - [0:0]
47744774
:KUBE-SOURCE-RANGES-FIREWALL - [0:0]
4775+
:KUBE-IPVS-FILTER - [0:0]
47754776
`
47764777
assert.Equal(t, expectedNATChains, string(fp.natChains.Bytes()))
47774778
assert.Equal(t, expectedFilterChains, string(fp.filterChains.Bytes()))

pkg/util/ipset/ipset.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,11 @@ func (e *Entry) Validate(set *IPSet) bool {
169169
return false
170170
}
171171
switch e.SetType {
172+
case HashIP:
173+
//check if IP of Entry is valid.
174+
if valid := e.checkIP(set); !valid {
175+
return false
176+
}
172177
case HashIPPort:
173178
//check if IP and Protocol of Entry is valid.
174179
if valid := e.checkIPandProtocol(set); !valid {
@@ -219,6 +224,9 @@ func (e *Entry) Validate(set *IPSet) bool {
219224
// String returns the string format for ipset entry.
220225
func (e *Entry) String() string {
221226
switch e.SetType {
227+
case HashIP:
228+
// Entry{192.168.1.1} -> 192.168.1.1
229+
return fmt.Sprintf("%s", e.IP)
222230
case HashIPPort:
223231
// Entry{192.168.1.1, udp, 53} -> 192.168.1.1,udp:53
224232
// Entry{192.168.1.2, tcp, 8080} -> 192.168.1.2,tcp:8080
@@ -247,7 +255,11 @@ func (e *Entry) checkIPandProtocol(set *IPSet) bool {
247255
} else if !validateProtocol(e.Protocol) {
248256
return false
249257
}
258+
return e.checkIP(set)
259+
}
250260

261+
// checkIP checks if IP of Entry is valid.
262+
func (e *Entry) checkIP(set *IPSet) bool {
251263
if netutils.ParseIPSloppy(e.IP) == nil {
252264
klog.Errorf("Error parsing entry %v ip address %v for ipset %v", e, e.IP, set)
253265
return false
@@ -283,7 +295,7 @@ func (runner *runner) CreateSet(set *IPSet, ignoreExistErr bool) error {
283295
// otherwise raised when the same set (setname and create parameters are identical) already exists.
284296
func (runner *runner) createSet(set *IPSet, ignoreExistErr bool) error {
285297
args := []string{"create", set.Name, string(set.SetType)}
286-
if set.SetType == HashIPPortIP || set.SetType == HashIPPort || set.SetType == HashIPPortNet {
298+
if set.SetType == HashIPPortIP || set.SetType == HashIPPort || set.SetType == HashIPPortNet || set.SetType == HashIP {
287299
args = append(args,
288300
"family", set.HashFamily,
289301
"hashsize", strconv.Itoa(set.HashSize),

pkg/util/ipset/types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ const (
3535
// BitmapPort represents the `bitmap:port` type ipset. The bitmap:port set type uses a memory range, where each bit
3636
// represents one TCP/UDP port. A bitmap:port type of set can store up to 65535 ports.
3737
BitmapPort Type = "bitmap:port"
38+
// HashIP represents the `hash:ip` type ipset.
39+
HashIP Type = "hash:ip"
3840
)
3941

4042
// DefaultPortRange defines the default bitmap:port valid port range.
@@ -59,4 +61,5 @@ var ValidIPSetTypes = []Type{
5961
HashIPPortIP,
6062
BitmapPort,
6163
HashIPPortNet,
64+
HashIP,
6265
}

0 commit comments

Comments
 (0)