diff --git a/docs/antrea-node-network-policy.md b/docs/antrea-node-network-policy.md index 8f79d0e6c39..47d5510c25e 100644 --- a/docs/antrea-node-network-policy.md +++ b/docs/antrea-node-network-policy.md @@ -6,6 +6,7 @@ - [Introduction](#introduction) - [Prerequisites](#prerequisites) - [Usage](#usage) +- [Logs](#logs) - [Limitations](#limitations) @@ -66,11 +67,15 @@ spec: ports: - protocol: TCP port: 80 + enableLogging: true + logLabel: allow-http - name: drop-other action: Drop ports: - protocol: TCP port: 80 + enableLogging: true + logLabel: default-drop-others ``` An example Node NetworkPolicy that blocks egress traffic from Nodes with label @@ -105,6 +110,58 @@ spec: port: 22 ``` +## Logs + +The `enableLogging` and `logLabel` options provide limited support for Node NetworkPolicies. Since Node NetworkPolicies +are implemented using iptables, enabling `enableLogging` causes the Linux kernel to log information about all matching +packets via the kernel log. However, Antrea cannot process these logs directly. Instead, these logs can be accessed +through syslog, allowing you to filter and direct them to specific files using syslog syntax. + +By default, `enableLogging` is unsupported in KinD clusters. To enable it, set the host’s +`/proc/sys/net/netfilter/nf_log_all_netns` to 1. Antrea uses the iptables `LOG` target to log packet information, +but by default, `/proc/sys/net/netfilter/nf_log_all_netns` is 0, preventing containers from logging to the kernel to +avoid clutter. If logging is required, you can enable it by setting the value to 1, but please be cautious to do so +unless you are clear about the impact. + +For example, consider the Node NetworkPolicy `restrict-http-to-node` above. It could generate the following logs: + +```text +Sep 2 10:31:07 k8s-node-control-plane kernel: [6657320.789675] Antrea:I:Allow:allow-http:IN=ens224 OUT= MAC=00:50:56:a7:fb:18:00:50:56:a7:23:47:08:00 SRC=10.10.0.10 DST=192.168.240.200 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=52813 DF PROTO=TCP SPT=57658 DPT=80 WINDOW=64240 RES=0x00 SYN URGP=0 +Sep 2 10:31:11 k8s-node-control-plane kernel: [6657324.899219] Antrea:I:Drop:default-drop:IN=ens224 OUT= MAC=00:50:56:a7:fb:18:00:50:56:a7:23:47:08:00 SRC=192.168.240.201 DST=192.168.240.200 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=27486 DF PROTO=TCP SPT=33152 DPT=80 WINDOW=64240 RES=0x00 SYN URGP=0 +``` + +In these logs, prefixes like `Antrea:I:Allow:allow-http:` and `Antrea:I:Drop:default-drop:` are added by iptables using +the `--log-prefix` parameter. The iptables log prefix is limited to 29 characters, as described in the +[iptables-extensions manual](https://ipset.netfilter.org/iptables-extensions.man.html). + +The log prefix format includes essential information of a Node NetworkPolicy rule, and consists of four parts, +formatted as follows: + +```text +|---1--| |2| |---3--| |----------4--------| +|Antrea|:|I|:|Reject|:|user-provided label|:| +|6 |1|1|1|4-6 |1|1-12 |1| +``` + +- Part 1: Fixed, "Antrea" +- Part 2: Direction, "I" (In) or "O" (Out) +- Part 3: Action, "Allow", "Drop", or "Reject" +- Part 4: User-provided `logLabel`, up to 12 characters + +Due to iptables' 29-character prefix limitation, the user-provided `logLabel` is restricted to a maximum of 12 characters. +To manage these logs effectively, you can configure rsyslog on each Node as follows: + +```text +# Example rsyslog configuration to filter Antrea logs +:msg, contains, "Antrea:I:Allow:allow-http" /var/log/antrea-node-netpol-allow.log +:msg, contains, "Antrea:I:Drop:default-drop" /var/log/antrea-node-netpol-drop.log +& stop +``` + +This configuration directs logs with the prefix `Antrea:I:Allow:allow-http` to `/var/log/antrea-node-netpol-allow.log` +and logs with the prefix `Antrea:I:Drop:default-drop` to `/var/log/antrea-node-netpol-drop.log`. The `& stop` command +ensures that these logs are not processed further. + ## Limitations - This feature is currently only supported for Linux Nodes. @@ -118,4 +175,4 @@ spec: - FQDN is not supported for ACNPs applied to Nodes. - Layer 7 NetworkPolicy is not supported yet. - For UDP or SCTP, when the `Reject` action is specified in an egress rule, it behaves identical to the `Drop` action. -- Traffic logging is not supported yet for ACNPs applied to Nodes. +- Limited support for traffic logging for ACNPs applied to Nodes. diff --git a/pkg/agent/controller/networkpolicy/node_reconciler_linux.go b/pkg/agent/controller/networkpolicy/node_reconciler_linux.go index db5db2c1f39..c57f1b4ecdb 100644 --- a/pkg/agent/controller/networkpolicy/node_reconciler_linux.go +++ b/pkg/agent/controller/networkpolicy/node_reconciler_linux.go @@ -43,6 +43,10 @@ const ( ipv6Any = "::/0" ) +// The logging of Node NetworkPolicy is implemented by iptables target LOG, which turns on kernel logging of matching +// packets. The label is useful for distinguishing Node NetworkPolicy logs from other kernel logs. +const logLabelPrefix = "Antrea" + var ipsetTypeHashIP = ipset.HashIP /* @@ -124,7 +128,7 @@ directly. type coreIPTRule struct { ruleID string priority *types.Priority - ruleStr string + ruleStrs []string } type chainKey struct { @@ -256,7 +260,7 @@ func (r *nodeReconciler) batchAdd(rules []*CompletedRule) error { } // Collect all core iptables rules. - coreIPTRule := &coreIPTRule{ruleID, iptRule.Priority, iptRule.CoreIPTRule} + coreIPTRule := &coreIPTRule{ruleID, iptRule.Priority, iptRule.CoreIPTRules} if rule.Direction == v1beta2.DirectionIn { ingressCoreIPTRules[ipProtocol] = append(ingressCoreIPTRules[ipProtocol], coreIPTRule) } else { @@ -322,6 +326,11 @@ func (r *nodeReconciler) GetRuleByFlowID(ruleFlowID uint32) (*types.PolicyRule, func (r *nodeReconciler) computeIPTRules(rule *CompletedRule) (map[iptables.Protocol]*types.NodePolicyRule, *nodePolicyLastRealized) { ruleID := rule.ID + enableLogging := rule.EnableLogging + var logLabel string + if enableLogging { + logLabel = generateLogLabel(rule) + } lastRealized := newNodePolicyLastRealized() priority := &types.Priority{ TierPriority: *rule.TierPriority, @@ -362,7 +371,12 @@ func (r *nodeReconciler) computeIPTRules(rule *CompletedRule) (map[iptables.Prot var serviceIPTRules []string if serviceIPTChain != "" { - serviceIPTRules = buildServiceIPTRules(ipProtocol, rule.Services, serviceIPTChain, serviceIPTRuleTarget) + serviceIPTRules = buildServiceIPTRules(ipProtocol, + rule.Services, + serviceIPTChain, + serviceIPTRuleTarget, + enableLogging, + logLabel) } ipnets := getIPNetsFromRule(rule, isIPv6) @@ -383,14 +397,19 @@ func (r *nodeReconciler) computeIPTRules(rule *CompletedRule) (map[iptables.Prot lastRealized.ipnets[ipProtocol] = ipnet } - coreIPTRule := buildCoreIPTRule(ipProtocol, + coreIPTRules := buildCoreIPTRules(ipProtocol, coreIPTChain, ipset, ipnet, coreIPTRuleTarget, coreIPTRuleComment, service, - rule.Direction == v1beta2.DirectionIn) + rule.Direction == v1beta2.DirectionIn, + // If the target of a core iptables rule is not a service chain, the iptables rule for logging should be + // generated along with the core iptables rule. Otherwise, the iptables rules for logging should be generated + // along with the service iptables rules. + enableLogging && serviceIPTChain == "", + logLabel) nodePolicyRules[ipProtocol] = &types.NodePolicyRule{ IPSet: ipset, @@ -399,7 +418,7 @@ func (r *nodeReconciler) computeIPTRules(rule *CompletedRule) (map[iptables.Prot ServiceIPTChain: serviceIPTChain, ServiceIPTRules: serviceIPTRules, CoreIPTChain: coreIPTChain, - CoreIPTRule: coreIPTRule, + CoreIPTRules: coreIPTRules, IsIPv6: isIPv6, } } @@ -422,7 +441,7 @@ func (r *nodeReconciler) add(rule *CompletedRule) error { return err } } - if err := r.addOrUpdateCoreIPTRules(iptRule.CoreIPTChain, iptRule.IsIPv6, false, &coreIPTRule{ruleID, iptRule.Priority, iptRule.CoreIPTRule}); err != nil { + if err := r.addOrUpdateCoreIPTRules(iptRule.CoreIPTChain, iptRule.IsIPv6, false, &coreIPTRule{ruleID, iptRule.Priority, iptRule.CoreIPTRules}); err != nil { return err } } @@ -456,7 +475,7 @@ func (r *nodeReconciler) update(lastRealized *nodePolicyLastRealized, newRule *C return err } if shouldUpdateCoreIPTRules { - if err := r.addOrUpdateCoreIPTRules(iptRule.CoreIPTChain, iptRule.IsIPv6, true, &coreIPTRule{ruleID, iptRule.Priority, iptRule.CoreIPTRule}); err != nil { + if err := r.addOrUpdateCoreIPTRules(iptRule.CoreIPTChain, iptRule.IsIPv6, true, &coreIPTRule{ruleID, iptRule.Priority, iptRule.CoreIPTRules}); err != nil { return err } } @@ -464,7 +483,7 @@ func (r *nodeReconciler) update(lastRealized *nodePolicyLastRealized, newRule *C // If the previous rule used an ipset, sync the new core iptables rule first to remove its reference, then // delete the unused ipset. if shouldUpdateCoreIPTRules { - if err := r.addOrUpdateCoreIPTRules(iptRule.CoreIPTChain, iptRule.IsIPv6, true, &coreIPTRule{ruleID, iptRule.Priority, iptRule.CoreIPTRule}); err != nil { + if err := r.addOrUpdateCoreIPTRules(iptRule.CoreIPTChain, iptRule.IsIPv6, true, &coreIPTRule{ruleID, iptRule.Priority, iptRule.CoreIPTRules}); err != nil { return err } } @@ -473,7 +492,7 @@ func (r *nodeReconciler) update(lastRealized *nodePolicyLastRealized, newRule *C } } else { if shouldUpdateCoreIPTRules { - if err := r.addOrUpdateCoreIPTRules(iptRule.CoreIPTChain, iptRule.IsIPv6, true, &coreIPTRule{ruleID, iptRule.Priority, iptRule.CoreIPTRule}); err != nil { + if err := r.addOrUpdateCoreIPTRules(iptRule.CoreIPTChain, iptRule.IsIPv6, true, &coreIPTRule{ruleID, iptRule.Priority, iptRule.CoreIPTRules}); err != nil { return err } } @@ -517,9 +536,7 @@ func (r *nodeReconciler) addOrUpdateCoreIPTRules(chain string, isIPv6 bool, isUp // Get all iptables rules and synchronize them. var ruleStrs []string for _, rule := range rules { - if rule.ruleStr != "" { - ruleStrs = append(ruleStrs, rule.ruleStr) - } + ruleStrs = append(ruleStrs, rule.ruleStrs...) } if err := r.routeClient.AddOrUpdateNodeNetworkPolicyIPTables([]string{chain}, [][]string{ruleStrs}, isIPv6); err != nil { return err @@ -554,7 +571,7 @@ func (r *nodeReconciler) deleteCoreIPTRule(ruleID string, iptChain string, isIPv // Get all the iptables rules and synchronize them. var ruleStrs []string for _, r := range rules { - ruleStrs = append(ruleStrs, r.ruleStr) + ruleStrs = append(ruleStrs, r.ruleStrs...) } if err := r.routeClient.AddOrUpdateNodeNetworkPolicyIPTables([]string{iptChain}, [][]string{ruleStrs}, isIPv6); err != nil { return err @@ -636,23 +653,26 @@ func getIPNetsFromRule(rule *CompletedRule, isIPv6 bool) sets.Set[string] { return set } -func buildCoreIPTRule(ipProtocol iptables.Protocol, +func buildCoreIPTRules(ipProtocol iptables.Protocol, iptChain string, ipset string, ipnet string, iptRuleTarget string, iptRuleComment string, service *v1beta2.Service, - isIngress bool) string { + isIngress bool, + enableLogging bool, + logLabel string) []string { builder := iptables.NewRuleBuilder(iptChain) + var rules []string if isIngress { if ipset != "" { builder = builder.MatchIPSetSrc(ipset, ipsetTypeHashIP) } else if ipnet != "" { builder = builder.MatchCIDRSrc(ipnet) } else { - // If no source IP address is matched, return an empty string since the core iptables will never be matched. - return "" + // If no source IP address is matched, return an empty slice since the core iptables will never be matched. + return rules } } else { if ipset != "" { @@ -660,8 +680,8 @@ func buildCoreIPTRule(ipProtocol iptables.Protocol, } else if ipnet != "" { builder = builder.MatchCIDRDst(ipnet) } else { - // If no destination IP address is matched, return an empty string since the core iptables will never be matched. - return "" + // If no destination IP address is matched, return an empty slice since the core iptables will never be matched. + return rules } } if service != nil { @@ -679,13 +699,26 @@ func buildCoreIPTRule(ipProtocol iptables.Protocol, builder = builder.MatchICMP(service.ICMPType, service.ICMPCode, ipProtocol) } } - return builder.SetTarget(iptRuleTarget). + if enableLogging { + rules = append(rules, builder.CopyBuilder(). + SetTarget(iptables.LOGTarget). + SetLogPrefix(logLabel). + Done(). + GetRule()) + } + rules = append(rules, builder.SetTarget(iptRuleTarget). SetComment(iptRuleComment). Done(). - GetRule() + GetRule()) + return rules } -func buildServiceIPTRules(ipProtocol iptables.Protocol, services []v1beta2.Service, chain string, ruleTarget string) []string { +func buildServiceIPTRules(ipProtocol iptables.Protocol, + services []v1beta2.Service, + chain string, + ruleTarget string, + enableLogging bool, + logLabel string) []string { var rules []string builder := iptables.NewRuleBuilder(chain) for _, svc := range services { @@ -703,6 +736,13 @@ func buildServiceIPTRules(ipProtocol iptables.Protocol, services []v1beta2.Servi case "icmp": copiedBuilder = copiedBuilder.MatchICMP(svc.ICMPType, svc.ICMPCode, ipProtocol) } + if enableLogging { + rules = append(rules, copiedBuilder.CopyBuilder(). + SetTarget(iptables.LOGTarget). + SetLogPrefix(logLabel). + Done(). + GetRule()) + } rules = append(rules, copiedBuilder.SetTarget(ruleTarget). Done(). GetRule()) @@ -729,3 +769,24 @@ func getServiceTransProtocol(protocol *v1beta2.Protocol) string { } return strings.ToLower(string(*protocol)) } + +func generateLogLabel(rule *CompletedRule) string { + // Construct the log label used as iptables log prefix. According to https://ipset.netfilter.org/iptables-extensions.man.html, + // the log prefix is up to 29 letters long. The log label should include essential information to help filter the + // generated iptables kernel log. As a result, the user-provided log label is limited to 12 characters. + // The log label format: + // |Antrea|:|I|:|Reject|:|user-provided label|:| + // |6 |1|1|1|4-6 |1|1-12 |1| + logLabel := fmt.Sprintf("%s:%s:%s", logLabelPrefix, rule.Direction[:1], *rule.Action) + if rule.LogLabel != "" { + ruleLogLabel := rule.LogLabel + // Truncate the user-provided log label if it exceeds 12 characters. + if len(ruleLogLabel) > 12 { + klog.InfoS("The rule log label that exceeds 12 characters will be truncated", "rule.LogLabel", rule.LogLabel) + ruleLogLabel = ruleLogLabel[:12] + } + logLabel = fmt.Sprintf("%s:%s", logLabel, ruleLogLabel) + } + logLabel += ":" + return logLabel +} diff --git a/pkg/agent/controller/networkpolicy/node_reconciler_linux_test.go b/pkg/agent/controller/networkpolicy/node_reconciler_linux_test.go index 0e39c38ec9e..c4d59404b0a 100644 --- a/pkg/agent/controller/networkpolicy/node_reconciler_linux_test.go +++ b/pkg/agent/controller/networkpolicy/node_reconciler_linux_test.go @@ -86,6 +86,8 @@ var ( PolicyPriority: &policyPriority1, TierPriority: &tierPriority1, SourceRef: &cnp1, + EnableLogging: true, + LogLabel: "", }, FromAddresses: dualAddressGroup1, ToAddresses: nil, @@ -102,6 +104,8 @@ var ( PolicyPriority: &policyPriority1, TierPriority: &tierPriority2, SourceRef: &cnp1, + EnableLogging: false, + LogLabel: "ingress2", }, FromAddresses: dualAddressGroup1, ToAddresses: nil, @@ -119,6 +123,8 @@ var ( PolicyPriority: &policyPriority1, TierPriority: &tierPriority2, SourceRef: &cnp1, + EnableLogging: true, + LogLabel: "ingress3", }, FromAddresses: nil, ToAddresses: nil, @@ -136,6 +142,8 @@ var ( PolicyPriority: &policyPriority1, TierPriority: &tierPriority2, SourceRef: &cnp1, + EnableLogging: true, + LogLabel: "ingress3", }, FromAddresses: addressGroup1, ToAddresses: nil, @@ -152,6 +160,8 @@ var ( PolicyPriority: &policyPriority1, TierPriority: &tierPriority2, SourceRef: &cnp1, + EnableLogging: true, + LogLabel: "ingress3", }, FromAddresses: addressGroup2, ToAddresses: nil, @@ -168,6 +178,8 @@ var ( PolicyPriority: &policyPriority1, TierPriority: &tierPriority2, SourceRef: &cnp1, + EnableLogging: true, + LogLabel: "ingress3", }, FromAddresses: addressGroup2.Union(addressGroup1), ToAddresses: nil, @@ -184,6 +196,8 @@ var ( PolicyPriority: &policyPriority1, TierPriority: &tierPriority2, SourceRef: &cnp1, + EnableLogging: true, + LogLabel: "ingress3", }, FromAddresses: addressGroup2.Union(v1beta2.NewGroupMemberSet(newAddressGroupMember("1.1.1.3"))), ToAddresses: nil, @@ -200,6 +214,8 @@ var ( PolicyPriority: &policyPriority1, TierPriority: &tierPriority2, SourceRef: &cnp1, + EnableLogging: true, + LogLabel: "ingress3", }, FromAddresses: nil, ToAddresses: nil, @@ -216,6 +232,8 @@ var ( PolicyPriority: &policyPriority1, TierPriority: &tierPriority1, SourceRef: &cnp1, + EnableLogging: true, + LogLabel: "egress1", }, ToAddresses: dualAddressGroup1, FromAddresses: nil, @@ -232,6 +250,8 @@ var ( PolicyPriority: &policyPriority1, TierPriority: &tierPriority2, SourceRef: &cnp1, + EnableLogging: true, + LogLabel: "egress2:test_log_label", }, ToAddresses: dualAddressGroup1, FromAddresses: nil, @@ -252,14 +272,16 @@ func TestNodeReconcilerReconcileAndForget(t *testing.T) { expectedCalls func(mockRouteClient *routetest.MockInterfaceMockRecorder) }{ { - name: "IPv4, add an ingress rule, then forget it", + name: "IPv4, add an ingress rule, update it, then forget it", ipv4Enabled: true, ipv6Enabled: false, expectedCalls: func(mockRouteClient *routetest.MockInterfaceMockRecorder) { serviceRules := [][]string{ { - "-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j ACCEPT", - "-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j ACCEPT", + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j LOG --log-prefix "Antrea:I:Allow:"`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j ACCEPT`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j LOG --log-prefix "Antrea:I:Allow:"`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j ACCEPT`, }, } coreRules := [][]string{ @@ -290,8 +312,10 @@ func TestNodeReconcilerReconcileAndForget(t *testing.T) { expectedCalls: func(mockRouteClient *routetest.MockInterfaceMockRecorder) { serviceRules := [][]string{ { - "-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 80 -j ACCEPT", - "-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 443 -j ACCEPT", + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 80 -j LOG --log-prefix "Antrea:O:Allow:egress1:"`, + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 80 -j ACCEPT`, + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 443 -j LOG --log-prefix "Antrea:O:Allow:egress1:"`, + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 443 -j ACCEPT`, }, } coreRules := [][]string{ @@ -320,8 +344,10 @@ func TestNodeReconcilerReconcileAndForget(t *testing.T) { expectedCalls: func(mockRouteClient *routetest.MockInterfaceMockRecorder) { serviceRules := [][]string{ { - "-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j ACCEPT", - "-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j ACCEPT", + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j LOG --log-prefix "Antrea:I:Allow:"`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j ACCEPT`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j LOG --log-prefix "Antrea:I:Allow:"`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j ACCEPT`, }, } coreRulesIPv4 := [][]string{ @@ -365,8 +391,10 @@ func TestNodeReconcilerReconcileAndForget(t *testing.T) { expectedCalls: func(mockRouteClient *routetest.MockInterfaceMockRecorder) { serviceRules1 := [][]string{ { - "-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j ACCEPT", - "-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j ACCEPT", + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j LOG --log-prefix "Antrea:I:Allow:"`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j ACCEPT`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j LOG --log-prefix "Antrea:I:Allow:"`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j ACCEPT`, }, } coreRules1 := [][]string{ @@ -384,6 +412,7 @@ func TestNodeReconcilerReconcileAndForget(t *testing.T) { { `-A ANTREA-POL-INGRESS-RULES -m set --match-set ANTREA-POL-INGRESSRULE1-4 src -j ANTREA-POL-INGRESSRULE1 -m comment --comment "Antrea: for rule ingress-rule-01, policy AntreaClusterNetworkPolicy:name1"`, `-A ANTREA-POL-INGRESS-RULES -s 1.1.1.1/32 -p tcp --dport 443 -j ACCEPT -m comment --comment "Antrea: for rule ingress-rule-02, policy AntreaClusterNetworkPolicy:name1"`, + `-A ANTREA-POL-INGRESS-RULES -p tcp --dport 8080 -j LOG --log-prefix "Antrea:I:Allow:ingress3:"`, `-A ANTREA-POL-INGRESS-RULES -p tcp --dport 8080 -j ACCEPT -m comment --comment "Antrea: for rule ingress-rule-03, policy AntreaClusterNetworkPolicy:name1"`, }, } @@ -426,25 +455,30 @@ func TestNodeReconcilerReconcileAndForget(t *testing.T) { expectedCalls: func(mockRouteClient *routetest.MockInterfaceMockRecorder) { coreRules3 := [][]string{ { + `-A ANTREA-POL-INGRESS-RULES -p tcp --dport 8080 -j LOG --log-prefix "Antrea:I:Allow:ingress3:"`, `-A ANTREA-POL-INGRESS-RULES -p tcp --dport 8080 -j ACCEPT -m comment --comment "Antrea: for rule ingress-rule-03, policy AntreaClusterNetworkPolicy:name1"`, }, } coreRules2 := [][]string{ { `-A ANTREA-POL-INGRESS-RULES -s 1.1.1.1/32 -p tcp --dport 443 -j ACCEPT -m comment --comment "Antrea: for rule ingress-rule-02, policy AntreaClusterNetworkPolicy:name1"`, + `-A ANTREA-POL-INGRESS-RULES -p tcp --dport 8080 -j LOG --log-prefix "Antrea:I:Allow:ingress3:"`, `-A ANTREA-POL-INGRESS-RULES -p tcp --dport 8080 -j ACCEPT -m comment --comment "Antrea: for rule ingress-rule-03, policy AntreaClusterNetworkPolicy:name1"`, }, } serviceRules1 := [][]string{ { - "-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j ACCEPT", - "-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j ACCEPT", + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j LOG --log-prefix "Antrea:I:Allow:"`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j ACCEPT`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j LOG --log-prefix "Antrea:I:Allow:"`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j ACCEPT`, }, } coreRules1 := [][]string{ { `-A ANTREA-POL-INGRESS-RULES -m set --match-set ANTREA-POL-INGRESSRULE1-4 src -j ANTREA-POL-INGRESSRULE1 -m comment --comment "Antrea: for rule ingress-rule-01, policy AntreaClusterNetworkPolicy:name1"`, `-A ANTREA-POL-INGRESS-RULES -s 1.1.1.1/32 -p tcp --dport 443 -j ACCEPT -m comment --comment "Antrea: for rule ingress-rule-02, policy AntreaClusterNetworkPolicy:name1"`, + `-A ANTREA-POL-INGRESS-RULES -p tcp --dport 8080 -j LOG --log-prefix "Antrea:I:Allow:ingress3:"`, `-A ANTREA-POL-INGRESS-RULES -p tcp --dport 8080 -j ACCEPT -m comment --comment "Antrea: for rule ingress-rule-03, policy AntreaClusterNetworkPolicy:name1"`, }, } @@ -493,8 +527,10 @@ func TestNodeReconcilerReconcileAndForget(t *testing.T) { } serviceRules1 := [][]string{ { - "-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j ACCEPT", - "-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j ACCEPT", + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j LOG --log-prefix "Antrea:I:Allow:"`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j ACCEPT`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j LOG --log-prefix "Antrea:I:Allow:"`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j ACCEPT`, }, } coreRules1 := [][]string{ @@ -507,17 +543,20 @@ func TestNodeReconcilerReconcileAndForget(t *testing.T) { { `-A ANTREA-POL-INGRESS-RULES -m set --match-set ANTREA-POL-INGRESSRULE1-4 src -j ANTREA-POL-INGRESSRULE1 -m comment --comment "Antrea: for rule ingress-rule-01, policy AntreaClusterNetworkPolicy:name1"`, `-A ANTREA-POL-INGRESS-RULES -s 1.1.1.1/32 -p tcp --dport 443 -j ACCEPT -m comment --comment "Antrea: for rule ingress-rule-02, policy AntreaClusterNetworkPolicy:name1"`, + `-A ANTREA-POL-INGRESS-RULES -p tcp --dport 8080 -j LOG --log-prefix "Antrea:I:Allow:ingress3:"`, `-A ANTREA-POL-INGRESS-RULES -p tcp --dport 8080 -j ACCEPT -m comment --comment "Antrea: for rule ingress-rule-03, policy AntreaClusterNetworkPolicy:name1"`, }, } coreRulesDelete2 := [][]string{ { `-A ANTREA-POL-INGRESS-RULES -m set --match-set ANTREA-POL-INGRESSRULE1-4 src -j ANTREA-POL-INGRESSRULE1 -m comment --comment "Antrea: for rule ingress-rule-01, policy AntreaClusterNetworkPolicy:name1"`, + `-A ANTREA-POL-INGRESS-RULES -p tcp --dport 8080 -j LOG --log-prefix "Antrea:I:Allow:ingress3:"`, `-A ANTREA-POL-INGRESS-RULES -p tcp --dport 8080 -j ACCEPT -m comment --comment "Antrea: for rule ingress-rule-03, policy AntreaClusterNetworkPolicy:name1"`, }, } coreRulesDelete1 := [][]string{ { + `-A ANTREA-POL-INGRESS-RULES -p tcp --dport 8080 -j LOG --log-prefix "Antrea:I:Allow:ingress3:"`, `-A ANTREA-POL-INGRESS-RULES -p tcp --dport 8080 -j ACCEPT -m comment --comment "Antrea: for rule ingress-rule-03, policy AntreaClusterNetworkPolicy:name1"`, }, } @@ -550,21 +589,25 @@ func TestNodeReconcilerReconcileAndForget(t *testing.T) { expectedCalls: func(mockRouteClient *routetest.MockInterfaceMockRecorder) { coreRules1 := [][]string{ { + `-A ANTREA-POL-INGRESS-RULES -p tcp --dport 8080 -j LOG --log-prefix "Antrea:I:Allow:ingress3:"`, `-A ANTREA-POL-INGRESS-RULES -p tcp --dport 8080 -j ACCEPT -m comment --comment "Antrea: for rule ingress-rule-03, policy AntreaClusterNetworkPolicy:name1"`, }, } coreRules2 := [][]string{ { + `-A ANTREA-POL-INGRESS-RULES -s 1.1.1.1/32 -p tcp --dport 8080 -j LOG --log-prefix "Antrea:I:Allow:ingress3:"`, `-A ANTREA-POL-INGRESS-RULES -s 1.1.1.1/32 -p tcp --dport 8080 -j ACCEPT -m comment --comment "Antrea: for rule ingress-rule-03, policy AntreaClusterNetworkPolicy:name1"`, }, } coreRules3 := [][]string{ { + `-A ANTREA-POL-INGRESS-RULES -s 1.1.1.2/32 -p tcp --dport 8080 -j LOG --log-prefix "Antrea:I:Allow:ingress3:"`, `-A ANTREA-POL-INGRESS-RULES -s 1.1.1.2/32 -p tcp --dport 8080 -j ACCEPT -m comment --comment "Antrea: for rule ingress-rule-03, policy AntreaClusterNetworkPolicy:name1"`, }, } coreRules4 := [][]string{ { + `-A ANTREA-POL-INGRESS-RULES -m set --match-set ANTREA-POL-INGRESSRULE3-4 src -p tcp --dport 8080 -j LOG --log-prefix "Antrea:I:Allow:ingress3:"`, `-A ANTREA-POL-INGRESS-RULES -m set --match-set ANTREA-POL-INGRESSRULE3-4 src -p tcp --dport 8080 -j ACCEPT -m comment --comment "Antrea: for rule ingress-rule-03, policy AntreaClusterNetworkPolicy:name1"`, }, } @@ -670,8 +713,10 @@ func TestNodeReconcilerBatchReconcileAndForget(t *testing.T) { } svcRules := [][]string{ { - "-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j ACCEPT", - "-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j ACCEPT", + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j LOG --log-prefix "Antrea:I:Allow:"`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j ACCEPT`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j LOG --log-prefix "Antrea:I:Allow:"`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j ACCEPT`, }, } updatedCoreRules := [][]string{ @@ -714,8 +759,10 @@ func TestNodeReconcilerBatchReconcileAndForget(t *testing.T) { } svcRules := [][]string{ { - "-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j ACCEPT", - "-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j ACCEPT", + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j LOG --log-prefix "Antrea:I:Allow:"`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j ACCEPT`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j LOG --log-prefix "Antrea:I:Allow:"`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j ACCEPT`, }, } updatedCoreRules := [][]string{ @@ -763,8 +810,10 @@ func TestNodeReconcilerBatchReconcileAndForget(t *testing.T) { } ipv4SvcRules := [][]string{ { - "-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j ACCEPT", - "-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j ACCEPT", + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j LOG --log-prefix "Antrea:I:Allow:"`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j ACCEPT`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j LOG --log-prefix "Antrea:I:Allow:"`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j ACCEPT`, }, } ipv6SvcRules := ipv4SvcRules @@ -814,6 +863,7 @@ func TestNodeReconcilerBatchReconcileAndForget(t *testing.T) { coreRules := [][]string{ { `-A ANTREA-POL-EGRESS-RULES -d 1.1.1.1/32 -j ANTREA-POL-EGRESSRULE1 -m comment --comment "Antrea: for rule egress-rule-01, policy AntreaClusterNetworkPolicy:name1"`, + `-A ANTREA-POL-EGRESS-RULES -d 1.1.1.1/32 -p tcp --dport 443 -j LOG --log-prefix "Antrea:O:Allow:egress2:test:"`, `-A ANTREA-POL-EGRESS-RULES -d 1.1.1.1/32 -p tcp --dport 443 -j ACCEPT -m comment --comment "Antrea: for rule egress-rule-02, policy AntreaClusterNetworkPolicy:name1"`, }, } @@ -822,12 +872,15 @@ func TestNodeReconcilerBatchReconcileAndForget(t *testing.T) { } svcRules := [][]string{ { - "-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 80 -j ACCEPT", - "-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 443 -j ACCEPT", + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 80 -j LOG --log-prefix "Antrea:O:Allow:egress1:"`, + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 80 -j ACCEPT`, + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 443 -j LOG --log-prefix "Antrea:O:Allow:egress1:"`, + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 443 -j ACCEPT`, }, } updatedCoreRules := [][]string{ { + `-A ANTREA-POL-EGRESS-RULES -d 1.1.1.1/32 -p tcp --dport 443 -j LOG --log-prefix "Antrea:O:Allow:egress2:test:"`, `-A ANTREA-POL-EGRESS-RULES -d 1.1.1.1/32 -p tcp --dport 443 -j ACCEPT -m comment --comment "Antrea: for rule egress-rule-02, policy AntreaClusterNetworkPolicy:name1"`, }, } @@ -856,6 +909,7 @@ func TestNodeReconcilerBatchReconcileAndForget(t *testing.T) { coreRules := [][]string{ { `-A ANTREA-POL-EGRESS-RULES -d 2002:1a23:fb44::1/128 -j ANTREA-POL-EGRESSRULE1 -m comment --comment "Antrea: for rule egress-rule-01, policy AntreaClusterNetworkPolicy:name1"`, + `-A ANTREA-POL-EGRESS-RULES -d 2002:1a23:fb44::1/128 -p tcp --dport 443 -j LOG --log-prefix "Antrea:O:Allow:egress2:test:"`, `-A ANTREA-POL-EGRESS-RULES -d 2002:1a23:fb44::1/128 -p tcp --dport 443 -j ACCEPT -m comment --comment "Antrea: for rule egress-rule-02, policy AntreaClusterNetworkPolicy:name1"`, }, } @@ -864,12 +918,15 @@ func TestNodeReconcilerBatchReconcileAndForget(t *testing.T) { } svcRules := [][]string{ { - "-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 80 -j ACCEPT", - "-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 443 -j ACCEPT", + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 80 -j LOG --log-prefix "Antrea:O:Allow:egress1:"`, + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 80 -j ACCEPT`, + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 443 -j LOG --log-prefix "Antrea:O:Allow:egress1:"`, + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 443 -j ACCEPT`, }, } updatedCoreRules := [][]string{ { + `-A ANTREA-POL-EGRESS-RULES -d 2002:1a23:fb44::1/128 -p tcp --dport 443 -j LOG --log-prefix "Antrea:O:Allow:egress2:test:"`, `-A ANTREA-POL-EGRESS-RULES -d 2002:1a23:fb44::1/128 -p tcp --dport 443 -j ACCEPT -m comment --comment "Antrea: for rule egress-rule-02, policy AntreaClusterNetworkPolicy:name1"`, }, } @@ -899,12 +956,14 @@ func TestNodeReconcilerBatchReconcileAndForget(t *testing.T) { ipv4CoreRules := [][]string{ { `-A ANTREA-POL-EGRESS-RULES -d 1.1.1.1/32 -j ANTREA-POL-EGRESSRULE1 -m comment --comment "Antrea: for rule egress-rule-01, policy AntreaClusterNetworkPolicy:name1"`, + `-A ANTREA-POL-EGRESS-RULES -d 1.1.1.1/32 -p tcp --dport 443 -j LOG --log-prefix "Antrea:O:Allow:egress2:test:"`, `-A ANTREA-POL-EGRESS-RULES -d 1.1.1.1/32 -p tcp --dport 443 -j ACCEPT -m comment --comment "Antrea: for rule egress-rule-02, policy AntreaClusterNetworkPolicy:name1"`, }, } ipv6CoreRules := [][]string{ { `-A ANTREA-POL-EGRESS-RULES -d 2002:1a23:fb44::1/128 -j ANTREA-POL-EGRESSRULE1 -m comment --comment "Antrea: for rule egress-rule-01, policy AntreaClusterNetworkPolicy:name1"`, + `-A ANTREA-POL-EGRESS-RULES -d 2002:1a23:fb44::1/128 -p tcp --dport 443 -j LOG --log-prefix "Antrea:O:Allow:egress2:test:"`, `-A ANTREA-POL-EGRESS-RULES -d 2002:1a23:fb44::1/128 -p tcp --dport 443 -j ACCEPT -m comment --comment "Antrea: for rule egress-rule-02, policy AntreaClusterNetworkPolicy:name1"`, }, } @@ -913,18 +972,22 @@ func TestNodeReconcilerBatchReconcileAndForget(t *testing.T) { } ipv4SvcRules := [][]string{ { - "-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 80 -j ACCEPT", - "-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 443 -j ACCEPT", + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 80 -j LOG --log-prefix "Antrea:O:Allow:egress1:"`, + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 80 -j ACCEPT`, + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 443 -j LOG --log-prefix "Antrea:O:Allow:egress1:"`, + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 443 -j ACCEPT`, }, } ipv6SvcRules := ipv4SvcRules updatedIPv4CoreRules := [][]string{ { + `-A ANTREA-POL-EGRESS-RULES -d 1.1.1.1/32 -p tcp --dport 443 -j LOG --log-prefix "Antrea:O:Allow:egress2:test:"`, `-A ANTREA-POL-EGRESS-RULES -d 1.1.1.1/32 -p tcp --dport 443 -j ACCEPT -m comment --comment "Antrea: for rule egress-rule-02, policy AntreaClusterNetworkPolicy:name1"`, }, } updatedIPv6CoreRules := [][]string{ { + `-A ANTREA-POL-EGRESS-RULES -d 2002:1a23:fb44::1/128 -p tcp --dport 443 -j LOG --log-prefix "Antrea:O:Allow:egress2:test:"`, `-A ANTREA-POL-EGRESS-RULES -d 2002:1a23:fb44::1/128 -p tcp --dport 443 -j ACCEPT -m comment --comment "Antrea: for rule egress-rule-02, policy AntreaClusterNetworkPolicy:name1"`, }, } @@ -961,12 +1024,16 @@ func TestNodeReconcilerBatchReconcileAndForget(t *testing.T) { } svcRules := [][]string{ { - "-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j ACCEPT", - "-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j ACCEPT", + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j LOG --log-prefix "Antrea:I:Allow:"`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j ACCEPT`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j LOG --log-prefix "Antrea:I:Allow:"`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j ACCEPT`, }, { - "-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 80 -j ACCEPT", - "-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 443 -j ACCEPT", + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 80 -j LOG --log-prefix "Antrea:O:Allow:egress1:"`, + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 80 -j ACCEPT`, + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 443 -j LOG --log-prefix "Antrea:O:Allow:egress1:"`, + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 443 -j ACCEPT`, }, } ingressCoreChains := []string{"ANTREA-POL-INGRESS-RULES"} @@ -980,6 +1047,7 @@ func TestNodeReconcilerBatchReconcileAndForget(t *testing.T) { egressCoreRules := [][]string{ { `-A ANTREA-POL-EGRESS-RULES -d 1.1.1.1/32 -j ANTREA-POL-EGRESSRULE1 -m comment --comment "Antrea: for rule egress-rule-01, policy AntreaClusterNetworkPolicy:name1"`, + `-A ANTREA-POL-EGRESS-RULES -d 1.1.1.1/32 -p tcp --dport 443 -j LOG --log-prefix "Antrea:O:Allow:egress2:test:"`, `-A ANTREA-POL-EGRESS-RULES -d 1.1.1.1/32 -p tcp --dport 443 -j ACCEPT -m comment --comment "Antrea: for rule egress-rule-02, policy AntreaClusterNetworkPolicy:name1"`, }, } @@ -990,6 +1058,7 @@ func TestNodeReconcilerBatchReconcileAndForget(t *testing.T) { } updatedEgressCoreRules := [][]string{ { + `-A ANTREA-POL-EGRESS-RULES -d 1.1.1.1/32 -p tcp --dport 443 -j LOG --log-prefix "Antrea:O:Allow:egress2:test:"`, `-A ANTREA-POL-EGRESS-RULES -d 1.1.1.1/32 -p tcp --dport 443 -j ACCEPT -m comment --comment "Antrea: for rule egress-rule-02, policy AntreaClusterNetworkPolicy:name1"`, }, } @@ -1027,12 +1096,16 @@ func TestNodeReconcilerBatchReconcileAndForget(t *testing.T) { } svcRules := [][]string{ { - "-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j ACCEPT", - "-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j ACCEPT", + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j LOG --log-prefix "Antrea:I:Allow:"`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 80 -j ACCEPT`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j LOG --log-prefix "Antrea:I:Allow:"`, + `-A ANTREA-POL-INGRESSRULE1 -p tcp --dport 443 -j ACCEPT`, }, { - "-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 80 -j ACCEPT", - "-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 443 -j ACCEPT", + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 80 -j LOG --log-prefix "Antrea:O:Allow:egress1:"`, + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 80 -j ACCEPT`, + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 443 -j LOG --log-prefix "Antrea:O:Allow:egress1:"`, + `-A ANTREA-POL-EGRESSRULE1 -p tcp --dport 443 -j ACCEPT`, }, } ingressCoreChains := []string{"ANTREA-POL-INGRESS-RULES"} @@ -1046,6 +1119,7 @@ func TestNodeReconcilerBatchReconcileAndForget(t *testing.T) { egressCoreRules := [][]string{ { `-A ANTREA-POL-EGRESS-RULES -d 2002:1a23:fb44::1/128 -j ANTREA-POL-EGRESSRULE1 -m comment --comment "Antrea: for rule egress-rule-01, policy AntreaClusterNetworkPolicy:name1"`, + `-A ANTREA-POL-EGRESS-RULES -d 2002:1a23:fb44::1/128 -p tcp --dport 443 -j LOG --log-prefix "Antrea:O:Allow:egress2:test:"`, `-A ANTREA-POL-EGRESS-RULES -d 2002:1a23:fb44::1/128 -p tcp --dport 443 -j ACCEPT -m comment --comment "Antrea: for rule egress-rule-02, policy AntreaClusterNetworkPolicy:name1"`, }, } @@ -1056,6 +1130,7 @@ func TestNodeReconcilerBatchReconcileAndForget(t *testing.T) { } updatedEgressCoreRules := [][]string{ { + `-A ANTREA-POL-EGRESS-RULES -d 2002:1a23:fb44::1/128 -p tcp --dport 443 -j LOG --log-prefix "Antrea:O:Allow:egress2:test:"`, `-A ANTREA-POL-EGRESS-RULES -d 2002:1a23:fb44::1/128 -p tcp --dport 443 -j ACCEPT -m comment --comment "Antrea: for rule egress-rule-02, policy AntreaClusterNetworkPolicy:name1"`, }, } diff --git a/pkg/agent/types/networkpolicy.go b/pkg/agent/types/networkpolicy.go index 483c70de108..35116708239 100644 --- a/pkg/agent/types/networkpolicy.go +++ b/pkg/agent/types/networkpolicy.go @@ -84,7 +84,7 @@ type NodePolicyRule struct { ServiceIPTChain string ServiceIPTRules []string CoreIPTChain string - CoreIPTRule string + CoreIPTRules []string IsIPv6 bool } diff --git a/pkg/agent/util/iptables/builder.go b/pkg/agent/util/iptables/builder.go index f247ed5d250..f9b49880f05 100644 --- a/pkg/agent/util/iptables/builder.go +++ b/pkg/agent/util/iptables/builder.go @@ -69,6 +69,15 @@ func (b *iptablesRuleBuilder) MatchCIDRDst(cidr string) IPTablesRuleBuilder { return b } +func (b *iptablesRuleBuilder) SetLogPrefix(prefix string) IPTablesRuleBuilder { + if prefix == "" { + return b + } + matchStr := fmt.Sprintf("--log-prefix \"%s\"", prefix) + b.writeSpec(matchStr) + return b +} + func (b *iptablesRuleBuilder) MatchIPSetSrc(ipsetName string, ipsetType ipset.SetType) IPTablesRuleBuilder { if ipsetName == "" { return b diff --git a/pkg/agent/util/iptables/builder_test.go b/pkg/agent/util/iptables/builder_test.go index c03fc94d949..f81f853769b 100644 --- a/pkg/agent/util/iptables/builder_test.go +++ b/pkg/agent/util/iptables/builder_test.go @@ -103,6 +103,18 @@ func TestBuilders(t *testing.T) { }, expected: `-A FORWARD -i eth0 -p icmp --icmp-type 0/0 -j ACCEPT`, }, + { + name: "Accept ICMP IPv4 with logging", + chain: ForwardChain, + buildFunc: func(builder IPTablesRuleBuilder) IPTablesRule { + return builder.MatchInputInterface(eth0). + MatchICMP(&icmpType0, &icmpCode0, ProtocolIPv4). + SetTarget(LOGTarget). + SetLogPrefix("Accept ICMP IPv4"). + Done() + }, + expected: `-A FORWARD -i eth0 -p icmp --icmp-type 0/0 -j LOG --log-prefix "Accept ICMP IPv4"`, + }, { name: "Accept ICMP IPv6", chain: ForwardChain, diff --git a/pkg/agent/util/iptables/iptables.go b/pkg/agent/util/iptables/iptables.go index 1dec9529d97..e5612c83765 100644 --- a/pkg/agent/util/iptables/iptables.go +++ b/pkg/agent/util/iptables/iptables.go @@ -49,6 +49,7 @@ const ( DNATTarget = "DNAT" RejectTarget = "REJECT" NotrackTarget = "NOTRACK" + LOGTarget = "LOG" PreRoutingChain = "PREROUTING" InputChain = "INPUT" @@ -133,6 +134,7 @@ type IPTablesRuleBuilder interface { MatchEstablishedOrRelated() IPTablesRuleBuilder MatchInputInterface(interfaceName string) IPTablesRuleBuilder MatchOutputInterface(interfaceName string) IPTablesRuleBuilder + SetLogPrefix(prefix string) IPTablesRuleBuilder SetTarget(target string) IPTablesRuleBuilder SetTargetDNATToDst(dnatIP string, dnatPort *int32) IPTablesRuleBuilder SetComment(comment string) IPTablesRuleBuilder diff --git a/pkg/controller/networkpolicy/validate.go b/pkg/controller/networkpolicy/validate.go index 0dbe0fcb955..91be6b0bd21 100644 --- a/pkg/controller/networkpolicy/validate.go +++ b/pkg/controller/networkpolicy/validate.go @@ -45,10 +45,10 @@ import ( type validator interface { // createValidate is the interface which must be satisfied for resource // CREATE events. - createValidate(curObj interface{}, userInfo authenticationv1.UserInfo) (string, bool) + createValidate(curObj interface{}, userInfo authenticationv1.UserInfo) ([]string, string, bool) // updateValidate is the interface which must be satisfied for resource // UPDATE events. - updateValidate(curObj, oldObj interface{}, userInfo authenticationv1.UserInfo) (string, bool) + updateValidate(curObj, oldObj interface{}, userInfo authenticationv1.UserInfo) ([]string, string, bool) // deleteValidate is the interface which must be satisfied for resource // DELETE events. deleteValidate(oldObj interface{}, userInfo authenticationv1.UserInfo) (string, bool) @@ -163,6 +163,7 @@ func NewNetworkPolicyValidator(networkPolicyController *NetworkPolicyController) func (v *NetworkPolicyValidator) Validate(ar *admv1.AdmissionReview) *admv1.AdmissionResponse { var result *metav1.Status var msg string + var warnings []string allowed := false op := ar.Request.Operation ui := ar.Request.UserInfo @@ -187,7 +188,7 @@ func (v *NetworkPolicyValidator) Validate(ar *admv1.AdmissionReview) *admv1.Admi return GetAdmissionResponseForErr(err) } } - msg, allowed = v.validateTier(&curTier, &oldTier, op, ui) + warnings, msg, allowed = v.validateTier(&curTier, &oldTier, op, ui) case "ClusterGroup": klog.V(2).Info("Validating ClusterGroup CRD") // Current serving versions of ClusterGroup are v1alpha3 and v1beta1. They have @@ -206,7 +207,7 @@ func (v *NetworkPolicyValidator) Validate(ar *admv1.AdmissionReview) *admv1.Admi return GetAdmissionResponseForErr(err) } } - msg, allowed = v.validateAntreaGroup(&curCG, &oldCG, op, ui) + warnings, msg, allowed = v.validateAntreaGroup(&curCG, &oldCG, op, ui) case "Group": klog.V(2).Info("Validating Group CRD") // Current serving versions of Group are v1alpha3 and v1beta1. They have the same @@ -225,7 +226,7 @@ func (v *NetworkPolicyValidator) Validate(ar *admv1.AdmissionReview) *admv1.Admi return GetAdmissionResponseForErr(err) } } - msg, allowed = v.validateAntreaGroup(&curG, &oldG, op, ui) + warnings, msg, allowed = v.validateAntreaGroup(&curG, &oldG, op, ui) case "ClusterNetworkPolicy": klog.V(2).Info("Validating Antrea ClusterNetworkPolicy CRD") var curACNP, oldACNP crdv1beta1.ClusterNetworkPolicy @@ -241,7 +242,7 @@ func (v *NetworkPolicyValidator) Validate(ar *admv1.AdmissionReview) *admv1.Admi return GetAdmissionResponseForErr(err) } } - msg, allowed = v.validateAntreaPolicy(&curACNP, &oldACNP, op, ui) + warnings, msg, allowed = v.validateAntreaPolicy(&curACNP, &oldACNP, op, ui) case "NetworkPolicy": klog.V(2).Info("Validating Antrea NetworkPolicy CRD") var curANNP, oldANNP crdv1beta1.NetworkPolicy @@ -257,7 +258,7 @@ func (v *NetworkPolicyValidator) Validate(ar *admv1.AdmissionReview) *admv1.Admi return GetAdmissionResponseForErr(err) } } - msg, allowed = v.validateAntreaPolicy(&curANNP, &oldANNP, op, ui) + warnings, msg, allowed = v.validateAntreaPolicy(&curANNP, &oldANNP, op, ui) case "AdminNetworkPolicy": klog.V(2).Info("Validating AdminNetworkPolicy CRD") var curANP, oldANP v1alpha1.AdminNetworkPolicy @@ -273,7 +274,7 @@ func (v *NetworkPolicyValidator) Validate(ar *admv1.AdmissionReview) *admv1.Admi return GetAdmissionResponseForErr(err) } } - msg, allowed = v.validateAdminNetworkPolicy(&curANP, &oldANP, op, ui) + warnings, msg, allowed = v.validateAdminNetworkPolicy(&curANP, &oldANP, op, ui) case "BaselineAdminNetworkPolicy": klog.V(2).Info("Validating BaselineAdminNetworkPolicy CRD") var curBANP, oldBANP v1alpha1.BaselineAdminNetworkPolicy @@ -289,7 +290,7 @@ func (v *NetworkPolicyValidator) Validate(ar *admv1.AdmissionReview) *admv1.Admi return GetAdmissionResponseForErr(err) } } - msg, allowed = v.validateAdminNetworkPolicy(&curBANP, &oldBANP, op, ui) + warnings, msg, allowed = v.validateAdminNetworkPolicy(&curBANP, &oldBANP, op, ui) } if msg != "" { result = &metav1.Status{ @@ -297,28 +298,30 @@ func (v *NetworkPolicyValidator) Validate(ar *admv1.AdmissionReview) *admv1.Admi } } return &admv1.AdmissionResponse{ - Allowed: allowed, - Result: result, + Allowed: allowed, + Result: result, + Warnings: warnings, } } -// validateAntreaPolicy validates the admission of a Antrea NetworkPolicy CRDs -func (v *NetworkPolicyValidator) validateAntreaPolicy(curObj, oldObj interface{}, op admv1.Operation, userInfo authenticationv1.UserInfo) (string, bool) { +// validateAntreaPolicy validates the admission of Antrea NetworkPolicy CRDs +func (v *NetworkPolicyValidator) validateAntreaPolicy(curObj, oldObj interface{}, op admv1.Operation, userInfo authenticationv1.UserInfo) ([]string, string, bool) { allowed := true reason := "" + var warnings []string switch op { case admv1.Create: for _, val := range v.antreaPolicyValidators { - reason, allowed = val.createValidate(curObj, userInfo) + warnings, reason, allowed = val.createValidate(curObj, userInfo) if !allowed { - return reason, allowed + return warnings, reason, allowed } } case admv1.Update: for _, val := range v.antreaPolicyValidators { - reason, allowed = val.updateValidate(curObj, oldObj, userInfo) + warnings, reason, allowed = val.updateValidate(curObj, oldObj, userInfo) if !allowed { - return reason, allowed + return warnings, reason, allowed } } case admv1.Delete: @@ -327,40 +330,66 @@ func (v *NetworkPolicyValidator) validateAntreaPolicy(curObj, oldObj interface{} for _, val := range v.antreaPolicyValidators { reason, allowed = val.deleteValidate(oldObj, userInfo) if !allowed { - return reason, allowed + return warnings, reason, allowed } } } - return reason, allowed + return warnings, reason, allowed } -func (v *NetworkPolicyValidator) validateAdminNetworkPolicy(curObj, oldObj interface{}, op admv1.Operation, userInfo authenticationv1.UserInfo) (string, bool) { +func (v *NetworkPolicyValidator) validateAdminNetworkPolicy(curObj, oldObj interface{}, op admv1.Operation, userInfo authenticationv1.UserInfo) ([]string, string, bool) { allowed := true reason := "" + var warnings []string switch op { case admv1.Create: for _, val := range v.adminNPValidators { - reason, allowed = val.createValidate(curObj, userInfo) + warnings, reason, allowed = val.createValidate(curObj, userInfo) if !allowed { - return reason, allowed + return warnings, reason, allowed } } case admv1.Update: for _, val := range v.adminNPValidators { - reason, allowed = val.updateValidate(curObj, oldObj, userInfo) + warnings, reason, allowed = val.updateValidate(curObj, oldObj, userInfo) if !allowed { - return reason, allowed + return warnings, reason, allowed } } case admv1.Delete: for _, val := range v.adminNPValidators { reason, allowed = val.deleteValidate(oldObj, userInfo) if !allowed { - return reason, allowed + return warnings, reason, allowed } } } - return reason, allowed + return warnings, reason, allowed +} + +func (v *antreaPolicyValidator) checkLogLabel(specAppliedTo []crdv1beta1.AppliedTo, ingress, egress []crdv1beta1.Rule) []string { + appliedToNode := false + for _, eachAppliedTo := range specAppliedTo { + if eachAppliedTo.NodeSelector != nil { + appliedToNode = true + break + } + } + if !appliedToNode { + return nil + } + var warnings []string + for _, eachIngress := range ingress { + if eachIngress.EnableLogging && len(eachIngress.LogLabel) > 12 { + warnings = append(warnings, fmt.Sprintf("LogLabels for Node NetworkPolicies are limited to 12 characters, but the label %q for policy rule %q exceeds the limit and will be truncated in kernel logs", eachIngress.LogLabel, eachIngress.Name)) + } + } + for _, eachEgress := range egress { + if eachEgress.EnableLogging && len(eachEgress.LogLabel) > 12 { + warnings = append(warnings, fmt.Sprintf("LogLabels for Node NetworkPolicies are limited to 12 characters, but the label %q for policy rule %q exceeds the limit and will be truncated in kernel logs", eachEgress.LogLabel, eachEgress.Name)) + } + } + return warnings } // validatePort validates if ports is valid @@ -401,24 +430,25 @@ func (v *antreaPolicyValidator) validatePort(ingress, egress []crdv1beta1.Rule) } // validateAntreaGroup validates the admission of a Group, ClusterGroup resource -func (v *NetworkPolicyValidator) validateAntreaGroup(curAG, oldAG interface{}, op admv1.Operation, userInfo authenticationv1.UserInfo) (string, bool) { +func (v *NetworkPolicyValidator) validateAntreaGroup(curAG, oldAG interface{}, op admv1.Operation, userInfo authenticationv1.UserInfo) ([]string, string, bool) { allowed := true reason := "" + var warnings []string switch op { case admv1.Create: klog.V(2).Info("Validating CREATE request for ClusterGroup/Group") for _, val := range v.groupValidators { - reason, allowed = val.createValidate(curAG, userInfo) + warnings, reason, allowed = val.createValidate(curAG, userInfo) if !allowed { - return reason, allowed + return warnings, reason, allowed } } case admv1.Update: klog.V(2).Info("Validating UPDATE request for ClusterGroup/Group") for _, val := range v.groupValidators { - reason, allowed = val.updateValidate(curAG, oldAG, userInfo) + warnings, reason, allowed = val.updateValidate(curAG, oldAG, userInfo) if !allowed { - return reason, allowed + return warnings, reason, allowed } } case admv1.Delete: @@ -426,33 +456,34 @@ func (v *NetworkPolicyValidator) validateAntreaGroup(curAG, oldAG interface{}, o for _, val := range v.groupValidators { reason, allowed = val.deleteValidate(oldAG, userInfo) if !allowed { - return reason, allowed + return warnings, reason, allowed } } } - return reason, allowed + return warnings, reason, allowed } // validateTier validates the admission of a Tier resource -func (v *NetworkPolicyValidator) validateTier(curTier, oldTier *crdv1beta1.Tier, op admv1.Operation, userInfo authenticationv1.UserInfo) (string, bool) { +func (v *NetworkPolicyValidator) validateTier(curTier, oldTier *crdv1beta1.Tier, op admv1.Operation, userInfo authenticationv1.UserInfo) ([]string, string, bool) { allowed := true reason := "" + var warnings []string switch op { case admv1.Create: klog.V(2).Info("Validating CREATE request for Tier") for _, val := range v.tierValidators { - reason, allowed = val.createValidate(curTier, userInfo) + warnings, reason, allowed = val.createValidate(curTier, userInfo) if !allowed { - return reason, allowed + return warnings, reason, allowed } } case admv1.Update: // Tier priority updates are not allowed klog.V(2).Info("Validating UPDATE request for Tier") for _, val := range v.tierValidators { - reason, allowed = val.updateValidate(curTier, oldTier, userInfo) + warnings, reason, allowed = val.updateValidate(curTier, oldTier, userInfo) if !allowed { - return reason, allowed + return warnings, reason, allowed } } case admv1.Delete: @@ -460,11 +491,11 @@ func (v *NetworkPolicyValidator) validateTier(curTier, oldTier *crdv1beta1.Tier, for _, val := range v.tierValidators { reason, allowed = val.deleteValidate(oldTier, userInfo) if !allowed { - return reason, allowed + return warnings, reason, allowed } } } - return reason, allowed + return warnings, reason, allowed } func (v *antreaPolicyValidator) tierExists(name string) bool { @@ -486,15 +517,16 @@ func GetAdmissionResponseForErr(err error) *admv1.AdmissionResponse { } // createValidate validates the CREATE events of Antrea-native policies, -func (v *antreaPolicyValidator) createValidate(curObj interface{}, userInfo authenticationv1.UserInfo) (string, bool) { +func (v *antreaPolicyValidator) createValidate(curObj interface{}, userInfo authenticationv1.UserInfo) ([]string, string, bool) { return v.validatePolicy(curObj) } // validatePolicy validates the CREATE and UPDATE events of Antrea-native policies, -func (v *antreaPolicyValidator) validatePolicy(curObj interface{}) (string, bool) { +func (v *antreaPolicyValidator) validatePolicy(curObj interface{}) ([]string, string, bool) { var tier string var ingress, egress []crdv1beta1.Rule var specAppliedTo []crdv1beta1.AppliedTo + var warnings []string switch curObj.(type) { case *crdv1beta1.ClusterNetworkPolicy: curACNP := curObj.(*crdv1beta1.ClusterNetworkPolicy) @@ -511,47 +543,48 @@ func (v *antreaPolicyValidator) validatePolicy(curObj interface{}) (string, bool } reason, allowed := v.validateTierForPolicy(tier) if !allowed { - return reason, allowed + return warnings, reason, allowed } reason, allowed = v.validateTierForPassAction(tier, ingress, egress) if !allowed { - return reason, allowed + return warnings, reason, allowed } if ruleNameUnique := v.validateRuleName(ingress, egress); !ruleNameUnique { - return "rules names must be unique within the policy", false + return warnings, "rules names must be unique within the policy", false } reason, allowed = v.validateAppliedTo(ingress, egress, specAppliedTo) if !allowed { - return reason, allowed + return warnings, reason, allowed } reason, allowed = v.validatePeers(ingress, egress) if !allowed { - return reason, allowed + return warnings, reason, allowed } reason, allowed = v.validateAppliedToServiceIngressPeer(specAppliedTo, ingress) if !allowed { - return reason, allowed + return warnings, reason, allowed } reason, allowed = v.validateFQDNSelectors(egress) if !allowed { - return reason, allowed + return warnings, reason, allowed } reason, allowed = v.validateEgressMulticastAddress(egress) if !allowed { - return reason, allowed + return warnings, reason, allowed } reason, allowed = v.validateMulticastIGMP(ingress, egress) if !allowed { - return reason, allowed + return warnings, reason, allowed } reason, allowed = v.validateL7Protocols(ingress, egress) if !allowed { - return reason, allowed + return warnings, reason, allowed } if err := v.validatePort(ingress, egress); err != nil { - return err.Error(), false + return warnings, err.Error(), false } - return "", true + warnings = append(warnings, v.checkLogLabel(specAppliedTo, ingress, egress)...) + return warnings, "", true } // validateRuleName validates if the name of each rule is unique within a policy @@ -599,7 +632,6 @@ func (v *antreaPolicyValidator) validateAppliedTo(ingress, egress []crdv1beta1.R appliedToEgressRule = 2 ) - appliedToNode := false checkAppliedTo := func(appliedTo []crdv1beta1.AppliedTo, appliedToScope int) (string, bool) { appliedToSvcNum := 0 for _, eachAppliedTo := range appliedTo { @@ -611,7 +643,6 @@ func (v *antreaPolicyValidator) validateAppliedTo(ingress, egress []crdv1beta1.R if appliedToFieldsNum > 1 { return "nodeSelector cannot be set with other peers in appliedTo", false } - appliedToNode = true } if eachAppliedTo.ServiceAccount != nil && appliedToFieldsNum > 1 { return "serviceAccount cannot be set with other peers in appliedTo", false @@ -640,24 +671,18 @@ func (v *antreaPolicyValidator) validateAppliedTo(ingress, egress []crdv1beta1.R return reason, allowed } - enableLogging := false for _, eachIngress := range ingress { - enableLogging = enableLogging || eachIngress.EnableLogging reason, allowed = checkAppliedTo(eachIngress.AppliedTo, appliedToIngressRule) if !allowed { return reason, allowed } } for _, eachEgress := range egress { - enableLogging = enableLogging || eachEgress.EnableLogging reason, allowed = checkAppliedTo(eachEgress.AppliedTo, appliedToEgressRule) if !allowed { return reason, allowed } } - if enableLogging && appliedToNode { - return "traffic logging for NodeNetworkPolicy is not supported", false - } return "", true } @@ -957,7 +982,7 @@ func (v *antreaPolicyValidator) validateFQDNSelectors(egressRules []crdv1beta1.R } // updateValidate validates the UPDATE events of Antrea-native policies. -func (v *antreaPolicyValidator) updateValidate(curObj, oldObj interface{}, userInfo authenticationv1.UserInfo) (string, bool) { +func (v *antreaPolicyValidator) updateValidate(curObj, oldObj interface{}, userInfo authenticationv1.UserInfo) ([]string, string, bool) { return v.validatePolicy(curObj) } @@ -967,25 +992,25 @@ func (v *antreaPolicyValidator) deleteValidate(oldObj interface{}, userInfo auth } // createValidate validates the CREATE events of Tier resources. -func (t *tierValidator) createValidate(curObj interface{}, userInfo authenticationv1.UserInfo) (string, bool) { +func (t *tierValidator) createValidate(curObj interface{}, userInfo authenticationv1.UserInfo) ([]string, string, bool) { if len(t.networkPolicyController.tierInformer.Informer().GetIndexer().ListIndexFuncValues(PriorityIndex)) >= maxSupportedTiers { - return fmt.Sprintf("maximum number of Tiers supported: %d", maxSupportedTiers), false + return nil, fmt.Sprintf("maximum number of Tiers supported: %d", maxSupportedTiers), false } curTier := curObj.(*crdv1beta1.Tier) // Tier priority must not overlap reserved tier's priority. if reservedTierPriorities.Has(curTier.Spec.Priority) { - return fmt.Sprintf("tier %s priority %d is reserved", curTier.Name, curTier.Spec.Priority), false + return nil, fmt.Sprintf("tier %s priority %d is reserved", curTier.Name, curTier.Spec.Priority), false } // Tier priority must not overlap existing tier's priority trs, err := t.networkPolicyController.tierInformer.Informer().GetIndexer().ByIndex(PriorityIndex, strconv.FormatInt(int64(curTier.Spec.Priority), 10)) if err != nil || len(trs) > 0 { - return fmt.Sprintf("tier %s priority %d overlaps with existing Tier", curTier.Name, curTier.Spec.Priority), false + return nil, fmt.Sprintf("tier %s priority %d overlaps with existing Tier", curTier.Name, curTier.Spec.Priority), false } - return "", true + return nil, "", true } // updateValidate validates the UPDATE events of Tier resources. -func (t *tierValidator) updateValidate(curObj, oldObj interface{}, userInfo authenticationv1.UserInfo) (string, bool) { +func (t *tierValidator) updateValidate(curObj, oldObj interface{}, userInfo authenticationv1.UserInfo) ([]string, string, bool) { allowed := true reason := "" curTier := curObj.(*crdv1beta1.Tier) @@ -994,13 +1019,13 @@ func (t *tierValidator) updateValidate(curObj, oldObj interface{}, userInfo auth namespace := env.GetAntreaNamespace() // Allow exception of Tier Priority updates performed by the antrea-controller if serviceaccount.MatchesUsername(namespace, env.GetAntreaControllerServiceAccount(), userInfo.Username) { - return "", true + return nil, "", true } if curTier.Spec.Priority != oldTier.Spec.Priority { allowed = false reason = "update to Tier priority is not allowed" } - return reason, allowed + return nil, reason, allowed } // deleteValidate validates the DELETE events of Tier resources. @@ -1148,17 +1173,17 @@ func (g *groupValidator) validateG(grp *crdv1beta1.Group) (string, bool) { } // createValidate validates the CREATE events of Group, ClusterGroup resources. -func (g *groupValidator) createValidate(curObj interface{}, userInfo authenticationv1.UserInfo) (string, bool) { +func (g *groupValidator) createValidate(curObj interface{}, userInfo authenticationv1.UserInfo) ([]string, string, bool) { return g.validateGroup(curObj) } // updateValidate validates the UPDATE events of Group, ClusterGroup resources. -func (g *groupValidator) updateValidate(curObj, oldObj interface{}, userInfo authenticationv1.UserInfo) (string, bool) { +func (g *groupValidator) updateValidate(curObj, oldObj interface{}, userInfo authenticationv1.UserInfo) ([]string, string, bool) { return g.validateGroup(curObj) } // validateGroup validates the CREATE and UPDATE events of Group, ClusterGroup resources. -func (g *groupValidator) validateGroup(curObj interface{}) (string, bool) { +func (g *groupValidator) validateGroup(curObj interface{}) ([]string, string, bool) { var curCG *crdv1beta1.ClusterGroup var curG *crdv1beta1.Group var reason string @@ -1171,7 +1196,7 @@ func (g *groupValidator) validateGroup(curObj interface{}) (string, bool) { curG = curObj.(*crdv1beta1.Group) reason, allowed = g.validateG(curG) } - return reason, allowed + return nil, reason, allowed } // deleteValidate validates the DELETE events of Group, ClusterGroup resources. @@ -1193,7 +1218,7 @@ func (a *adminPolicyValidator) validateBANP(banp *v1alpha1.BaselineAdminNetworkP return "", true } -func (a *adminPolicyValidator) createValidate(curObj interface{}, userInfo authenticationv1.UserInfo) (string, bool) { +func (a *adminPolicyValidator) createValidate(curObj interface{}, userInfo authenticationv1.UserInfo) ([]string, string, bool) { var reason string var allowed bool switch curObj.(type) { @@ -1204,10 +1229,10 @@ func (a *adminPolicyValidator) createValidate(curObj interface{}, userInfo authe curBANP := curObj.(*v1alpha1.BaselineAdminNetworkPolicy) reason, allowed = a.validateBANP(curBANP) } - return reason, allowed + return nil, reason, allowed } -func (a *adminPolicyValidator) updateValidate(curObj, oldObj interface{}, userInfo authenticationv1.UserInfo) (string, bool) { +func (a *adminPolicyValidator) updateValidate(curObj, oldObj interface{}, userInfo authenticationv1.UserInfo) ([]string, string, bool) { return a.createValidate(curObj, userInfo) } diff --git a/pkg/controller/networkpolicy/validate_test.go b/pkg/controller/networkpolicy/validate_test.go index acdd9415a04..eb67dd6b6e3 100644 --- a/pkg/controller/networkpolicy/validate_test.go +++ b/pkg/controller/networkpolicy/validate_test.go @@ -41,11 +41,12 @@ var ( func TestValidateAntreaClusterNetworkPolicy(t *testing.T) { tests := []struct { - name string - featureGates map[featuregate.Feature]bool - policy *crdv1beta1.ClusterNetworkPolicy - operation admv1.Operation - expectedReason string + name string + featureGates map[featuregate.Feature]bool + policy *crdv1beta1.ClusterNetworkPolicy + operation admv1.Operation + expectedReason string + expectedWarnings []string }{ { name: "acnp-non-existent-tier", @@ -526,10 +527,10 @@ func TestValidateAntreaClusterNetworkPolicy(t *testing.T) { expectedReason: "", }, { - name: "acnp-appliedto-node-with-logging", + name: "acnp-appliedto-node-with-loglabel", policy: &crdv1beta1.ClusterNetworkPolicy{ ObjectMeta: metav1.ObjectMeta{ - Name: "acnp-appliedto-node-with-logging", + Name: "acnp-appliedto-node-alone", }, Spec: crdv1beta1.ClusterNetworkPolicySpec{ AppliedTo: []crdv1beta1.AppliedTo{ @@ -541,6 +542,7 @@ func TestValidateAntreaClusterNetworkPolicy(t *testing.T) { }, Ingress: []crdv1beta1.Rule{ { + Name: "rule0", Action: &allowAction, From: []crdv1beta1.NetworkPolicyPeer{ { @@ -550,12 +552,28 @@ func TestValidateAntreaClusterNetworkPolicy(t *testing.T) { }, }, EnableLogging: true, + LogLabel: "long-long-long-label", + }, + { + Name: "rule1", + Action: &allowAction, + From: []crdv1beta1.NetworkPolicyPeer{ + { + NodeSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"foo1": "bar1"}, + }, + }, + }, + EnableLogging: true, + LogLabel: "short-label", }, }, }, }, - operation: admv1.Create, - expectedReason: "traffic logging for NodeNetworkPolicy is not supported", + operation: admv1.Create, + expectedWarnings: []string{ + `LogLabels for Node NetworkPolicies are limited to 12 characters, but the label "long-long-long-label" for policy rule "rule0" exceeds the limit and will be truncated in kernel logs`, + }, }, { name: "acnp-rule-group-set-with-psel", @@ -1935,8 +1953,9 @@ func TestValidateAntreaClusterNetworkPolicy(t *testing.T) { } _, controller := newController(nil, nil) validator := NewNetworkPolicyValidator(controller.NetworkPolicyController) - actualReason, allowed := validator.validateAntreaPolicy(tt.policy, "", tt.operation, authenticationv1.UserInfo{}) + warnings, actualReason, allowed := validator.validateAntreaPolicy(tt.policy, "", tt.operation, authenticationv1.UserInfo{}) assert.Equal(t, tt.expectedReason, actualReason) + assert.Equal(t, tt.expectedWarnings, warnings) if tt.expectedReason == "" { assert.True(t, allowed) } else { @@ -2042,7 +2061,7 @@ func TestValidateAntreaNetworkPolicy(t *testing.T) { } _, controller := newController(nil, nil) validator := NewNetworkPolicyValidator(controller.NetworkPolicyController) - actualReason, allowed := validator.validateAntreaPolicy(tt.policy, "", tt.operation, authenticationv1.UserInfo{}) + _, actualReason, allowed := validator.validateAntreaPolicy(tt.policy, "", tt.operation, authenticationv1.UserInfo{}) assert.Equal(t, tt.expectedReason, actualReason) if tt.expectedReason == "" { assert.True(t, allowed) @@ -2348,7 +2367,7 @@ func TestValidateAntreaClusterGroup(t *testing.T) { controller.addClusterGroup(tt.existGroup) } validator := NewNetworkPolicyValidator(controller.NetworkPolicyController) - actualReason, allowed := validator.validateAntreaGroup(tt.curCG, tt.oldCG, tt.operation, authenticationv1.UserInfo{}) + _, actualReason, allowed := validator.validateAntreaGroup(tt.curCG, tt.oldCG, tt.operation, authenticationv1.UserInfo{}) assert.Equal(t, tt.expectedReason, actualReason) if tt.expectedReason == "" { assert.True(t, allowed) @@ -2623,7 +2642,7 @@ func TestValidateAntreaGroup(t *testing.T) { controller.addGroup(tt.existGroup) } validator := NewNetworkPolicyValidator(controller.NetworkPolicyController) - actualReason, allowed := validator.validateAntreaGroup(tt.curGroup, tt.oldGroup, tt.operation, authenticationv1.UserInfo{}) + _, actualReason, allowed := validator.validateAntreaGroup(tt.curGroup, tt.oldGroup, tt.operation, authenticationv1.UserInfo{}) assert.Equal(t, tt.expectedReason, actualReason) if tt.expectedReason == "" { assert.True(t, allowed) @@ -2843,7 +2862,7 @@ func TestValidateTier(t *testing.T) { controller.annpStore.Add(tt.existANNP) } validator := NewNetworkPolicyValidator(controller.NetworkPolicyController) - actualReason, allowed := validator.validateTier(tt.curTier, tt.oldTier, tt.operation, tt.user) + _, actualReason, allowed := validator.validateTier(tt.curTier, tt.oldTier, tt.operation, tt.user) assert.Equal(t, tt.expectedReason, actualReason) if tt.expectedReason == "" { assert.True(t, allowed) @@ -3049,7 +3068,7 @@ func TestValidateAdminNetworkPolicy(t *testing.T) { t.Run(tt.name, func(t *testing.T) { _, controller := newController(nil, nil) validator := NewNetworkPolicyValidator(controller.NetworkPolicyController) - actualReason, allowed := validator.validateAdminNetworkPolicy(tt.policy, "", tt.operation, authenticationv1.UserInfo{}) + _, actualReason, allowed := validator.validateAdminNetworkPolicy(tt.policy, "", tt.operation, authenticationv1.UserInfo{}) assert.Equal(t, tt.expectedReason, actualReason) if tt.expectedReason == "" { assert.True(t, allowed) diff --git a/test/e2e/nodenetworkpolicy_test.go b/test/e2e/nodenetworkpolicy_test.go index c8e20a0d446..c58324ebdba 100644 --- a/test/e2e/nodenetworkpolicy_test.go +++ b/test/e2e/nodenetworkpolicy_test.go @@ -15,12 +15,18 @@ package e2e import ( + "context" "fmt" + "net" + "regexp" "strings" "testing" "time" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" "antrea.io/antrea/pkg/agent/config" crdv1beta1 "antrea.io/antrea/pkg/apis/crd/v1beta1" @@ -114,6 +120,7 @@ func TestAntreaNodeNetworkPolicy(t *testing.T) { t.Run("Case=ACNPTierOverride", func(t *testing.T) { testNodeACNPTierOverride(t) }) t.Run("Case=ACNPCustomTiers", func(t *testing.T) { testNodeACNPCustomTiers(t) }) t.Run("Case=ACNPPriorityConflictingRule", func(t *testing.T) { testNodeACNPPriorityConflictingRule(t) }) + t.Run("Case=ACNPAuditLogging", func(t *testing.T) { testNodeACNPAuditLogging(t, data) }) k8sUtils.Cleanup(namespaces) @@ -891,3 +898,102 @@ func testNodeACNPNestedIPBlockClusterGroupCreateAndUpdate(t *testing.T) { } executeTests(t, testCase) } + +func testNodeACNPAuditLogging(t *testing.T, data *TestData) { + // In a Kind cluster, each Kubernetes Node runs as a Docker container. The iptables LOG target doesn't generate + // kernel logs inside containers unless /proc/sys/net/netfilter/nf_log_all_netns is set to 1. However, this setting + // cannot be modified in GitHub Actions. + skipIfProviderIs(t, "kind", "The iptables LOG action doesn't generate log in container") + + randomLogLabel := randSeq(12) // Generate a random logLabel with 12 characters long. + toBeTruncated := "thisPartShouldBeTruncated" + logLabel := randomLogLabel + toBeTruncated // Append the suffix to the logLabel. + builder := &ClusterNetworkPolicySpecBuilder{} + builder = builder.SetName("acnp-drop-x-to-y-egress"). + SetPriority(1.0). + SetAppliedToGroup([]ACNPAppliedToSpec{{NodeSelector: map[string]string{labelNodeHostname: nodes["x"]}}}) + builder.AddEgress(ProtocolTCP, &p80, nil, nil, nil, nil, nil, nil, nil, nil, map[string]string{labelNodeHostname: nodes["y"]}, nil, + nil, nil, nil, nil, nil, crdv1beta1.RuleActionDrop, "", "", nil) + builder.AddEgressLogging(logLabel) + + acnp, err := k8sUtils.CreateOrUpdateACNP(builder.Get()) + require.NoError(t, err) + require.NoError(t, data.waitForACNPRealized(t, acnp.Name, policyRealizedTimeout)) + + antreaPodName, err := data.getAntreaPodOnNode(nodes["x"]) + require.NoError(t, err) + + expectedLogPrefix := fmt.Sprintf("Antrea:O:Drop:%s:", randomLogLabel) + + // Before generating some traffic, there should be no corresponding kernel log on hostNetwork Pod x/a, and the kernel + // logs should not contain the generated random logLabel. + stdout, stderr, err := data.RunCommandFromPod(antreaNamespace, antreaPodName, agentContainerName, []string{"sh", "-c", "dmesg | tail -n 200"}) + if err != nil || stderr != "" { + t.Fatalf("Got error: %v, %v", err, stderr) + } + require.NotContains(t, stdout, expectedLogPrefix) + + // Generate some traffic that will be dropped by acnp-drop-x-to-y-egress. In this test, the target IP is the Antrea + // gateway IP of Node y. + _, err = k8sUtils.Probe(getNS("x"), "a", getNS("y"), "a", p80, ProtocolTCP, nil, nil) + require.NoError(t, err) + + ipv4Enabled, ipv6Enabled := isIPv4Enabled(), isIPv6Enabled() + + getNodeIPs := func(nodeName string) (string, string) { + nodeInfo := getNodeByName(nodeName) + require.NotNil(t, nodeInfo) + ip4, ip6 := nodeGatewayIPs(nodeInfo.idx) + if ipv4Enabled { + require.NotEmpty(t, ip4) + } + if ipv6Enabled { + require.NotEmpty(t, ip6) + ip6 = expandIPv6(ip6) + } + return ip4, ip6 + } + srcIP4, srcIP6 := getNodeIPs(nodes["x"]) + dstIP4, dstIP6 := getNodeIPs(nodes["y"]) + + // Add (?s) to match \n. + re4 := regexp.MustCompile(fmt.Sprintf(`(?s)%s.*SRC=%s DST=%s`, expectedLogPrefix, srcIP4, dstIP4)) + re6 := regexp.MustCompile(fmt.Sprintf(`(?s)%s.*SRC=%s DST=%s`, expectedLogPrefix, srcIP6, dstIP6)) + + // TODO: Replace wait.PollUntilContextTimeout with assert.EventuallyWithT after the fix for + // https://github.com/stretchr/testify/issues/1396 is included in the latest release. + assert.NoError(t, wait.PollUntilContextTimeout(context.Background(), time.Second, 30*time.Second, true, func(ctx context.Context) (bool, error) { + // After generating some traffic, there should be corresponding kernel logs on hostNetwork Pod x/a, and the kernel + // logs should contain the generated random logLabel and not contain the part that should be truncated. + stdout, stderr, err = data.RunCommandFromPod(antreaNamespace, antreaPodName, agentContainerName, []string{"sh", "-c", "dmesg | tail -n 200"}) + require.NoError(t, err) + require.Empty(t, stderr) + + if ipv4Enabled { + assert.Regexp(t, re4, stdout, "Expected IPv4 log entry not found") + assert.NotContains(t, stdout, toBeTruncated) + } + if ipv6Enabled { + assert.Regexp(t, re6, stdout, "Expected IPv6 log entry not found") + assert.NotContains(t, stdout, toBeTruncated) + } + return true, nil + })) +} + +func expandIPv6(ipv6 string) string { + // Parse the IPv6 address + parsedIP := net.ParseIP(ipv6) + + // Get the IPv6 portion from the parsed IP (the last 16 bytes) + ipv6Bytes := parsedIP.To16() + + // Format each 2-byte segment as a four-digit hex value + var segments []string + for i := 0; i < len(ipv6Bytes); i += 2 { + segments = append(segments, fmt.Sprintf("%04x", int(ipv6Bytes[i])<<8|int(ipv6Bytes[i+1]))) + } + + // Join the segments with ":" to form the expanded address + return strings.Join(segments, ":") +}