Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions proxy/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,35 @@ func newTCPLogger(logger *zerolog.Logger, req *connection.TCPRequest) zerolog.Lo

// logHTTPRequest logs a Debug message with the corresponding HTTP request details from the eyeball.
func logHTTPRequest(logger *zerolog.Logger, r *http.Request) {
logger.Debug().
event := logger.Debug()
if !event.Enabled() {
return
}
event.
Str("host", r.Host).
Str("path", r.URL.Path).
Interface("headers", r.Header).
Interface("headers", redactedRequestHeaders(r.Header)).
Int64("content-length", r.ContentLength).
Msgf("%s %s %s", r.Method, r.URL, r.Proto)
}

// redactedRequestHeaders keeps header names and value counts useful for debugging
// without recording values that may contain credentials or other private data.
func redactedRequestHeaders(headers http.Header) http.Header {
if headers == nil {
return nil
}
redacted := make(http.Header, len(headers))
for name, values := range headers {
redactedValues := make([]string, len(values))
for i := range redactedValues {
redactedValues[i] = "[redacted]"
}
redacted[name] = redactedValues
}
return redacted
}

// logOriginHTTPResponse logs a Debug message of the origin response.
func logOriginHTTPResponse(logger *zerolog.Logger, resp *http.Response) {
responseByCode.WithLabelValues(strconv.Itoa(resp.StatusCode)).Inc()
Expand Down
47 changes: 47 additions & 0 deletions proxy/logger_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package proxy

import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"

"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestLogHTTPRequestRedactsHeaderValues(t *testing.T) {
t.Parallel()

request := httptest.NewRequest(http.MethodGet, "http://example.com/path", nil)
request.Header = http.Header{
"Authorization": {"Bearer test-credential"},
"Cookie": {"session=test-session"},
"X-Request-Id": {"request-123", "request-456"},
}
originalHeaders := request.Header.Clone()

var output bytes.Buffer
logger := zerolog.New(&output).Level(zerolog.DebugLevel)
logHTTPRequest(&logger, request)

var entry struct {
Level string `json:"level"`
Headers map[string][]string `json:"headers"`
Message string `json:"message"`
}
require.NoError(t, json.Unmarshal(output.Bytes(), &entry))
assert.Equal(t, "debug", entry.Level)
assert.Equal(t, "GET http://example.com/path HTTP/1.1", entry.Message)
assert.Equal(t, map[string][]string{
"Authorization": {"[redacted]"},
"Cookie": {"[redacted]"},
"X-Request-Id": {"[redacted]", "[redacted]"},
}, entry.Headers)
assert.Equal(t, originalHeaders, request.Header)
assert.NotContains(t, output.String(), "test-credential")
assert.NotContains(t, output.String(), "test-session")
assert.NotContains(t, output.String(), "request-123")
}