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
5 changes: 5 additions & 0 deletions driver/configuration/config_keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,8 @@ const (
ErrorsRedirectIsEnabled Key = ErrorsHandlers + ".redirect.enabled"
ErrorsWWWAuthenticateIsEnabled Key = ErrorsHandlers + ".www_authenticate.enabled"
)

// Log
const (
LogRedactHeaders Key = "log.redact_headers"
)
2 changes: 2 additions & 0 deletions driver/configuration/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ type Provider interface {

TLSConfig(daemon string) *TLSConfig

LogRedactHeaders() []string

SetForTest(t testing.TB, key string, value interface{})
}

Expand Down
4 changes: 4 additions & 0 deletions driver/configuration/provider_koanf.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,10 @@ func (v *KoanfProvider) TracingConfig() *otelx.Config {
return v.source.TracingConfig(v.TracingServiceName())
}

func (v *KoanfProvider) LogRedactHeaders() []string {
return v.source.StringsF(LogRedactHeaders, []string{})
}

func (v *KoanfProvider) PrometheusHideRequestPaths() bool {
return v.source.BoolF(PrometheusServeHideRequestPaths, false)
}
Expand Down
45 changes: 45 additions & 0 deletions driver/configuration/provider_koanf_public_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -453,3 +453,48 @@ func TestAuthenticatorOAuth2TokenIntrospectionPreAuthorization(t *testing.T) {
})
}
}

func TestLogRedactHeaders(t *testing.T) {
t.Run("case=returns empty array when not configured", func(t *testing.T) {
p, err := configuration.NewKoanfProvider(
context.Background(),
nil,
logrusx.New("", ""),
)
require.NoError(t, err)

headers := p.LogRedactHeaders()
assert.Empty(t, headers)
})

t.Run("case=returns configured headers", func(t *testing.T) {
p, err := configuration.NewKoanfProvider(
context.Background(),
nil,
logrusx.New("", ""),
configx.SkipValidation(),
configx.WithValues(map[string]interface{}{
"log.redact_headers": []string{"x-custom-authorization", "x-api-key"},
}),
)
require.NoError(t, err)

headers := p.LogRedactHeaders()
assert.Equal(t, []string{"x-custom-authorization", "x-api-key"}, headers)
})

t.Run("case=reads from config file", func(t *testing.T) {
p, err := configuration.NewKoanfProvider(
context.Background(),
nil,
logrusx.New("", ""),
configx.SkipValidation(),
configx.WithConfigFiles("testdata/log_redact_headers_config.yaml"),
)
require.NoError(t, err)

headers := p.LogRedactHeaders()
assert.Contains(t, headers, "x-custom-authorization")
assert.Contains(t, headers, "x-api-key")
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
log:
redact_headers:
- x-custom-authorization
- x-api-key
2 changes: 1 addition & 1 deletion driver/registry_memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ func (r *RegistryMemory) Writer() herodot.Writer {

func (r *RegistryMemory) Logger() *logrusx.Logger {
if r.logger == nil {
r.logger = logrusx.New("ORY Oathkeeper", x.Version)
r.logger = logrusx.New("ORY Oathkeeper", x.Version, logrusx.WithAdditionalRedactedHeaders(r.c.LogRedactHeaders()))
}
return r.logger
}
Expand Down
69 changes: 69 additions & 0 deletions driver/registry_memory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ package driver

import (
"context"
"net/http"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/ory/oathkeeper/driver/configuration"
"github.com/ory/x/configx"
"github.com/ory/x/logrusx"
)

Expand Down Expand Up @@ -50,3 +52,70 @@ func TestRegistryMemoryPipelineAuthorizer(t *testing.T) {
})
}
}

func TestRegistryLoggerRedactsConfiguredHeaders(t *testing.T) {
t.Run("case=logger redacts custom headers from configuration", func(t *testing.T) {
c, err := configuration.NewKoanfProvider(
context.Background(),
nil,
logrusx.New("", ""),
configx.SkipValidation(),
configx.WithValues(map[string]interface{}{
"log.redact_headers": []string{"x-custom-authorization", "x-api-key"},
}),
)
require.NoError(t, err)

r := NewRegistry(c)
logger := r.Logger()

// Create headers with custom auth header
headers := http.Header{}
headers.Set("Authorization", "Bearer default-token")
headers.Set("X-Custom-Authorization", "Bearer custom-token")
headers.Set("X-API-Key", "secret-key")
headers.Set("X-Request-ID", "request-123")

// Get redacted headers
redacted := logger.HTTPHeadersRedacted(headers)

// Default sensitive headers should be redacted
assert.Contains(t, redacted["authorization"], "Value is sensitive")

// Custom configured headers should be redacted
assert.Contains(t, redacted["x-custom-authorization"], "Value is sensitive")
assert.Contains(t, redacted["x-api-key"], "Value is sensitive")

// Non-sensitive headers should not be redacted
assert.Equal(t, "request-123", redacted["x-request-id"])
})

t.Run("case=logger without custom configuration only redacts default headers", func(t *testing.T) {
c, err := configuration.NewKoanfProvider(
context.Background(),
nil,
logrusx.New("", ""),
configx.SkipValidation(),
)
require.NoError(t, err)

r := NewRegistry(c)
logger := r.Logger()

// Create headers with custom auth header
headers := http.Header{}
headers.Set("Authorization", "Bearer default-token")
headers.Set("X-Custom-Authorization", "Bearer custom-token")
headers.Set("X-API-Key", "secret-key")

// Get redacted headers
redacted := logger.HTTPHeadersRedacted(headers)

// Default sensitive headers should be redacted
assert.Contains(t, redacted["authorization"], "Value is sensitive")

// Custom headers should NOT be redacted (no configuration)
assert.Equal(t, "Bearer custom-token", redacted["x-custom-authorization"])
assert.Equal(t, "secret-key", redacted["x-api-key"])
})
}
Loading