github.com/cilium/cilium@v1.16.2/pkg/hubble/parser/seven/http.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/url"
     9  	"sort"
    10  	"strings"
    11  
    12  	flowpb "github.com/cilium/cilium/api/v1/flow"
    13  	"github.com/cilium/cilium/pkg/hubble/defaults"
    14  	"github.com/cilium/cilium/pkg/hubble/parser/options"
    15  	"github.com/cilium/cilium/pkg/proxy/accesslog"
    16  	"github.com/cilium/cilium/pkg/time"
    17  )
    18  
    19  func decodeHTTP(flowType accesslog.FlowType, http *accesslog.LogRecordHTTP, opts *options.Options) *flowpb.Layer7_Http {
    20  	var headers []*flowpb.HTTPHeader
    21  	keys := make([]string, 0, len(http.Headers))
    22  	for key := range http.Headers {
    23  		keys = append(keys, key)
    24  	}
    25  	sort.Strings(keys)
    26  	for _, key := range keys {
    27  		for _, value := range http.Headers[key] {
    28  			filteredValue := filterHeader(key, value, opts.HubbleRedactSettings)
    29  			headers = append(headers, &flowpb.HTTPHeader{Key: key, Value: filteredValue})
    30  		}
    31  	}
    32  	uri := filteredURL(http.URL, opts.HubbleRedactSettings)
    33  
    34  	if flowType == accesslog.TypeRequest {
    35  		// Set only fields that are relevant for requests.
    36  		return &flowpb.Layer7_Http{
    37  			Http: &flowpb.HTTP{
    38  				Method:   http.Method,
    39  				Protocol: http.Protocol,
    40  				Url:      uri.String(),
    41  				Headers:  headers,
    42  			},
    43  		}
    44  	}
    45  
    46  	return &flowpb.Layer7_Http{
    47  		Http: &flowpb.HTTP{
    48  			Code:     uint32(http.Code),
    49  			Method:   http.Method,
    50  			Protocol: http.Protocol,
    51  			Url:      uri.String(),
    52  			Headers:  headers,
    53  		},
    54  	}
    55  }
    56  
    57  func (p *Parser) httpSummary(flowType accesslog.FlowType, http *accesslog.LogRecordHTTP, flow *flowpb.Flow) string {
    58  	uri := filteredURL(http.URL, p.opts.HubbleRedactSettings)
    59  	httpRequest := http.Method + " " + uri.String()
    60  	switch flowType {
    61  	case accesslog.TypeRequest:
    62  		return fmt.Sprintf("%s %s", http.Protocol, httpRequest)
    63  	case accesslog.TypeResponse:
    64  		return fmt.Sprintf("%s %d %dms (%s)", http.Protocol, http.Code, uint64(time.Duration(flow.GetL7().LatencyNs)/time.Millisecond), httpRequest)
    65  	}
    66  	return ""
    67  }
    68  
    69  // filterHeader receives a key-value pair of an http header along with an HubbleRedactSettings.
    70  // Based on the allow/deny lists of the provided HttpHeadersList it returns the original value
    71  // or the redacted constant "HUBBLE_REDACTED" accordingly:
    72  //  1. If HubbleRedactSettings is enabled (meaning that hubble.redact feature is enabled) but both allow/deny lists are empty then the value of the
    73  //     header is redacted by default.
    74  //  2. If the header's key is contained in the allow list then the value
    75  //     of the header will not be redacted.
    76  //  3. If the header's key is contained in the deny list then the value
    77  //     of the header will be redacted.
    78  //  4. If none of the above happens, then if there is any allow list defined then the value will be redacted
    79  //     otherwise if there is a deny list defined the value will not be redacted.
    80  func filterHeader(key string, value string, redactSettings options.HubbleRedactSettings) string {
    81  	if !redactSettings.Enabled {
    82  		return value
    83  	}
    84  	if len(redactSettings.RedactHttpHeaders.Allow) == 0 && len(redactSettings.RedactHttpHeaders.Deny) == 0 {
    85  		// That's the default case, where redact is generally enabled but not headers' lists
    86  		// have been specified. In that case we redact everything by default.
    87  		return defaults.SensitiveValueRedacted
    88  	}
    89  	if _, ok := redactSettings.RedactHttpHeaders.Allow[strings.ToLower(key)]; ok {
    90  		return value
    91  	}
    92  	if _, ok := redactSettings.RedactHttpHeaders.Deny[strings.ToLower(key)]; ok {
    93  		return defaults.SensitiveValueRedacted
    94  	}
    95  
    96  	if len(redactSettings.RedactHttpHeaders.Allow) > 0 {
    97  		return defaults.SensitiveValueRedacted
    98  	}
    99  	return value
   100  }
   101  
   102  // filteredURL return a copy of the given URL potentially mutated depending on
   103  // Hubble redact settings.
   104  // If configured and user info exists, it removes the password from the flow.
   105  // If configured, it removes the URL's query parts from the flow.
   106  func filteredURL(uri *url.URL, redactSettings options.HubbleRedactSettings) *url.URL {
   107  	if uri == nil {
   108  		// NOTE: return a non-nil URL so that we can always call String() on
   109  		// it.
   110  		return &url.URL{}
   111  	}
   112  	u2 := cloneURL(uri)
   113  	if redactSettings.RedactHTTPUserInfo && u2.User != nil {
   114  		if _, ok := u2.User.Password(); ok {
   115  			u2.User = url.UserPassword(u2.User.Username(), defaults.SensitiveValueRedacted)
   116  		}
   117  	}
   118  	if redactSettings.RedactHTTPQuery {
   119  		u2.RawQuery = ""
   120  		u2.Fragment = ""
   121  	}
   122  	return u2
   123  }
   124  
   125  // cloneURL return a copy of the given URL. Copied from src/net/http/clone.go.
   126  func cloneURL(u *url.URL) *url.URL {
   127  	if u == nil {
   128  		return nil
   129  	}
   130  	u2 := new(url.URL)
   131  	*u2 = *u
   132  	if u.User != nil {
   133  		u2.User = new(url.Userinfo)
   134  		*u2.User = *u.User
   135  	}
   136  	return u2
   137  }