diff --git a/docs/antrea-node-network-policy.md b/docs/antrea-node-network-policy.md index 8f79d0e6c39..dfb1b9268b1 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,52 @@ 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. + +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 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 +169,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 f3c2d3982ee..6e2467a14a9 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 } } @@ -453,7 +472,7 @@ func (r *nodeReconciler) update(lastRealized *nodePolicyLastRealized, newRule *C } } if prevIPSet != ipset || prevIPNet != ipnet { - 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 } } @@ -496,9 +515,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 @@ -533,7 +550,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 @@ -614,23 +631,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 != "" { @@ -638,8 +658,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 { @@ -657,13 +677,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 { @@ -681,6 +714,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()) @@ -707,3 +747,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 08428520c7a..e8b862a641d 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{ @@ -288,8 +310,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{ @@ -316,8 +340,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{ @@ -357,8 +383,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{ @@ -376,6 +404,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"`, }, } @@ -416,25 +445,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"`, }, } @@ -481,8 +515,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{ @@ -495,17 +531,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"`, }, } @@ -537,21 +576,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"`, }, } @@ -675,8 +718,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{ @@ -719,8 +764,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{ @@ -768,8 +815,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 @@ -816,6 +865,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"`, }, } @@ -824,12 +874,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"`, }, } @@ -858,6 +911,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"`, }, } @@ -866,12 +920,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"`, }, } @@ -901,12 +958,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"`, }, } @@ -915,18 +974,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"`, }, } @@ -1026,12 +1095,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"} @@ -1045,6 +1118,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"`, }, } @@ -1055,6 +1129,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 e0bda05924b..de01dcfa68f 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) { +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,64 @@ 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 label %q for policy rule %q exceeds the limit and will be truncated in kernel logs", eachIngress.LogLabel, eachIngress.Name)) + } + } + for _, eachEgress := range egress { + warnings = append(warnings, fmt.Sprintf("LogLabels for Node NetworkPolicies are limited to 12 characters, but 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 +428,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 +454,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 +489,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 +515,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 +541,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 +630,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 +641,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 +669,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 } @@ -929,7 +952,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) } @@ -939,25 +962,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) @@ -966,13 +989,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. @@ -1116,17 +1139,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 @@ -1139,7 +1162,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. @@ -1161,7 +1184,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) { @@ -1172,10 +1195,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 94d8bbe8cc3..16ce0b6b9f3 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 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", @@ -1836,8 +1854,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 { @@ -1908,7 +1927,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) @@ -2195,7 +2214,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) @@ -2452,7 +2471,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) @@ -2672,7 +2691,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) @@ -2878,7 +2897,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..8d3fd0eafef 100644 --- a/test/e2e/nodenetworkpolicy_test.go +++ b/test/e2e/nodenetworkpolicy_test.go @@ -20,6 +20,7 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "antrea.io/antrea/pkg/agent/config" @@ -114,6 +115,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 +893,36 @@ func testNodeACNPNestedIPBlockClusterGroupCreateAndUpdate(t *testing.T) { } executeTests(t, testCase) } + +func testNodeACNPAuditLogging(t *testing.T, data *TestData) { + logLabel := "test-labels-thisPartShouldBeTruncated" + 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()) + failOnError(err, t) + failOnError(data.waitForACNPRealized(t, acnp.Name, policyRealizedTimeout), t) + + expectedLogPrefix := "Antrea:O:Drop:test-labels-:" + podXa, err := k8sUtils.GetPodByLabel(getNS("x"), "a") + require.NoError(t, err) + podXaName := podXa.GetName() + + // Before generating some traffic, there should be no corresponding log on hostNetwork Pod x/a. + stdout, _, err := data.RunCommandFromPod(getNS("x"), podXaName, "c80", []string{"dmesg"}) + require.NoError(t, err) + require.NotContains(t, stdout, expectedLogPrefix) + + // Generate some traffic that will be dropped by acnp-drop-x-to-y-egress. + k8sUtils.Probe(getNS("x"), "a", getNS("y"), "a", p80, ProtocolTCP, nil, nil) + + // After generating some traffic, there should be corresponding logs on hostNetwork Pod x/a. + stdout, _, err = data.RunCommandFromPod(getNS("x"), podXaName, "c80", []string{"dmesg"}) + require.NoError(t, err) + require.Contains(t, stdout, expectedLogPrefix) +}