github.com/crowdsecurity/crowdsec@v1.6.1/pkg/acquisition/modules/kubernetesaudit/k8s_audit_test.go (about) 1 package kubernetesauditacquisition 2 3 import ( 4 "net/http/httptest" 5 "strings" 6 "testing" 7 "time" 8 9 "github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration" 10 "github.com/crowdsecurity/crowdsec/pkg/types" 11 log "github.com/sirupsen/logrus" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 "gopkg.in/tomb.v2" 15 ) 16 17 func TestBadConfiguration(t *testing.T) { 18 tests := []struct { 19 config string 20 name string 21 expectedErr string 22 }{ 23 { 24 name: "unknown field", 25 config: `source: k8s-audit 26 foobar: asd.log`, 27 expectedErr: "line 2: field foobar not found in type kubernetesauditacquisition.KubernetesAuditConfiguration", 28 }, 29 { 30 name: "missing listen_addr", 31 config: `source: k8s-audit`, 32 expectedErr: "listen_addr cannot be empty", 33 }, 34 { 35 name: "missing listen_port", 36 config: `source: k8s-audit 37 listen_addr: 0.0.0.0`, 38 expectedErr: "listen_port cannot be empty", 39 }, 40 } 41 42 for _, test := range tests { 43 t.Run(test.name, func(t *testing.T) { 44 f := KubernetesAuditSource{} 45 46 err := f.UnmarshalConfig([]byte(test.config)) 47 48 assert.Contains(t, err.Error(), test.expectedErr) 49 50 }) 51 } 52 } 53 54 func TestInvalidConfig(t *testing.T) { 55 tests := []struct { 56 name string 57 config string 58 expectedErr string 59 }{ 60 { 61 name: "invalid_port", 62 config: `source: k8s-audit 63 listen_addr: 127.0.0.1 64 listen_port: 9999999 65 webhook_path: /k8s-audit`, 66 expectedErr: "listen tcp: address 9999999: invalid port", 67 }, 68 } 69 70 subLogger := log.WithFields(log.Fields{ 71 "type": "k8s-audit", 72 }) 73 74 for _, test := range tests { 75 t.Run(test.name, func(t *testing.T) { 76 out := make(chan types.Event) 77 tb := &tomb.Tomb{} 78 79 f := KubernetesAuditSource{} 80 81 err := f.UnmarshalConfig([]byte(test.config)) 82 83 require.NoError(t, err) 84 85 err = f.Configure([]byte(test.config), subLogger, configuration.METRICS_NONE) 86 87 require.NoError(t, err) 88 f.StreamingAcquisition(out, tb) 89 90 time.Sleep(1 * time.Second) 91 tb.Kill(nil) 92 err = tb.Wait() 93 if test.expectedErr != "" { 94 require.ErrorContains(t, err, test.expectedErr) 95 return 96 } 97 require.NoError(t, err) 98 }) 99 } 100 } 101 102 func TestHandler(t *testing.T) { 103 tests := []struct { 104 name string 105 config string 106 expectedStatusCode int 107 body string 108 method string 109 eventCount int 110 }{ 111 { 112 name: "valid_json", 113 config: `source: k8s-audit 114 listen_addr: 127.0.0.1 115 listen_port: 49234 116 webhook_path: /k8s-audit`, 117 method: "POST", 118 expectedStatusCode: 200, 119 body: ` 120 { 121 "Items": [ 122 { 123 "Level": "RequestResponse", 124 "AuditID": "2fca7950-03b6-41fa-95cd-08c5bcec8487", 125 "Stage": "ResponseComplete", 126 "RequestURI": "/api/v1/namespaces/default/pods?fieldManager=kubectl-client-side-apply\u0026fieldValidation=Strict", 127 "Verb": "create", 128 "User": { 129 "username": "minikube-user", 130 "groups": [ 131 "system:masters", 132 "system:authenticated" 133 ] 134 }, 135 "ImpersonatedUser": null, 136 "SourceIPs": [ 137 "192.168.9.212" 138 ], 139 "UserAgent": "kubectl.exe/v1.25.2 (windows/amd64) kubernetes/5835544", 140 "ObjectRef": { 141 "Resource": "pods", 142 "Namespace": "default", 143 "Name": "test-pod-hostpath", 144 "UID": "", 145 "APIGroup": "", 146 "APIVersion": "v1", 147 "ResourceVersion": "", 148 "Subresource": "" 149 }, 150 "ResponseStatus": { 151 "metadata": {}, 152 "code": 201 153 }, 154 "RequestObject": {}, 155 "ResponseObject": {}, 156 "RequestReceivedTimestamp": "2022-09-26T15:24:52.316938Z", 157 "StageTimestamp": "2022-09-26T15:24:52.322575Z", 158 "Annotations": { 159 "authorization.k8s.io/decision": "allow", 160 "authorization.k8s.io/reason": "", 161 "pod-security.kubernetes.io/enforce-policy": "privileged:latest" 162 } 163 }, 164 { 165 "Level": "RequestResponse", 166 "AuditID": "2fca7950-03b6-41fa-95cd-08c5bcec8487", 167 "Stage": "ResponseComplete", 168 "RequestURI": "/api/v1/namespaces/default/pods?fieldManager=kubectl-client-side-apply\u0026fieldValidation=Strict", 169 "Verb": "create", 170 "User": { 171 "username": "minikube-user", 172 "groups": [ 173 "system:masters", 174 "system:authenticated" 175 ] 176 }, 177 "ImpersonatedUser": null, 178 "SourceIPs": [ 179 "192.168.9.212" 180 ], 181 "UserAgent": "kubectl.exe/v1.25.2 (windows/amd64) kubernetes/5835544", 182 "ObjectRef": { 183 "Resource": "pods", 184 "Namespace": "default", 185 "Name": "test-pod-hostpath", 186 "UID": "", 187 "APIGroup": "", 188 "APIVersion": "v1", 189 "ResourceVersion": "", 190 "Subresource": "" 191 }, 192 "ResponseStatus": { 193 "metadata": {}, 194 "code": 201 195 }, 196 "RequestObject": {}, 197 "ResponseObject": {}, 198 "RequestReceivedTimestamp": "2022-09-26T15:24:52.316938Z", 199 "StageTimestamp": "2022-09-26T15:24:52.322575Z", 200 "Annotations": { 201 "authorization.k8s.io/decision": "allow", 202 "authorization.k8s.io/reason": "", 203 "pod-security.kubernetes.io/enforce-policy": "privileged:latest" 204 } 205 } 206 ] 207 }`, 208 eventCount: 2, 209 }, 210 { 211 name: "invalid_json", 212 config: `source: k8s-audit 213 listen_addr: 127.0.0.1 214 listen_port: 49234 215 webhook_path: /k8s-audit`, 216 expectedStatusCode: 500, 217 body: "invalid json", 218 method: "POST", 219 eventCount: 0, 220 }, 221 { 222 name: "invalid_method", 223 config: `source: k8s-audit 224 listen_addr: 127.0.0.1 225 listen_port: 49234 226 webhook_path: /k8s-audit`, 227 expectedStatusCode: 405, 228 method: "GET", 229 eventCount: 0, 230 }, 231 } 232 233 subLogger := log.WithFields(log.Fields{ 234 "type": "k8s-audit", 235 }) 236 237 for _, test := range tests { 238 t.Run(test.name, func(t *testing.T) { 239 out := make(chan types.Event) 240 tb := &tomb.Tomb{} 241 eventCount := 0 242 243 tb.Go(func() error { 244 for { 245 select { 246 case <-out: 247 eventCount++ 248 case <-tb.Dying(): 249 return nil 250 } 251 } 252 }) 253 254 f := KubernetesAuditSource{} 255 err := f.UnmarshalConfig([]byte(test.config)) 256 require.NoError(t, err) 257 err = f.Configure([]byte(test.config), subLogger, configuration.METRICS_NONE) 258 259 require.NoError(t, err) 260 261 req := httptest.NewRequest(test.method, "/k8s-audit", strings.NewReader(test.body)) 262 w := httptest.NewRecorder() 263 264 f.StreamingAcquisition(out, tb) 265 266 f.webhookHandler(w, req) 267 268 res := w.Result() 269 270 assert.Equal(t, test.expectedStatusCode, res.StatusCode) 271 //time.Sleep(1 * time.Second) 272 require.NoError(t, err) 273 274 tb.Kill(nil) 275 err = tb.Wait() 276 require.NoError(t, err) 277 278 assert.Equal(t, test.eventCount, eventCount) 279 }) 280 } 281 }