From f300ef4ce780a08a40126d00d45ed96b5a9f1909 Mon Sep 17 00:00:00 2001 From: Mayank Pande Date: Thu, 18 Apr 2024 11:48:28 +0530 Subject: [PATCH 1/2] fix: sanitize headers based on header key (#37) --- containers/container.go | 1 - ebpftracer/l7/http.go | 55 +++++++++++------------------------------ flags/flags.go | 10 ++++---- 3 files changed, 20 insertions(+), 46 deletions(-) diff --git a/containers/container.go b/containers/container.go index fc65e849..83252b79 100644 --- a/containers/container.go +++ b/containers/container.go @@ -532,7 +532,6 @@ func ignoreControlPlane(name string) bool { } for _, keyword := range keywords { if strings.Contains(strings.ToLower(name), keyword) { - klog.Warningln("Ignoring control plane ", name) return true } } diff --git a/ebpftracer/l7/http.go b/ebpftracer/l7/http.go index 00ad08c4..14141b29 100644 --- a/ebpftracer/l7/http.go +++ b/ebpftracer/l7/http.go @@ -2,14 +2,12 @@ package l7 import ( "bytes" - "encoding/base64" "encoding/json" "errors" "io" "log" "net/http" "net/url" - "regexp" "strings" "github.com/coroot/coroot-node-agent/flags" @@ -70,7 +68,11 @@ func ParseHTTPRequest(data []byte) (*http.Request, error) { var header = http.Header{} var host = "" headerLines := bytes.Split(headers, []byte("\n")) - + sensitiveHeaders := make(map[string]bool) + sensitiveKeysList := strings.Split(*flags.SensitiveHeader, ",") + for _, key := range sensitiveKeysList { + sensitiveHeaders[strings.TrimSpace(key)] = true + } for _, line := range headerLines { part1, part2, ok := bytes.Cut(line, []byte(":")) if !ok { @@ -79,8 +81,12 @@ func ParseHTTPRequest(data []byte) (*http.Request, error) { if strings.HasPrefix(string(part1), "Host") { host = strings.TrimSpace(string(part2)) } + key := strings.TrimSpace(string(part1)) val := strings.TrimSpace(string(part2)) + if sensitiveHeaders[key] { + val = SanitizeString(val) + } header.Add(key, val) } req := &http.Request{ @@ -125,53 +131,22 @@ func ParseHostFromHttpRequest(input string) (string, error) { func ConvertHeadersToString(headers http.Header) string { headerMap := make(map[string][]string) - for key, values := range headers { - var sanitizedValues []string - for _, value := range values { - sanitizedValues = append(sanitizedValues, SanitizeString(value)) - } - headerMap[key] = sanitizedValues + headerMap[key] = values } - jsonString, err := json.Marshal(headerMap) if err != nil { return "" } - return string(jsonString) } -func SanitizeString(input string) string { +func SanitizeString(value string) string { if !*flags.SanitizeHeaders { - return input - } - patternList := strings.Split(*flags.SensitiveHeaderPattern, ",") - // Compile regex patterns - var sensitivePatterns []*regexp.Regexp - for _, pattern := range patternList { - // Trim leading and trailing whitespace from the pattern - pattern = strings.TrimSpace(pattern) - if len(pattern) > 0 { - // Compile each pattern and append to the list - sensitivePatterns = append(sensitivePatterns, regexp.MustCompile(pattern)) - } + return value } - - // Replace sensitive data with placeholder '*' - sanitized := input - for _, pattern := range sensitivePatterns { - sanitized = pattern.ReplaceAllStringFunc(sanitized, func(match string) string { - // Only replace the sensitive part, keeping the structure intact - sanitized_string := strings.Repeat("*", min(len(match), 5)) - log.Printf("Replacing %s with %s , using pattern %s", match, sanitized_string, pattern) - return sanitized_string - }) + if len(value) <= 10 { + return strings.Repeat("*", len(value)) } - - byteData := []byte(sanitized) - - // Encode byte slice to Base64 - base64String := base64.StdEncoding.EncodeToString(byteData) - return base64String + return strings.Repeat("*", 10) + value[10:] } diff --git a/flags/flags.go b/flags/flags.go index c6028976..ddfdb6d1 100644 --- a/flags/flags.go +++ b/flags/flags.go @@ -32,11 +32,11 @@ var ( ApiKey = kingpin.Flag("api-key", "Coroot API key").Envar("API_KEY").String() ScrapeInterval = kingpin.Flag("scrape-interval", "How often to gather metrics from the agent").Default("15s").Envar("SCRAPE_INTERVAL").Duration() - WalDir = kingpin.Flag("wal-dir", "Path to where the agent stores data (e.g. the metrics Write-Ahead Log)").Default("/tmp/coroot-node-agent").Envar("WAL_DIR").String() - ResolveDns = kingpin.Flag("resolve-dns", "should resolve DNS").Default("true").Envar("RESOLVE_DNS").Bool() - IgnoreControlPlane = kingpin.Flag("ignore-control-plane", "ignore control plane like loki").Default("karpenter,loki,prometheus,grafana,kubelet,etcd,apiserver,victoria").Envar("IGNORE_CONTROL_PLANE").String() - SensitiveHeaderPattern = kingpin.Flag("sensitive-header-patterns", "sanitize headers using patterns").Default("(?i)Authorization: (Bearer|Basic)\\s+[a-zA-Z0-9\\-_\\.\\=]+, (?i)ApiKey\\s+[a-zA-Z0-9\\-_\\.\\=]+, (?i)JWT\\s+[a-zA-Z0-9\\-_\\.\\=]+, (?i)OAuth\\s+[a-zA-Z0-9\\-_\\.\\=]+").Envar("SENSITIVE_HEADER_PATTERN").String() - SanitizeHeaders = kingpin.Flag("sanitize-headers", "should sanitize headers").Default("false").Envar("SANITIZE_HEADERS").Bool() + WalDir = kingpin.Flag("wal-dir", "Path to where the agent stores data (e.g. the metrics Write-Ahead Log)").Default("/tmp/coroot-node-agent").Envar("WAL_DIR").String() + ResolveDns = kingpin.Flag("resolve-dns", "should resolve DNS").Default("true").Envar("RESOLVE_DNS").Bool() + IgnoreControlPlane = kingpin.Flag("ignore-control-plane", "ignore control plane like loki").Default("karpenter,loki,prometheus,grafana,kubelet,etcd,apiserver,victoria").Envar("IGNORE_CONTROL_PLANE").String() + SanitizeHeaders = kingpin.Flag("sanitize-headers", "should sanitize headers").Default("true").Envar("SANITIZE_HEADERS").Bool() + SensitiveHeader = kingpin.Flag("sensitive-headers", "sanitize headers using patterns").Default("Authorization, Cookie").Envar("SENSITIVE_HEADERS").String() ) func GetString(fl *string) string { From 87c6cba412b4e3602b03982113af8bfcec11ccc3 Mon Sep 17 00:00:00 2001 From: Mayank Pande Date: Fri, 19 Apr 2024 11:42:34 +0530 Subject: [PATCH 2/2] fix: fix for sanitize headers (#38) * fix: added http path as attribute * fix: fix for sanitize headers --- ebpftracer/l7/http.go | 10 ++++------ ebpftracer/l7/l7_test.go | 9 ++++++++- flags/flags.go | 2 +- tracing/tracing.go | 1 + 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/ebpftracer/l7/http.go b/ebpftracer/l7/http.go index 14141b29..01a8a4a6 100644 --- a/ebpftracer/l7/http.go +++ b/ebpftracer/l7/http.go @@ -71,7 +71,7 @@ func ParseHTTPRequest(data []byte) (*http.Request, error) { sensitiveHeaders := make(map[string]bool) sensitiveKeysList := strings.Split(*flags.SensitiveHeader, ",") for _, key := range sensitiveKeysList { - sensitiveHeaders[strings.TrimSpace(key)] = true + sensitiveHeaders[strings.ToLower(strings.TrimSpace(key))] = true } for _, line := range headerLines { part1, part2, ok := bytes.Cut(line, []byte(":")) @@ -84,7 +84,7 @@ func ParseHTTPRequest(data []byte) (*http.Request, error) { key := strings.TrimSpace(string(part1)) val := strings.TrimSpace(string(part2)) - if sensitiveHeaders[key] { + if sensitiveHeaders[strings.ToLower(key)] { val = SanitizeString(val) } header.Add(key, val) @@ -143,10 +143,8 @@ func ConvertHeadersToString(headers http.Header) string { func SanitizeString(value string) string { if !*flags.SanitizeHeaders { + return value } - if len(value) <= 10 { - return strings.Repeat("*", len(value)) - } - return strings.Repeat("*", 10) + value[10:] + return strings.Repeat("*", len(value)) } diff --git a/ebpftracer/l7/l7_test.go b/ebpftracer/l7/l7_test.go index 6bb809c9..ef3ee60c 100644 --- a/ebpftracer/l7/l7_test.go +++ b/ebpftracer/l7/l7_test.go @@ -6,6 +6,7 @@ import ( "io" "testing" + "github.com/coroot/coroot-node-agent/flags" "github.com/stretchr/testify/assert" "go.mongodb.org/mongo-driver/bson" ) @@ -91,12 +92,18 @@ func TestParseMongo(t *testing.T) { } func TestParseHost(t *testing.T) { - requestString := "HEAD /1 HTTP/1.1\r\nHost: 127.0.0.1\r\nUser-Agent: curl/8.0.1\r\nAccept: */*\r\n\r\nxzxxxxxxzx" + SanitizeHeaders := "Authorization, cookie" + f := true + flags.SensitiveHeader = &SanitizeHeaders + flags.SanitizeHeaders = &f + requestString := "HEAD /1 HTTP/1.1\r\nHost: 127.0.0.1\r\nUser-Agent: curl/8.0.1\r\nAuthorization: abcd\r\nCookie: abcd\r\nAccept: */*\r\n\r\nxzxxxxxxzx" req, err := ParseHTTPRequest([]byte(requestString)) assert.Nil(t, nil, err) assert.Equal(t, "/1", req.URL.Path) assert.Equal(t, "127.0.0.1", req.Host) assert.Equal(t, "HEAD", req.Method) + assert.Equal(t, "****", req.Header.Get("Authorization")) + assert.Equal(t, "****", req.Header.Get("Cookie")) body, err := io.ReadAll(req.Body) assert.Nil(t, nil, err) assert.Equal(t, "xzxxxxxxzx", string(body)) diff --git a/flags/flags.go b/flags/flags.go index ddfdb6d1..2cff3464 100644 --- a/flags/flags.go +++ b/flags/flags.go @@ -36,7 +36,7 @@ var ( ResolveDns = kingpin.Flag("resolve-dns", "should resolve DNS").Default("true").Envar("RESOLVE_DNS").Bool() IgnoreControlPlane = kingpin.Flag("ignore-control-plane", "ignore control plane like loki").Default("karpenter,loki,prometheus,grafana,kubelet,etcd,apiserver,victoria").Envar("IGNORE_CONTROL_PLANE").String() SanitizeHeaders = kingpin.Flag("sanitize-headers", "should sanitize headers").Default("true").Envar("SANITIZE_HEADERS").Bool() - SensitiveHeader = kingpin.Flag("sensitive-headers", "sanitize headers using patterns").Default("Authorization, Cookie").Envar("SENSITIVE_HEADERS").String() + SensitiveHeader = kingpin.Flag("sensitive-headers", "sanitize headers using patterns").Default("Authorization, Cookie, X-Action-Token").Envar("SENSITIVE_HEADERS").String() ) func GetString(fl *string) string { diff --git a/tracing/tracing.go b/tracing/tracing.go index e0dcc153..dac1ca5b 100644 --- a/tracing/tracing.go +++ b/tracing/tracing.go @@ -122,6 +122,7 @@ func (t *Trace) HttpRequest(method, path string, status l7.Status, duration time attribute.Key("http.request_payload").String(payload), attribute.Key("http.headers").String(headers), attribute.Key("http.response").String(response), + attribute.Key("http.path").String(path), ) }