From 8294adaded87c1a6162e7b11216c50435a530cf7 Mon Sep 17 00:00:00 2001 From: Alexandre Perrin Date: Tue, 4 Apr 2023 10:20:19 +0200 Subject: [PATCH] cmd/observe: Add traffic direction filter Before this patch, it was not possible to filter flows by traffic direction. Signed-off-by: Alexandre Perrin --- cmd/observe/flows.go | 6 ++ cmd/observe/flows_filter.go | 16 +++++ cmd/observe/flows_filter_test.go | 59 +++++++++++++++ cmd/observe_help.txt | 119 ++++++++++++++++--------------- 4 files changed, 141 insertions(+), 59 deletions(-) diff --git a/cmd/observe/flows.go b/cmd/observe/flows.go index b77bb30b8..bac5ff9f3 100644 --- a/cmd/observe/flows.go +++ b/cmd/observe/flows.go @@ -419,6 +419,9 @@ func newFlowsCmdHelper(usage cmdUsage, vp *viper.Viper, ofilter *flowFilter) *co filterFlags.Var(filterVar( "to-identity", ofilter, "Show all flows terminating at an endpoint with the given security identity")) + filterFlags.Var(filterVar( + "traffic-direction", ofilter, + "Show all flows in the given traffic direction (either ingress or egress)")) rawFilterFlags := pflag.NewFlagSet("raw-filters", pflag.ContinueOnError) rawFilterFlags.StringArray(allowlistFlag, []string{}, "Specify allowlist as JSON encoded FlowFilters") @@ -504,6 +507,9 @@ func newFlowsCmdHelper(usage cmdUsage, vp *viper.Viper, ofilter *flowFilter) *co flowsCmd.RegisterFlagCompletionFunc("from-identity", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { return reservedIdentitiesNames(), cobra.ShellCompDirectiveDefault }) + flowsCmd.RegisterFlagCompletionFunc("traffic-direction", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { + return []string{"ingress", "egress"}, cobra.ShellCompDirectiveDefault + }) flowsCmd.RegisterFlagCompletionFunc("output", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { return []string{ "compact", diff --git a/cmd/observe/flows_filter.go b/cmd/observe/flows_filter.go index 83edc33c5..f6355bcf4 100644 --- a/cmd/observe/flows_filter.go +++ b/cmd/observe/flows_filter.go @@ -129,6 +129,7 @@ func newFlowFilter() *flowFilter { {"node-name"}, {"tcp-flags"}, {"uuid"}, + {"traffic-direction"}, }, } } @@ -555,6 +556,21 @@ func (of *flowFilter) set(f *filterTracker, name, val string, track bool) error f.apply(func(f *flowpb.FlowFilter) { f.TcpFlags = append(f.TcpFlags, flags) }) + + // traffic direction filter + case "traffic-direction": + switch td := strings.ToLower(val); td { + case "ingress": + f.apply(func(f *flowpb.FlowFilter) { + f.TrafficDirection = append(f.TrafficDirection, flowpb.TrafficDirection_INGRESS) + }) + case "egress": + f.apply(func(f *flowpb.FlowFilter) { + f.TrafficDirection = append(f.TrafficDirection, flowpb.TrafficDirection_EGRESS) + }) + default: + return fmt.Errorf("%s: invalid traffic direction, expected ingress or egress", td) + } } return nil diff --git a/cmd/observe/flows_filter_test.go b/cmd/observe/flows_filter_test.go index e90946a2d..06dd774e4 100644 --- a/cmd/observe/flows_filter_test.go +++ b/cmd/observe/flows_filter_test.go @@ -433,3 +433,62 @@ func TestUuid(t *testing.T) { } assert.Nil(t, f.blacklist) } + +func TestTrafficDirection(t *testing.T) { + tt := []struct { + name string + flags []string + filters []*flowpb.FlowFilter + err string + }{ + { + name: "ingress", + flags: []string{"--traffic-direction", "ingress"}, + filters: []*flowpb.FlowFilter{ + {TrafficDirection: []flowpb.TrafficDirection{flowpb.TrafficDirection_INGRESS}}, + }, + }, + { + name: "egress", + flags: []string{"--traffic-direction", "egress"}, + filters: []*flowpb.FlowFilter{ + {TrafficDirection: []flowpb.TrafficDirection{flowpb.TrafficDirection_EGRESS}}, + }, + }, + { + name: "mixed case", + flags: []string{"--traffic-direction", "INGRESS", "--traffic-direction", "EgrEss"}, + filters: []*flowpb.FlowFilter{ + { + TrafficDirection: []flowpb.TrafficDirection{ + flowpb.TrafficDirection_INGRESS, + flowpb.TrafficDirection_EGRESS, + }, + }, + }, + }, + { + name: "invalid", + flags: []string{"--traffic-direction", "to the moon"}, + err: "to the moon: invalid traffic direction, expected ingress or egress", + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + f := newFlowFilter() + cmd := newFlowsCmdWithFilter(viper.New(), f) + err := cmd.Flags().Parse(tc.flags) + diff := cmp.Diff(tc.filters, f.whitelist.flowFilters(), cmpopts.IgnoreUnexported(flowpb.FlowFilter{})) + if diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + if tc.err != "" { + assert.Errorf(t, err, tc.err) + } else { + assert.NoError(t, err) + } + assert.Nil(t, f.blacklist) + }) + } +} diff --git a/cmd/observe_help.txt b/cmd/observe_help.txt index f6d3bbe1e..7d44c6dc2 100644 --- a/cmd/observe_help.txt +++ b/cmd/observe_help.txt @@ -38,65 +38,66 @@ Selectors Flags: Filters Flags: - --fqdn filter Show all flows related to the given fully qualified domain name (e.g. "*.cilium.io"). - --from-fqdn filter Show all flows originating at the given fully qualified domain name (e.g. "*.cilium.io"). - --from-identity filter Show all flows originating at an endpoint with the given security identity - --from-ip filter Show all flows originating at the given IP address. Each of the source IPs can be specified as an exact match (e.g. '1.1.1.1') or as a CIDR range (e.g.'1.1.1.0/24'). - --from-label filter Show only flows originating in an endpoint with the given labels (e.g. "key1=value1", "reserved:world") - --from-namespace filter Show all flows originating in the given Kubernetes namespace. - --from-pod filter Show all flows originating in the given pod name prefix([namespace/]). If namespace is not provided, 'default' is used - --from-port filter Show only flows with the given source port (e.g. 8080) - --from-service filter Shows flows where the source IP address matches the ClusterIP address of the given service name prefix([namespace/]). If namespace is not provided, 'default' is used - --from-workload filter Show all flows originating at an endpoint with the given workload - --http-method filter Show only flows which match this HTTP method (e.g. "get", "post") - --http-path filter Show only flows which match this HTTP path regular expressions (e.g. "/page/\\d+") - --http-status filter Show only flows which match this HTTP status code prefix (e.g. "404", "5+") - --identity filter Show all flows related to an endpoint with the given security identity - --ip filter Show all flows related to the given IP address. Each of the IPs can be specified as an exact match (e.g. '1.1.1.1') or as a CIDR range (e.g.'1.1.1.0/24'). - --ip-version filter Show only IPv4, IPv6 flows or non IP flows (e.g. ARP packets) (ie: "none", "v4", "v6") - -4, --ipv4 filter[=v4] Show only IPv4 flows - -6, --ipv6 filter[=v6] Show only IPv6 flows - -l, --label filter Show only flows related to an endpoint with the given labels (e.g. "key1=value1", "reserved:world") - -n, --namespace filter Show all flows related to the given Kubernetes namespace. - --node-name filter Show all flows which match the given node names (e.g. "k8s*", "test-cluster/*.company.com") - --not filter[=true] Reverses the next filter to be blacklist i.e. --not --from-ip 2.2.2.2 - --pod filter Show all flows related to the given pod name prefix ([namespace/]). If namespace is not provided, 'default' is used. - --port filter Show only flows with given port in either source or destination (e.g. 8080) - --protocol filter Show only flows which match the given L4/L7 flow protocol (e.g. "udp", "http") - --service filter Shows flows where either the source or destination IP address matches the ClusterIP address of the given service name prefix ([namespace/]). If namespace is not provided, 'default' is used. - --tcp-flags filter Show only flows which match the given TCP flags (e.g. "syn", "ack", "fin") - --to-fqdn filter Show all flows terminating at the given fully qualified domain name (e.g. "*.cilium.io"). - --to-identity filter Show all flows terminating at an endpoint with the given security identity - --to-ip filter Show all flows terminating at the given IP address. Each of the destination IPs can be specified as an exact match (e.g. '1.1.1.1') or as a CIDR range (e.g.'1.1.1.0/24'). - --to-label filter Show only flows terminating in an endpoint with given labels (e.g. "key1=value1", "reserved:world") - --to-namespace filter Show all flows terminating in the given Kubernetes namespace. - --to-pod filter Show all flows terminating in the given pod name prefix([namespace/]). If namespace is not provided, 'default' is used - --to-port filter Show only flows with the given destination port (e.g. 8080) - --to-service filter Shows flows where the destination IP address matches the ClusterIP address of the given service name prefix ([namespace/]). If namespace is not provided, 'default' is used - --to-workload filter Show all flows terminating at an endpoint with the given workload - --trace-id filter Show only flows which match this trace ID - -t, --type filter Filter by event types TYPE[:SUBTYPE]. Available types and subtypes: - TYPE SUBTYPE - capture n/a - drop n/a - l7 n/a - policy-verdict n/a - trace from-endpoint - from-host - from-network - from-overlay - from-proxy - from-stack - to-endpoint - to-host - to-network - to-overlay - to-proxy - to-stack - trace-sock n/a - --uuid filter Show the only flow matching this unique flow identifier, if any - --verdict filter Show only flows with this verdict [FORWARDED, DROPPED, AUDIT, REDIRECTED, ERROR, TRACED, TRANSLATED] - --workload filter Show all flows related to an endpoint with the given workload + --fqdn filter Show all flows related to the given fully qualified domain name (e.g. "*.cilium.io"). + --from-fqdn filter Show all flows originating at the given fully qualified domain name (e.g. "*.cilium.io"). + --from-identity filter Show all flows originating at an endpoint with the given security identity + --from-ip filter Show all flows originating at the given IP address. Each of the source IPs can be specified as an exact match (e.g. '1.1.1.1') or as a CIDR range (e.g.'1.1.1.0/24'). + --from-label filter Show only flows originating in an endpoint with the given labels (e.g. "key1=value1", "reserved:world") + --from-namespace filter Show all flows originating in the given Kubernetes namespace. + --from-pod filter Show all flows originating in the given pod name prefix([namespace/]). If namespace is not provided, 'default' is used + --from-port filter Show only flows with the given source port (e.g. 8080) + --from-service filter Shows flows where the source IP address matches the ClusterIP address of the given service name prefix([namespace/]). If namespace is not provided, 'default' is used + --from-workload filter Show all flows originating at an endpoint with the given workload + --http-method filter Show only flows which match this HTTP method (e.g. "get", "post") + --http-path filter Show only flows which match this HTTP path regular expressions (e.g. "/page/\\d+") + --http-status filter Show only flows which match this HTTP status code prefix (e.g. "404", "5+") + --identity filter Show all flows related to an endpoint with the given security identity + --ip filter Show all flows related to the given IP address. Each of the IPs can be specified as an exact match (e.g. '1.1.1.1') or as a CIDR range (e.g.'1.1.1.0/24'). + --ip-version filter Show only IPv4, IPv6 flows or non IP flows (e.g. ARP packets) (ie: "none", "v4", "v6") + -4, --ipv4 filter[=v4] Show only IPv4 flows + -6, --ipv6 filter[=v6] Show only IPv6 flows + -l, --label filter Show only flows related to an endpoint with the given labels (e.g. "key1=value1", "reserved:world") + -n, --namespace filter Show all flows related to the given Kubernetes namespace. + --node-name filter Show all flows which match the given node names (e.g. "k8s*", "test-cluster/*.company.com") + --not filter[=true] Reverses the next filter to be blacklist i.e. --not --from-ip 2.2.2.2 + --pod filter Show all flows related to the given pod name prefix ([namespace/]). If namespace is not provided, 'default' is used. + --port filter Show only flows with given port in either source or destination (e.g. 8080) + --protocol filter Show only flows which match the given L4/L7 flow protocol (e.g. "udp", "http") + --service filter Shows flows where either the source or destination IP address matches the ClusterIP address of the given service name prefix ([namespace/]). If namespace is not provided, 'default' is used. + --tcp-flags filter Show only flows which match the given TCP flags (e.g. "syn", "ack", "fin") + --to-fqdn filter Show all flows terminating at the given fully qualified domain name (e.g. "*.cilium.io"). + --to-identity filter Show all flows terminating at an endpoint with the given security identity + --to-ip filter Show all flows terminating at the given IP address. Each of the destination IPs can be specified as an exact match (e.g. '1.1.1.1') or as a CIDR range (e.g.'1.1.1.0/24'). + --to-label filter Show only flows terminating in an endpoint with given labels (e.g. "key1=value1", "reserved:world") + --to-namespace filter Show all flows terminating in the given Kubernetes namespace. + --to-pod filter Show all flows terminating in the given pod name prefix([namespace/]). If namespace is not provided, 'default' is used + --to-port filter Show only flows with the given destination port (e.g. 8080) + --to-service filter Shows flows where the destination IP address matches the ClusterIP address of the given service name prefix ([namespace/]). If namespace is not provided, 'default' is used + --to-workload filter Show all flows terminating at an endpoint with the given workload + --trace-id filter Show only flows which match this trace ID + --traffic-direction filter Show all flows in the given traffic direction (either ingress or egress) + -t, --type filter Filter by event types TYPE[:SUBTYPE]. Available types and subtypes: + TYPE SUBTYPE + capture n/a + drop n/a + l7 n/a + policy-verdict n/a + trace from-endpoint + from-host + from-network + from-overlay + from-proxy + from-stack + to-endpoint + to-host + to-network + to-overlay + to-proxy + to-stack + trace-sock n/a + --uuid filter Show the only flow matching this unique flow identifier, if any + --verdict filter Show only flows with this verdict [FORWARDED, DROPPED, AUDIT, REDIRECTED, ERROR, TRACED, TRANSLATED] + --workload filter Show all flows related to an endpoint with the given workload Raw-Filters Flags: --allowlist stringArray Specify allowlist as JSON encoded FlowFilters