Skip to content

Commit 4e1679f

Browse files
committed
fix(NPC): don't add chains for missing family
On dual-stack nodes there can still be pods that are single stack. When this happens there won't be a pod IP for a given family and if kube-router tries to add rules with a missing pod IP the iptables rules won't be formatted correctly (because it won't have a valid source or destination for that family). So rather than breaking the whole iptables-restore we warn in the logs and skip the pod policy chains for that family.
1 parent a2bb2ba commit 4e1679f

File tree

2 files changed

+74
-29
lines changed

2 files changed

+74
-29
lines changed

pkg/controllers/netpol/pod.go

+50-27
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,17 @@ func (npc *NetworkPolicyController) syncPodFirewallChains(networkPoliciesInfo []
7878

7979
activePodFwChains := make(map[string]bool)
8080

81-
dropUnmarkedTrafficRules := func(podName, podNamespace, podFwChainName string) {
82-
for _, filterTableRules := range npc.filterTableRules {
81+
dropUnmarkedTrafficRules := func(pod podInfo, podFwChainName string) {
82+
for ipFamily, filterTableRules := range npc.filterTableRules {
83+
_, err := getPodIPForFamily(pod, ipFamily)
84+
if err != nil {
85+
klog.V(2).Infof("unable to get address for pod: %s -- skipping drop rules for pod "+
86+
"(this is normal for pods that are not dual-stack)", err.Error())
87+
continue
88+
}
89+
8390
// add rule to log the packets that will be dropped due to network policy enforcement
84-
comment := "\"rule to log dropped traffic POD name:" + podName + " namespace: " + podNamespace + "\""
91+
comment := "\"rule to log dropped traffic POD name:" + pod.name + " namespace: " + pod.namespace + "\""
8592
args := []string{"-A", podFwChainName, "-m", "comment", "--comment", comment,
8693
"-m", "mark", "!", "--mark", "0x10000/0x10000", "-j", "NFLOG",
8794
"--nflog-group", "100", "-m", "limit", "--limit", "10/minute", "--limit-burst", "10", "\n"}
@@ -93,7 +100,8 @@ func (npc *NetworkPolicyController) syncPodFirewallChains(networkPoliciesInfo []
93100
filterTableRules.WriteString(strings.Join(args, " "))
94101

95102
// add rule to DROP if no applicable network policy permits the traffic
96-
comment = "\"rule to REJECT traffic destined for POD name:" + podName + " namespace: " + podNamespace + "\""
103+
comment = "\"rule to REJECT traffic destined for POD name:" + pod.name + " namespace: " +
104+
pod.namespace + "\""
97105
args = []string{"-A", podFwChainName, "-m", "comment", "--comment", comment,
98106
"-m", "mark", "!", "--mark", "0x10000/0x10000", "-j", "REJECT", "\n"}
99107
filterTableRules.WriteString(strings.Join(args, " "))
@@ -113,7 +121,17 @@ func (npc *NetworkPolicyController) syncPodFirewallChains(networkPoliciesInfo []
113121

114122
// ensure pod specific firewall chain exist for all the pods that need ingress firewall
115123
podFwChainName := podFirewallChainName(pod.namespace, pod.name, version)
116-
for _, filterTableRules := range npc.filterTableRules {
124+
for ipFamily, filterTableRules := range npc.filterTableRules {
125+
_, err := getPodIPForFamily(pod, ipFamily)
126+
if err != nil {
127+
// If the pod doesn't have an address in this family we skip it here and all the various places below
128+
// because there won't be a valid source or destination address for iptables, and it will stop iptables
129+
// restore actions from completing successfully
130+
klog.Infof("unable to get address for pod: %s -- skipping pod chain for pod "+
131+
"(this is normal for pods that are not dual-stack)", err.Error())
132+
continue
133+
}
134+
117135
filterTableRules.WriteString(":" + podFwChainName + "\n")
118136
}
119137

@@ -128,9 +146,16 @@ func (npc *NetworkPolicyController) syncPodFirewallChains(networkPoliciesInfo []
128146
// setup rules to intercept inbound traffic to the pods
129147
npc.interceptPodOutboundTraffic(pod, podFwChainName)
130148

131-
dropUnmarkedTrafficRules(pod.name, pod.namespace, podFwChainName)
149+
dropUnmarkedTrafficRules(pod, podFwChainName)
150+
151+
for ipFamily, filterTableRules := range npc.filterTableRules {
152+
_, err := getPodIPForFamily(pod, ipFamily)
153+
if err != nil {
154+
klog.V(2).Infof("unable to get address for pod: %s -- skipping accept rules for pod "+
155+
"(this is normal for pods that are not dual-stack)", err.Error())
156+
continue
157+
}
132158

133-
for _, filterTableRules := range npc.filterTableRules {
134159
// set mark to indicate traffic from/to the pod passed network policies.
135160
// Mark will be checked to explicitly ACCEPT the traffic
136161
comment := "\"set mark to ACCEPT traffic that comply to network policies\""
@@ -151,13 +176,13 @@ func (npc *NetworkPolicyController) setupPodNetpolRules(pod podInfo, podFwChainN
151176
hasEgressPolicy := false
152177

153178
for ipFamily, filterTableRules := range npc.filterTableRules {
154-
var ip string
155-
switch ipFamily {
156-
case api.IPv4Protocol:
157-
ip, _ = getPodIPv4Address(pod)
158-
case api.IPv6Protocol:
159-
ip, _ = getPodIPv6Address(pod)
179+
ip, err := getPodIPForFamily(pod, ipFamily)
180+
if err != nil {
181+
klog.V(2).Infof("unable to get address for pod: %s -- skipping iptables policy for pod "+
182+
"(this is normal for pods that are not dual-stack)", err.Error())
183+
continue
160184
}
185+
161186
// add entries in pod firewall to run through applicable network policies
162187
for _, policy := range networkPoliciesInfo {
163188
if _, ok := policy.targetPods[pod.ip]; !ok {
@@ -187,7 +212,7 @@ func (npc *NetworkPolicyController) setupPodNetpolRules(pod podInfo, podFwChainN
187212
// if pod does not have any network policy which applies rules for pod's ingress traffic
188213
// then apply default network policy
189214
if !hasIngressPolicy {
190-
comment := "\"run through default ingress network policy chain\""
215+
comment := "\"run through default ingress network policy chain\""
191216
args := []string{"-I", podFwChainName, "1", "-d", ip, "-m", "comment", "--comment", comment,
192217
"-j", kubeDefaultNetpolChain, "\n"}
193218
filterTableRules.WriteString(strings.Join(args, " "))
@@ -196,7 +221,7 @@ func (npc *NetworkPolicyController) setupPodNetpolRules(pod podInfo, podFwChainN
196221
// if pod does not have any network policy which applies rules for pod's egress traffic
197222
// then apply default network policy
198223
if !hasEgressPolicy {
199-
comment := "\"run through default egress network policy chain\""
224+
comment := "\"run through default egress network policy chain\""
200225
args := []string{"-I", podFwChainName, "1", "-s", ip, "-m", "comment", "--comment", comment,
201226
"-j", kubeDefaultNetpolChain, "\n"}
202227
filterTableRules.WriteString(strings.Join(args, " "))
@@ -228,12 +253,11 @@ func (npc *NetworkPolicyController) setupPodNetpolRules(pod podInfo, podFwChainN
228253

229254
func (npc *NetworkPolicyController) interceptPodInboundTraffic(pod podInfo, podFwChainName string) {
230255
for ipFamily, filterTableRules := range npc.filterTableRules {
231-
var ip string
232-
switch ipFamily {
233-
case api.IPv4Protocol:
234-
ip, _ = getPodIPv4Address(pod)
235-
case api.IPv6Protocol:
236-
ip, _ = getPodIPv6Address(pod)
256+
ip, err := getPodIPForFamily(pod, ipFamily)
257+
if err != nil {
258+
klog.V(2).Infof("unable to get address for pod: %s -- skipping iptables inbound intercept "+
259+
"policy for pod (this is normal for pods that are not dual-stack)", err.Error())
260+
continue
237261
}
238262

239263
// ensure there is rule in filter table and FORWARD chain to jump to pod specific firewall chain
@@ -266,12 +290,11 @@ func (npc *NetworkPolicyController) interceptPodInboundTraffic(pod podInfo, podF
266290
// firewall chain corresponding to the pod so that egress network policies are enforced
267291
func (npc *NetworkPolicyController) interceptPodOutboundTraffic(pod podInfo, podFwChainName string) {
268292
for ipFamily, filterTableRules := range npc.filterTableRules {
269-
var ip string
270-
switch ipFamily {
271-
case api.IPv4Protocol:
272-
ip, _ = getPodIPv4Address(pod)
273-
case api.IPv6Protocol:
274-
ip, _ = getPodIPv6Address(pod)
293+
ip, err := getPodIPForFamily(pod, ipFamily)
294+
if err != nil {
295+
klog.V(2).Infof("unable to get address for pod: %s -- skipping iptables outbound intercept "+
296+
"policy for pod (this is normal for pods that are not dual-stack)", err.Error())
297+
continue
275298
}
276299

277300
for _, chain := range defaultChains {

pkg/controllers/netpol/utils.go

+24-2
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,8 @@ func getPodIPv6Address(pod podInfo) (string, error) {
131131
return ip.IP, nil
132132
}
133133
}
134-
return "", fmt.Errorf("pod %s has no IPv6Address", pod.name)
134+
return "", fmt.Errorf("pod %s:%s has no IPv6Address, available addresses: %s",
135+
pod.namespace, pod.name, pod.ips)
135136
}
136137

137138
func getPodIPv4Address(pod podInfo) (string, error) {
@@ -140,5 +141,26 @@ func getPodIPv4Address(pod podInfo) (string, error) {
140141
return ip.IP, nil
141142
}
142143
}
143-
return "", fmt.Errorf("pod %s has no IPv4Address", pod.name)
144+
return "", fmt.Errorf("pod %s:%s has no IPv4Address, available addresses: %s",
145+
pod.namespace, pod.name, pod.ips)
146+
}
147+
148+
func getPodIPForFamily(pod podInfo, ipFamily api.IPFamily) (string, error) {
149+
switch ipFamily {
150+
case api.IPv4Protocol:
151+
if ip, err := getPodIPv4Address(pod); err != nil {
152+
return "", err
153+
} else {
154+
return ip, nil
155+
}
156+
case api.IPv6Protocol:
157+
if ip, err := getPodIPv6Address(pod); err != nil {
158+
return "", err
159+
} else {
160+
return ip, nil
161+
}
162+
}
163+
164+
return "", fmt.Errorf("did not recognize IP Family for pod: %s:%s family: %s", pod.namespace, pod.name,
165+
ipFamily)
144166
}

0 commit comments

Comments
 (0)