github.com/cilium/cilium@v1.16.2/pkg/hubble/dropeventemitter/dropeventemitter_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package dropeventemitter
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"testing"
    10  
    11  	flowpb "github.com/cilium/cilium/api/v1/flow"
    12  	"github.com/cilium/cilium/pkg/identity"
    13  	slim_corev1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/core/v1"
    14  	slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1"
    15  
    16  	"github.com/stretchr/testify/assert"
    17  )
    18  
    19  const (
    20  	fakePodName = "pod"
    21  	fakePodUid  = "79f04581-a0e7-4a42-a020-db51cf21a605"
    22  )
    23  
    24  func TestEndpointToString(t *testing.T) {
    25  	tests := []struct {
    26  		name     string
    27  		ip       string
    28  		endpoint *flowpb.Endpoint
    29  		expect   string
    30  	}{
    31  		{
    32  			name:     fakePodName,
    33  			ip:       "1.2.3.4",
    34  			endpoint: &flowpb.Endpoint{PodName: fakePodName, Namespace: "namespace"},
    35  			expect:   "namespace/pod (1.2.3.4)",
    36  		},
    37  		{
    38  			name:     "node",
    39  			ip:       "1.2.3.4",
    40  			endpoint: &flowpb.Endpoint{Identity: identity.ReservedIdentityRemoteNode.Uint32()},
    41  			expect:   identity.ReservedIdentityRemoteNode.String() + " (1.2.3.4)",
    42  		},
    43  		{
    44  			name:     "unknown",
    45  			ip:       "1.2.3.4",
    46  			endpoint: &flowpb.Endpoint{Identity: identity.MaxLocalIdentity.Uint32() + 1},
    47  			expect:   "1.2.3.4",
    48  		},
    49  	}
    50  	for _, tt := range tests {
    51  		t.Run(tt.name, func(t *testing.T) {
    52  			e := &DropEventEmitter{}
    53  			str := e.endpointToString(tt.ip, tt.endpoint)
    54  			assert.Equal(t, str, tt.expect)
    55  		})
    56  	}
    57  }
    58  
    59  func TestL4protocolToString(t *testing.T) {
    60  	tests := []struct {
    61  		name   string
    62  		l4     *flowpb.Layer4
    63  		expect string
    64  	}{
    65  		{
    66  			name:   "udp/512",
    67  			l4:     &flowpb.Layer4{Protocol: &flowpb.Layer4_UDP{UDP: &flowpb.UDP{DestinationPort: 512}}},
    68  			expect: "UDP/512",
    69  		},
    70  		{
    71  			name:   "tcp/443",
    72  			l4:     &flowpb.Layer4{Protocol: &flowpb.Layer4_TCP{TCP: &flowpb.TCP{DestinationPort: 443}}},
    73  			expect: "TCP/443",
    74  		},
    75  		{
    76  			name:   "unknown",
    77  			l4:     &flowpb.Layer4{},
    78  			expect: "",
    79  		},
    80  	}
    81  	for _, tt := range tests {
    82  		t.Run(tt.name, func(t *testing.T) {
    83  			e := &DropEventEmitter{}
    84  			str := e.l4protocolToString(tt.l4)
    85  			assert.Equal(t, str, tt.expect)
    86  		})
    87  	}
    88  }
    89  
    90  func TestProcessFlow(t *testing.T) {
    91  	tests := []struct {
    92  		name   string
    93  		flow   *flowpb.Flow
    94  		expect string
    95  	}{
    96  		{
    97  			name: "valid ingress drop event",
    98  			flow: &flowpb.Flow{
    99  				Verdict:          flowpb.Verdict_DROPPED,
   100  				DropReasonDesc:   flowpb.DropReason_POLICY_DENIED,
   101  				TrafficDirection: flowpb.TrafficDirection_INGRESS,
   102  				IP:               &flowpb.IP{Source: "1.2.3.4", Destination: "5.6.7.8"},
   103  				Source:           &flowpb.Endpoint{},
   104  				Destination:      &flowpb.Endpoint{Namespace: "namespace", PodName: fakePodName},
   105  				L4:               &flowpb.Layer4{Protocol: &flowpb.Layer4_TCP{TCP: &flowpb.TCP{DestinationPort: 443}}},
   106  			},
   107  			expect: "Incoming packet dropped (policy_denied) from unknown (1.2.3.4) TCP/443",
   108  		},
   109  		{
   110  			name: "valid egress drop event to node",
   111  			flow: &flowpb.Flow{
   112  				Verdict:          flowpb.Verdict_DROPPED,
   113  				DropReasonDesc:   flowpb.DropReason_POLICY_DENIED,
   114  				TrafficDirection: flowpb.TrafficDirection_EGRESS,
   115  				IP:               &flowpb.IP{Source: "1.2.3.4", Destination: "5.6.7.8"},
   116  				Source:           &flowpb.Endpoint{Namespace: "namespace", PodName: fakePodName},
   117  				Destination:      &flowpb.Endpoint{Identity: identity.ReservedIdentityRemoteNode.Uint32()},
   118  				L4:               &flowpb.Layer4{Protocol: &flowpb.Layer4_UDP{UDP: &flowpb.UDP{DestinationPort: 512}}},
   119  			},
   120  			expect: "Outgoing packet dropped (policy_denied) to remote-node (5.6.7.8) UDP/512",
   121  		},
   122  		{
   123  			name: "ingress drop event not matching reason",
   124  			flow: &flowpb.Flow{
   125  				Verdict:          flowpb.Verdict_DROPPED,
   126  				DropReasonDesc:   flowpb.DropReason_AUTH_REQUIRED,
   127  				TrafficDirection: flowpb.TrafficDirection_INGRESS,
   128  				IP:               &flowpb.IP{Source: "1.2.3.4", Destination: "5.6.7.8"},
   129  				Source:           &flowpb.Endpoint{},
   130  				Destination:      &flowpb.Endpoint{Namespace: "namespace", PodName: fakePodName},
   131  				L4:               &flowpb.Layer4{Protocol: &flowpb.Layer4_TCP{TCP: &flowpb.TCP{DestinationPort: 443}}},
   132  			},
   133  			expect: "",
   134  		},
   135  		{
   136  			name: "ingress verdict is not dropped",
   137  			flow: &flowpb.Flow{
   138  				Verdict:          flowpb.Verdict_ERROR,
   139  				DropReasonDesc:   flowpb.DropReason_POLICY_DENIED,
   140  				TrafficDirection: flowpb.TrafficDirection_INGRESS,
   141  				IP:               &flowpb.IP{Source: "1.2.3.4", Destination: "5.6.7.8"},
   142  				Source:           &flowpb.Endpoint{},
   143  				Destination:      &flowpb.Endpoint{Namespace: "namespace", PodName: fakePodName},
   144  				L4:               &flowpb.Layer4{Protocol: &flowpb.Layer4_TCP{TCP: &flowpb.TCP{DestinationPort: 443}}},
   145  			},
   146  			expect: "",
   147  		},
   148  		{
   149  			name: "ingress but no destination pod",
   150  			flow: &flowpb.Flow{
   151  				Verdict:          flowpb.Verdict_DROPPED,
   152  				DropReasonDesc:   flowpb.DropReason_POLICY_DENIED,
   153  				TrafficDirection: flowpb.TrafficDirection_INGRESS,
   154  				IP:               &flowpb.IP{Source: "1.2.3.4", Destination: "5.6.7.8"},
   155  				Source:           &flowpb.Endpoint{Namespace: "namespace", PodName: fakePodName},
   156  				Destination:      &flowpb.Endpoint{},
   157  				L4:               &flowpb.Layer4{Protocol: &flowpb.Layer4_TCP{TCP: &flowpb.TCP{DestinationPort: 443}}},
   158  			},
   159  			expect: "",
   160  		},
   161  		{
   162  			name: "egress but no source pod",
   163  			flow: &flowpb.Flow{
   164  				Verdict:          flowpb.Verdict_DROPPED,
   165  				DropReasonDesc:   flowpb.DropReason_POLICY_DENIED,
   166  				TrafficDirection: flowpb.TrafficDirection_EGRESS,
   167  				IP:               &flowpb.IP{Source: "1.2.3.4", Destination: "5.6.7.8"},
   168  				Source:           &flowpb.Endpoint{},
   169  				Destination:      &flowpb.Endpoint{},
   170  				L4:               &flowpb.Layer4{Protocol: &flowpb.Layer4_TCP{TCP: &flowpb.TCP{DestinationPort: 443}}},
   171  			},
   172  			expect: "",
   173  		},
   174  	}
   175  	for _, tt := range tests {
   176  		t.Run(tt.name, func(t *testing.T) {
   177  			fakeRecorder := &FakeRecorder{
   178  				Events:        make(chan string, 3),
   179  				IncludeObject: true,
   180  			}
   181  			e := &DropEventEmitter{
   182  				reasons:    []string{"policy_denied"},
   183  				recorder:   fakeRecorder,
   184  				k8sWatcher: &fakeK8SWatcher{},
   185  			}
   186  			if err := e.ProcessFlow(context.Background(), tt.flow); err != nil {
   187  				t.Errorf("DropEventEmitter.ProcessFlow() error = %v", err)
   188  			}
   189  			if tt.expect == "" {
   190  				assert.Len(t, fakeRecorder.Events, 0)
   191  			} else {
   192  				assert.Len(t, fakeRecorder.Events, 1)
   193  				event := <-fakeRecorder.Events
   194  				assert.Contains(t, event, tt.expect)
   195  				if tt.flow.Destination.PodName == fakePodName && tt.flow.TrafficDirection == flowpb.TrafficDirection_EGRESS {
   196  					assert.Contains(t, event, fakePodUid)
   197  				}
   198  			}
   199  		})
   200  	}
   201  }
   202  
   203  type fakeK8SWatcher struct {
   204  }
   205  
   206  func (k *fakeK8SWatcher) GetCachedNamespace(namespace string) (*slim_corev1.Namespace, error) {
   207  	return nil, nil
   208  }
   209  func (k *fakeK8SWatcher) GetCachedPod(namespace, name string) (*slim_corev1.Pod, error) {
   210  	if name == fakePodName {
   211  		return &slim_corev1.Pod{
   212  			ObjectMeta: slim_metav1.ObjectMeta{
   213  				Name: fakePodName,
   214  				UID:  fakePodUid,
   215  			},
   216  		}, nil
   217  	}
   218  	return nil, fmt.Errorf("pod not found in cache : %s", name)
   219  }