github.com/cilium/cilium@v1.16.2/pkg/hubble/parser/seven/http_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Hubble 3 4 package seven 5 6 import ( 7 "fmt" 8 "net/http" 9 "net/netip" 10 "net/url" 11 "testing" 12 "time" 13 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 17 flowpb "github.com/cilium/cilium/api/v1/flow" 18 "github.com/cilium/cilium/pkg/hubble/defaults" 19 "github.com/cilium/cilium/pkg/hubble/parser/getters" 20 "github.com/cilium/cilium/pkg/hubble/parser/options" 21 "github.com/cilium/cilium/pkg/hubble/testutils" 22 "github.com/cilium/cilium/pkg/ipcache" 23 "github.com/cilium/cilium/pkg/proxy/accesslog" 24 "github.com/cilium/cilium/pkg/u8proto" 25 ) 26 27 func TestDecodeL7HTTPRequest(t *testing.T) { 28 requestPath, err := url.Parse("http://myhost/some/path") 29 require.NoError(t, err) 30 lr := &accesslog.LogRecord{ 31 Type: accesslog.TypeRequest, 32 Timestamp: fakeTimestamp, 33 NodeAddressInfo: fakeNodeInfo, 34 ObservationPoint: accesslog.Ingress, 35 SourceEndpoint: fakeSourceEndpoint, 36 DestinationEndpoint: fakeDestinationEndpoint, 37 IPVersion: accesslog.VersionIPv4, 38 Verdict: accesslog.VerdictForwarded, 39 TransportProtocol: accesslog.TransportProtocol(u8proto.TCP), 40 ServiceInfo: nil, 41 DropReason: nil, 42 HTTP: &accesslog.LogRecordHTTP{ 43 Code: 0, 44 Method: "POST", 45 URL: requestPath, 46 Protocol: "HTTP/1.1", 47 Headers: http.Header{ 48 "Host": {"myhost"}, 49 "Traceparent": {"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}, 50 }, 51 }, 52 } 53 lr.SourceEndpoint.Port = 56789 54 lr.DestinationEndpoint.Port = 80 55 56 dnsGetter := &testutils.FakeFQDNCache{ 57 OnGetNamesOf: func(epID uint32, ip netip.Addr) (names []string) { 58 ipStr := ip.String() 59 switch { 60 case epID == uint32(fakeSourceEndpoint.ID) && ipStr == fakeDestinationEndpoint.IPv4: 61 return []string{"endpoint-1234"} 62 case epID == uint32(fakeDestinationEndpoint.ID) && ipStr == fakeSourceEndpoint.IPv4: 63 return []string{"endpoint-4321"} 64 } 65 return nil 66 }, 67 } 68 IPGetter := &testutils.FakeIPGetter{ 69 OnGetK8sMetadata: func(ip netip.Addr) *ipcache.K8sMetadata { 70 if ip == netip.MustParseAddr(fakeDestinationEndpoint.IPv4) { 71 return &ipcache.K8sMetadata{ 72 Namespace: "default", 73 PodName: "pod-1234", 74 } 75 } 76 return nil 77 }, 78 } 79 serviceGetter := &testutils.FakeServiceGetter{ 80 OnGetServiceByAddr: func(ip netip.Addr, port uint16) *flowpb.Service { 81 if ip == netip.MustParseAddr(fakeDestinationEndpoint.IPv4) && (port == fakeDestinationEndpoint.Port) { 82 return &flowpb.Service{ 83 Name: "service-1234", 84 Namespace: "default", 85 } 86 } 87 return nil 88 }, 89 } 90 endpointGetter := &testutils.FakeEndpointGetter{ 91 OnGetEndpointInfo: func(ip netip.Addr) (endpoint getters.EndpointInfo, ok bool) { 92 switch { 93 case ip == netip.MustParseAddr(fakeSourceEndpoint.IPv4): 94 return &testutils.FakeEndpointInfo{ 95 ID: fakeSourceEndpoint.ID, 96 }, true 97 case ip == netip.MustParseAddr(fakeDestinationEndpoint.IPv4): 98 return &testutils.FakeEndpointInfo{ 99 ID: fakeDestinationEndpoint.ID, 100 }, true 101 } 102 return nil, false 103 }, 104 } 105 106 parser, err := New(log, dnsGetter, IPGetter, serviceGetter, endpointGetter) 107 require.NoError(t, err) 108 109 f := &flowpb.Flow{} 110 err = parser.Decode(lr, f) 111 require.NoError(t, err) 112 113 assert.Equal(t, fakeSourceEndpoint.IPv4, f.GetIP().GetSource()) 114 assert.Equal(t, uint32(56789), f.GetL4().GetTCP().GetSourcePort()) 115 assert.Equal(t, []string{"endpoint-4321"}, f.GetSourceNames()) 116 assert.Equal(t, fakeSourceEndpoint.Labels.GetModel(), f.GetSource().GetLabels()) 117 assert.Equal(t, "", f.GetSource().GetNamespace()) 118 assert.Equal(t, "", f.GetSource().GetPodName()) 119 assert.Equal(t, "", f.GetSourceService().GetNamespace()) 120 assert.Equal(t, "", f.GetSourceService().GetName()) 121 122 assert.Equal(t, fakeDestinationEndpoint.IPv4, f.GetIP().GetDestination()) 123 assert.Equal(t, uint32(80), f.GetL4().GetTCP().GetDestinationPort()) 124 assert.Equal(t, []string{"endpoint-1234"}, f.GetDestinationNames()) 125 assert.Equal(t, fakeDestinationEndpoint.Labels.GetModel(), f.GetDestination().GetLabels()) 126 assert.Equal(t, "default", f.GetDestination().GetNamespace()) 127 assert.Equal(t, "pod-1234", f.GetDestination().GetPodName()) 128 assert.Equal(t, "default", f.GetDestinationService().GetNamespace()) 129 assert.Equal(t, "service-1234", f.GetDestinationService().GetName()) 130 131 assert.Equal(t, flowpb.Verdict_FORWARDED, f.GetVerdict()) 132 133 assert.Equal(t, &flowpb.HTTP{ 134 Code: 0, 135 Method: "POST", 136 Url: "http://myhost/some/path", 137 Protocol: "HTTP/1.1", 138 Headers: []*flowpb.HTTPHeader{ 139 {Key: "Host", Value: "myhost"}, 140 {Key: "Traceparent", Value: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}, 141 }, 142 }, f.GetL7().GetHttp()) 143 assert.Equal(t, "4bf92f3577b34da6a3ce929d0e0e4736", f.GetTraceContext().GetParent().GetTraceId()) 144 } 145 146 func TestDecodeL7HTTPRecordResponse(t *testing.T) { 147 requestPath, err := url.Parse("http://myhost/some/path") 148 require.NoError(t, err) 149 lr := &accesslog.LogRecord{ 150 Type: accesslog.TypeResponse, 151 Timestamp: fakeTimestamp, 152 NodeAddressInfo: fakeNodeInfo, 153 ObservationPoint: accesslog.Ingress, 154 SourceEndpoint: fakeDestinationEndpoint, 155 DestinationEndpoint: fakeSourceEndpoint, 156 IPVersion: accesslog.VersionIPv4, 157 Verdict: accesslog.VerdictForwarded, 158 TransportProtocol: accesslog.TransportProtocol(u8proto.TCP), 159 ServiceInfo: nil, 160 DropReason: nil, 161 HTTP: &accesslog.LogRecordHTTP{ 162 Code: 404, 163 Method: "POST", 164 URL: requestPath, 165 Protocol: "HTTP/1.1", 166 }, 167 } 168 lr.SourceEndpoint.Port = 80 169 lr.DestinationEndpoint.Port = 56789 170 171 dnsGetter := &testutils.FakeFQDNCache{ 172 OnGetNamesOf: func(epID uint32, ip netip.Addr) (names []string) { 173 ipStr := ip.String() 174 switch { 175 case epID == uint32(fakeSourceEndpoint.ID) && ipStr == fakeDestinationEndpoint.IPv4: 176 return []string{"endpoint-1234"} 177 case epID == uint32(fakeDestinationEndpoint.ID) && ipStr == fakeSourceEndpoint.IPv4: 178 return []string{"endpoint-4321"} 179 } 180 return nil 181 }, 182 } 183 IPGetter := &testutils.FakeIPGetter{ 184 OnGetK8sMetadata: func(ip netip.Addr) *ipcache.K8sMetadata { 185 if ip == netip.MustParseAddr(fakeDestinationEndpoint.IPv4) { 186 return &ipcache.K8sMetadata{ 187 Namespace: "default", 188 PodName: "pod-1234", 189 } 190 } 191 return nil 192 }, 193 } 194 serviceGetter := &testutils.FakeServiceGetter{ 195 OnGetServiceByAddr: func(ip netip.Addr, port uint16) *flowpb.Service { 196 if ip == netip.MustParseAddr(fakeDestinationEndpoint.IPv4) && (port == fakeDestinationEndpoint.Port) { 197 return &flowpb.Service{ 198 Name: "service-1234", 199 Namespace: "default", 200 } 201 } 202 return nil 203 }, 204 } 205 endpointGetter := &testutils.FakeEndpointGetter{ 206 OnGetEndpointInfo: func(ip netip.Addr) (endpoint getters.EndpointInfo, ok bool) { 207 switch { 208 case ip.String() == fakeSourceEndpoint.IPv4: 209 return &testutils.FakeEndpointInfo{ 210 ID: fakeSourceEndpoint.ID, 211 }, true 212 case ip.String() == fakeDestinationEndpoint.IPv4: 213 return &testutils.FakeEndpointInfo{ 214 ID: fakeDestinationEndpoint.ID, 215 }, true 216 } 217 return nil, false 218 }, 219 } 220 221 parser, err := New(log, dnsGetter, IPGetter, serviceGetter, endpointGetter) 222 require.NoError(t, err) 223 224 f := &flowpb.Flow{} 225 err = parser.Decode(lr, f) 226 require.NoError(t, err) 227 228 assert.Equal(t, fakeSourceEndpoint.IPv4, f.GetIP().GetDestination()) 229 assert.Equal(t, uint32(56789), f.GetL4().GetTCP().GetDestinationPort()) 230 assert.Equal(t, []string{"endpoint-4321"}, f.GetDestinationNames()) 231 assert.Equal(t, fakeSourceEndpoint.Labels.GetModel(), f.GetDestination().GetLabels()) 232 assert.Equal(t, "", f.GetDestination().GetNamespace()) 233 assert.Equal(t, "", f.GetDestination().GetPodName()) 234 assert.Equal(t, "", f.GetDestinationService().GetNamespace()) 235 assert.Equal(t, "", f.GetDestinationService().GetName()) 236 237 assert.Equal(t, fakeDestinationEndpoint.IPv4, f.GetIP().GetSource()) 238 assert.Equal(t, uint32(80), f.GetL4().GetTCP().GetSourcePort()) 239 assert.Equal(t, []string{"endpoint-1234"}, f.GetSourceNames()) 240 assert.Equal(t, fakeDestinationEndpoint.Labels.GetModel(), f.GetSource().GetLabels()) 241 assert.Equal(t, "default", f.GetSource().GetNamespace()) 242 assert.Equal(t, "pod-1234", f.GetSource().GetPodName()) 243 assert.Equal(t, "default", f.GetSourceService().GetNamespace()) 244 assert.Equal(t, "service-1234", f.GetSourceService().GetName()) 245 246 assert.Equal(t, flowpb.Verdict_FORWARDED, f.GetVerdict()) 247 248 assert.Equal(t, &flowpb.HTTP{ 249 Code: 404, 250 Method: "POST", 251 Url: "http://myhost/some/path", 252 Protocol: "HTTP/1.1", 253 }, f.GetL7().GetHttp()) 254 } 255 256 func TestDecodeL7HTTPResponseTime(t *testing.T) { 257 requestID := "req-id" 258 headers := http.Header{} 259 headers.Add("X-Request-Id", requestID) 260 httpRecord := &accesslog.LogRecordHTTP{ 261 Code: 200, 262 Headers: headers, 263 Method: "GET", 264 Protocol: "HTTP/1.1", 265 URL: &url.URL{ 266 Scheme: "http", 267 Host: "example.com", 268 Path: "/", 269 }, 270 } 271 requestTimestamp := time.Unix(0, 0).Format(time.RFC3339Nano) 272 responseTimestamp := time.Unix(1, 0).Format(time.RFC3339Nano) 273 274 parser, err := New(log, nil, nil, nil, nil) 275 require.NoError(t, err) 276 277 request := &accesslog.LogRecord{ 278 Type: accesslog.TypeRequest, 279 Timestamp: requestTimestamp, 280 HTTP: httpRecord, 281 } 282 283 response := &accesslog.LogRecord{ 284 Type: accesslog.TypeResponse, 285 Timestamp: responseTimestamp, 286 HTTP: httpRecord, 287 } 288 289 f := &flowpb.Flow{} 290 err = parser.Decode(request, f) 291 require.NoError(t, err) 292 _, ok := parser.timestampCache.Get(requestID) 293 assert.True(t, ok, "request id should be in the cache") 294 295 f.Reset() 296 err = parser.Decode(response, f) 297 require.NoError(t, err) 298 assert.Equal(t, 1*time.Second, time.Duration(f.GetL7().GetLatencyNs())) 299 _, ok = parser.timestampCache.Get(requestID) 300 assert.False(t, ok, "request id should not be in the cache") 301 302 // it should handle the case where the request id is not in the cache for response type. 303 f = &flowpb.Flow{} 304 err = parser.Decode(response, f) 305 require.NoError(t, err) 306 assert.Equal(t, uint64(0), f.GetL7().GetLatencyNs()) 307 _, ok = parser.timestampCache.Get(requestID) 308 assert.False(t, ok, "request id should not be in the cache") 309 } 310 311 func TestGetL7HTTPResponseTraceID(t *testing.T) { 312 requestID := "req-id" 313 requestRecord := &accesslog.LogRecordHTTP{ 314 Method: "GET", 315 Protocol: "HTTP/1.1", 316 URL: &url.URL{ 317 Scheme: "http", 318 Host: "example.com", 319 Path: "/", 320 }, 321 Headers: http.Header{ 322 "X-Request-Id": {requestID}, 323 "Host": {"myhost"}, 324 "Traceparent": {"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}, 325 }, 326 } 327 responseRecord := &accesslog.LogRecordHTTP{ 328 Code: 200, 329 Method: "GET", 330 Protocol: "HTTP/1.1", 331 URL: &url.URL{ 332 Scheme: "http", 333 Host: "example.com", 334 Path: "/", 335 }, 336 Headers: http.Header{ 337 "X-Request-Id": {requestID}, 338 }, 339 } 340 requestTimestamp := time.Unix(0, 0).Format(time.RFC3339Nano) 341 responseTimestamp := time.Unix(1, 0).Format(time.RFC3339Nano) 342 343 parser, err := New(log, nil, nil, nil, nil) 344 require.NoError(t, err) 345 346 request := &accesslog.LogRecord{ 347 Type: accesslog.TypeRequest, 348 Timestamp: requestTimestamp, 349 HTTP: requestRecord, 350 } 351 352 response := &accesslog.LogRecord{ 353 Type: accesslog.TypeResponse, 354 Timestamp: responseTimestamp, 355 HTTP: responseRecord, 356 } 357 358 f := &flowpb.Flow{} 359 err = parser.Decode(request, f) 360 require.NoError(t, err) 361 _, ok := parser.traceContextCache.Get(requestID) 362 assert.True(t, ok, "request id should be in the cache") 363 364 f.Reset() 365 err = parser.Decode(response, f) 366 require.NoError(t, err) 367 assert.Equal(t, "4bf92f3577b34da6a3ce929d0e0e4736", f.GetTraceContext().GetParent().GetTraceId()) 368 _, ok = parser.traceContextCache.Get(requestID) 369 assert.False(t, ok, "request id should not be in the cache") 370 371 // it should handle the case where the request id is not in the cache for response type. 372 f = &flowpb.Flow{} 373 err = parser.Decode(response, f) 374 require.NoError(t, err) 375 // no requestID means no traceID for response 376 assert.Empty(t, f.GetTraceContext().GetParent().GetTraceId()) 377 _, ok = parser.traceContextCache.Get(requestID) 378 assert.False(t, ok, "request id should not be in the cache") 379 } 380 381 // see https://github.com/cilium/cilium/issues/31071 382 func TestDecodeL7HTTPWithInvalidURL(t *testing.T) { 383 requestPath, err := url.Parse("http://myhost/some/path") 384 require.NoError(t, err) 385 386 // mutate requestPath such as url.Parse(requestPath.String()) fails, which 387 // triggered the panic described in #31071. 388 requestPath.Host += "@" // invalid hostname 389 _, err = url.Parse(requestPath.String()) 390 require.Error(t, err) 391 392 lr := &accesslog.LogRecord{ 393 Type: accesslog.TypeRequest, 394 Timestamp: fakeTimestamp, 395 NodeAddressInfo: fakeNodeInfo, 396 ObservationPoint: accesslog.Ingress, 397 SourceEndpoint: fakeSourceEndpoint, 398 DestinationEndpoint: fakeDestinationEndpoint, 399 IPVersion: accesslog.VersionIPv4, 400 Verdict: accesslog.VerdictForwarded, 401 TransportProtocol: accesslog.TransportProtocol(u8proto.TCP), 402 ServiceInfo: nil, 403 DropReason: nil, 404 HTTP: &accesslog.LogRecordHTTP{ 405 Code: 0, 406 Method: "POST", 407 URL: requestPath, 408 Protocol: "HTTP/1.1", 409 Headers: http.Header{ 410 "Host": {"myhost"}, 411 "Traceparent": {"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}, 412 }, 413 }, 414 } 415 416 parser, err := New(log, nil, nil, nil, nil) 417 require.NoError(t, err) 418 419 f := &flowpb.Flow{} 420 err = parser.Decode(lr, f) 421 require.NoError(t, err) 422 423 assert.Equal(t, &flowpb.HTTP{ 424 Code: 0, 425 Method: "POST", 426 Url: "http://myhost%40/some/path", 427 Protocol: "HTTP/1.1", 428 Headers: []*flowpb.HTTPHeader{ 429 {Key: "Host", Value: "myhost"}, 430 {Key: "Traceparent", Value: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}, 431 }, 432 }, f.GetL7().GetHttp()) 433 assert.Equal(t, "4bf92f3577b34da6a3ce929d0e0e4736", f.GetTraceContext().GetParent().GetTraceId()) 434 } 435 436 func TestDecodeL7HTTPWithNilURL(t *testing.T) { 437 lr := &accesslog.LogRecord{ 438 Type: accesslog.TypeRequest, 439 Timestamp: fakeTimestamp, 440 NodeAddressInfo: fakeNodeInfo, 441 ObservationPoint: accesslog.Ingress, 442 SourceEndpoint: fakeSourceEndpoint, 443 DestinationEndpoint: fakeDestinationEndpoint, 444 IPVersion: accesslog.VersionIPv4, 445 Verdict: accesslog.VerdictForwarded, 446 TransportProtocol: accesslog.TransportProtocol(u8proto.TCP), 447 ServiceInfo: nil, 448 DropReason: nil, 449 HTTP: &accesslog.LogRecordHTTP{ 450 Code: 0, 451 Method: "POST", 452 URL: nil, 453 Protocol: "HTTP/1.1", 454 Headers: http.Header{ 455 "Host": {"myhost"}, 456 "Traceparent": {"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}, 457 }, 458 }, 459 } 460 461 parser, err := New(log, nil, nil, nil, nil) 462 require.NoError(t, err) 463 464 f := &flowpb.Flow{} 465 err = parser.Decode(lr, f) 466 require.NoError(t, err) 467 468 assert.Equal(t, &flowpb.HTTP{ 469 Code: 0, 470 Method: "POST", 471 Url: "", 472 Protocol: "HTTP/1.1", 473 Headers: []*flowpb.HTTPHeader{ 474 {Key: "Host", Value: "myhost"}, 475 {Key: "Traceparent", Value: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}, 476 }, 477 }, f.GetL7().GetHttp()) 478 assert.Equal(t, "4bf92f3577b34da6a3ce929d0e0e4736", f.GetTraceContext().GetParent().GetTraceId()) 479 } 480 481 func TestDecodeL7HTTPRequestRemoveUrlQuery(t *testing.T) { 482 requestPath, err := url.Parse("http://myhost/some/path?foo=bar") 483 require.NoError(t, err) 484 lr := &accesslog.LogRecord{ 485 Type: accesslog.TypeRequest, 486 Timestamp: fakeTimestamp, 487 NodeAddressInfo: fakeNodeInfo, 488 ObservationPoint: accesslog.Ingress, 489 SourceEndpoint: fakeSourceEndpoint, 490 DestinationEndpoint: fakeDestinationEndpoint, 491 IPVersion: accesslog.VersionIPv4, 492 Verdict: accesslog.VerdictForwarded, 493 TransportProtocol: accesslog.TransportProtocol(u8proto.TCP), 494 ServiceInfo: nil, 495 DropReason: nil, 496 HTTP: &accesslog.LogRecordHTTP{ 497 Code: 0, 498 Method: "POST", 499 URL: requestPath, 500 Protocol: "HTTP/1.1", 501 Headers: http.Header{ 502 "Host": {"myhost"}, 503 "Traceparent": {"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}, 504 }, 505 }, 506 } 507 lr.SourceEndpoint.Port = 56789 508 lr.DestinationEndpoint.Port = 80 509 510 opts := []options.Option{options.Redact(nil, true, true, false, []string{}, []string{"authorization"})} 511 parser, err := New(log, nil, nil, nil, nil, opts...) 512 require.NoError(t, err) 513 514 f := &flowpb.Flow{} 515 err = parser.Decode(lr, f) 516 require.NoError(t, err) 517 assert.Equal(t, &flowpb.HTTP{ 518 Code: 0, 519 Method: "POST", 520 Url: "http://myhost/some/path", 521 Protocol: "HTTP/1.1", 522 Headers: []*flowpb.HTTPHeader{ 523 {Key: "Host", Value: "myhost"}, 524 {Key: "Traceparent", Value: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}, 525 }, 526 }, f.GetL7().GetHttp()) 527 } 528 529 func TestDecodeL7HTTPRequestHeadersRedact(t *testing.T) { 530 requestPath, err := url.Parse("http://myhost/some/path") 531 require.NoError(t, err) 532 lr := &accesslog.LogRecord{ 533 Type: accesslog.TypeRequest, 534 Timestamp: fakeTimestamp, 535 NodeAddressInfo: fakeNodeInfo, 536 ObservationPoint: accesslog.Ingress, 537 SourceEndpoint: fakeSourceEndpoint, 538 DestinationEndpoint: fakeDestinationEndpoint, 539 IPVersion: accesslog.VersionIPv4, 540 Verdict: accesslog.VerdictForwarded, 541 TransportProtocol: accesslog.TransportProtocol(u8proto.TCP), 542 ServiceInfo: nil, 543 DropReason: nil, 544 HTTP: &accesslog.LogRecordHTTP{ 545 Code: 0, 546 Method: "POST", 547 URL: requestPath, 548 Protocol: "HTTP/1.1", 549 Headers: http.Header{ 550 "Host": {"myhost"}, 551 "traceparent": {"asdf"}, 552 }, 553 }, 554 } 555 lr.SourceEndpoint.Port = 56789 556 lr.DestinationEndpoint.Port = 80 557 558 opts := []options.Option{options.Redact(nil, true, true, false, []string{"host"}, []string{})} 559 parser, err := New(log, nil, nil, nil, nil, opts...) 560 require.NoError(t, err) 561 562 f := &flowpb.Flow{} 563 err = parser.Decode(lr, f) 564 require.NoError(t, err) 565 assert.Equal(t, &flowpb.HTTP{ 566 Code: 0, 567 Method: "POST", 568 Url: "http://myhost/some/path", 569 Protocol: "HTTP/1.1", 570 Headers: []*flowpb.HTTPHeader{ 571 {Key: "Host", Value: "myhost"}, 572 {Key: "traceparent", Value: defaults.SensitiveValueRedacted}, 573 }, 574 }, f.GetL7().GetHttp()) 575 576 opts = []options.Option{options.Redact(nil, true, true, false, []string{}, []string{"host"})} 577 parser, err = New(log, nil, nil, nil, nil, opts...) 578 require.NoError(t, err) 579 580 f = &flowpb.Flow{} 581 err = parser.Decode(lr, f) 582 require.NoError(t, err) 583 assert.Equal(t, &flowpb.HTTP{ 584 Code: 0, 585 Method: "POST", 586 Url: "http://myhost/some/path", 587 Protocol: "HTTP/1.1", 588 Headers: []*flowpb.HTTPHeader{ 589 {Key: "Host", Value: defaults.SensitiveValueRedacted}, 590 {Key: "traceparent", Value: "asdf"}, 591 }, 592 }, f.GetL7().GetHttp()) 593 } 594 595 func TestFilterHeader(t *testing.T) { 596 tests := []struct { 597 key string 598 val string 599 redactSettings options.HubbleRedactSettings 600 expectedVal string 601 }{ 602 { 603 key: "tracecontent", 604 val: "foo_not_redacted", 605 redactSettings: options.HubbleRedactSettings{ 606 Enabled: true, 607 RedactHttpHeaders: options.HttpHeadersList{ 608 Allow: map[string]struct{}{"tracecontent": {}}, 609 Deny: map[string]struct{}{}, 610 }, 611 }, 612 expectedVal: "foo_not_redacted", 613 }, 614 { 615 key: "tracecontent", 616 val: "foo", 617 redactSettings: options.HubbleRedactSettings{ 618 Enabled: true, 619 RedactHttpHeaders: options.HttpHeadersList{ 620 Allow: map[string]struct{}{}, 621 Deny: map[string]struct{}{"tracecontent": {}}, 622 }, 623 }, 624 expectedVal: defaults.SensitiveValueRedacted, 625 }, 626 { 627 key: "tracecontent", 628 val: "foo", 629 redactSettings: options.HubbleRedactSettings{ 630 Enabled: true, 631 RedactHttpHeaders: options.HttpHeadersList{ 632 Allow: map[string]struct{}{}, 633 Deny: map[string]struct{}{}, 634 }, 635 }, 636 expectedVal: defaults.SensitiveValueRedacted, 637 }, 638 { 639 key: "tracecontent", 640 val: "foo", 641 redactSettings: options.HubbleRedactSettings{ 642 Enabled: true, 643 RedactHttpHeaders: options.HttpHeadersList{ 644 Allow: map[string]struct{}{"host": {}}, 645 Deny: map[string]struct{}{}, 646 }, 647 }, 648 expectedVal: defaults.SensitiveValueRedacted, 649 }, 650 { 651 key: "tracecontent", 652 val: "foo", 653 redactSettings: options.HubbleRedactSettings{ 654 Enabled: true, 655 RedactHttpHeaders: options.HttpHeadersList{ 656 Allow: map[string]struct{}{}, 657 Deny: map[string]struct{}{"authorization": {}}, 658 }, 659 }, 660 expectedVal: "foo", 661 }, 662 } 663 for _, tt := range tests { 664 t.Run(tt.key, func(t *testing.T) { 665 got := filterHeader(tt.key, tt.val, tt.redactSettings) 666 assert.Equal(t, tt.expectedVal, got) 667 }) 668 } 669 } 670 671 func TestDecodeL7HTTPRequestPasswordRedact(t *testing.T) { 672 requestPath, err := url.Parse("http://user:pass@myhost") 673 require.NoError(t, err) 674 lr := &accesslog.LogRecord{ 675 Type: accesslog.TypeRequest, 676 Timestamp: fakeTimestamp, 677 NodeAddressInfo: fakeNodeInfo, 678 ObservationPoint: accesslog.Ingress, 679 SourceEndpoint: fakeSourceEndpoint, 680 DestinationEndpoint: fakeDestinationEndpoint, 681 IPVersion: accesslog.VersionIPv4, 682 Verdict: accesslog.VerdictForwarded, 683 TransportProtocol: accesslog.TransportProtocol(u8proto.TCP), 684 ServiceInfo: nil, 685 DropReason: nil, 686 HTTP: &accesslog.LogRecordHTTP{ 687 Code: 0, 688 Method: "POST", 689 URL: requestPath, 690 Protocol: "HTTP/1.1", 691 }, 692 } 693 lr.SourceEndpoint.Port = 56789 694 lr.DestinationEndpoint.Port = 80 695 696 opts := []options.Option{options.Redact(nil, true, true, false, []string{}, []string{})} 697 parser, err := New(log, nil, nil, nil, nil, opts...) 698 require.NoError(t, err) 699 700 f := &flowpb.Flow{} 701 err = parser.Decode(lr, f) 702 require.NoError(t, err) 703 assert.Equal(t, &flowpb.HTTP{ 704 Code: 0, 705 Method: "POST", 706 Url: fmt.Sprintf("http://user:%s@myhost", defaults.SensitiveValueRedacted), 707 Protocol: "HTTP/1.1", 708 }, f.GetL7().GetHttp()) 709 }