Skip to content

Commit d330f61

Browse files
feat(configx): add HTTP header log redaction config
1 parent 78da4d7 commit d330f61

File tree

8 files changed

+104
-21
lines changed

8 files changed

+104
-21
lines changed

configx/stub/from-files/config.schema.json

+10
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,16 @@
850850
"title": "Sensitive log value redaction text",
851851
"description": "Text to use, when redacting sensitive log value."
852852
},
853+
"redactable_http_headers": {
854+
"type": "array",
855+
"title": "Redactable HTTP headers",
856+
"description": "Additional HTTP headers to redact in the logs.",
857+
"items": {
858+
"type": "string"
859+
},
860+
"examples": [["X-Auth-Token", "X-Authenticated-User"]],
861+
"uniqueItems": true
862+
},
853863
"format": {
854864
"type": "string",
855865
"enum": ["json", "text"]

configx/stub/hydra/config.schema.json

+10
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,16 @@
203203
"title": "Sensitive log value redaction text",
204204
"description": "Text to use, when redacting sensitive log value."
205205
},
206+
"redactable_http_headers": {
207+
"type": "array",
208+
"title": "Redactable HTTP headers",
209+
"description": "Additional HTTP headers to redact in the logs.",
210+
"items": {
211+
"type": "string"
212+
},
213+
"examples": [["X-Auth-Token", "X-Authenticated-User"]],
214+
"uniqueItems": true
215+
},
206216
"format": {
207217
"type": "string",
208218
"description": "Sets the log format.",

configx/stub/kratos/config.schema.json

+10
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,16 @@
850850
"title": "Sensitive log value redaction text",
851851
"description": "Text to use, when redacting sensitive log value."
852852
},
853+
"redactable_http_headers": {
854+
"type": "array",
855+
"title": "Redactable HTTP headers",
856+
"description": "Additional HTTP headers to redact in the logs.",
857+
"items": {
858+
"type": "string"
859+
},
860+
"examples": [["X-Auth-Token", "X-Authenticated-User"]],
861+
"uniqueItems": true
862+
},
853863
"format": {
854864
"type": "string",
855865
"enum": ["json", "text"]

configx/stub/multi/config.schema.json

+10
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,16 @@
850850
"title": "Sensitive log value redaction text",
851851
"description": "Text to use, when redacting sensitive log value."
852852
},
853+
"redactable_http_headers": {
854+
"type": "array",
855+
"title": "Redactable HTTP headers",
856+
"description": "Additional HTTP headers to redact in the logs.",
857+
"items": {
858+
"type": "string"
859+
},
860+
"examples": [["X-Auth-Token", "X-Authenticated-User"]],
861+
"uniqueItems": true
862+
},
853863
"format": {
854864
"type": "string",
855865
"enum": ["json", "text"]

logrusx/config.schema.json

+10
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@
2929
"type": "string",
3030
"title": "Sensitive log value redaction text",
3131
"description": "Text to use, when redacting sensitive log value."
32+
},
33+
"redactable_http_headers": {
34+
"type": "array",
35+
"title": "Redactable HTTP headers",
36+
"description": "Additional HTTP headers to redact in the logs.",
37+
"items": {
38+
"type": "string"
39+
},
40+
"examples": [["X-Auth-Token", "X-Authenticated-User"]],
41+
"uniqueItems": true
3242
}
3343
},
3444
"additionalProperties": false

logrusx/helper.go

+8-6
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,17 @@ import (
2121
"go.opentelemetry.io/otel/trace"
2222

2323
"github.com/ory/x/errorsx"
24+
"github.com/ory/x/stringslice"
2425
)
2526

2627
type Logger struct {
2728
*logrus.Entry
28-
leakSensitive bool
29-
redactionText string
30-
opts []Option
31-
name string
32-
version string
29+
leakSensitive bool
30+
redactionText string
31+
redactableHTTPHeaders []string
32+
opts []Option
33+
name string
34+
version string
3335
}
3436

3537
var opts = otelhttptrace.WithPropagators(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
@@ -59,7 +61,7 @@ func (l *Logger) HTTPHeadersRedacted(h http.Header) map[string]interface{} {
5961

6062
for key, value := range h {
6163
keyLower := strings.ToLower(key)
62-
if keyLower == "authorization" || keyLower == "cookie" || keyLower == "set-cookie" {
64+
if keyLower == "authorization" || keyLower == "cookie" || keyLower == "set-cookie" || stringslice.HasI(l.redactableHTTPHeaders, keyLower) {
6365
headers[keyLower] = l.maybeRedact(value)
6466
} else {
6567
headers[keyLower] = h.Get(key)

logrusx/logrus.go

+33-15
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,24 @@ import (
2020

2121
type (
2222
options struct {
23-
l *logrus.Logger
24-
level *logrus.Level
25-
formatter logrus.Formatter
26-
format string
27-
reportCaller bool
28-
exitFunc func(int)
29-
leakSensitive bool
30-
redactionText string
31-
hooks []logrus.Hook
32-
c configurator
23+
l *logrus.Logger
24+
level *logrus.Level
25+
formatter logrus.Formatter
26+
format string
27+
reportCaller bool
28+
exitFunc func(int)
29+
leakSensitive bool
30+
redactionText string
31+
redactableHTTPHeaders []string
32+
hooks []logrus.Hook
33+
c configurator
3334
}
3435
Option func(*options)
3536
nullConfigurator struct{}
3637
configurator interface {
3738
Bool(key string) bool
3839
String(key string) string
40+
Strings(key string) []string
3941
}
4042
)
4143

@@ -178,6 +180,12 @@ func RedactionText(text string) Option {
178180
}
179181
}
180182

183+
func RedactableHTTPHeaders(headers []string) Option {
184+
return func(o *options) {
185+
o.redactableHTTPHeaders = headers
186+
}
187+
}
188+
181189
func (c *nullConfigurator) Bool(_ string) bool {
182190
return false
183191
}
@@ -186,6 +194,10 @@ func (c *nullConfigurator) String(_ string) string {
186194
return ""
187195
}
188196

197+
func (c *nullConfigurator) Strings(_ string) []string {
198+
return []string{}
199+
}
200+
189201
func newOptions(opts []Option) *options {
190202
o := new(options)
191203
o.c = new(nullConfigurator)
@@ -199,11 +211,12 @@ func newOptions(opts []Option) *options {
199211
func New(name string, version string, opts ...Option) *Logger {
200212
o := newOptions(opts)
201213
return &Logger{
202-
opts: opts,
203-
name: name,
204-
version: version,
205-
leakSensitive: o.leakSensitive || o.c.Bool("log.leak_sensitive_values"),
206-
redactionText: stringsx.DefaultIfEmpty(o.redactionText, `Value is sensitive and has been redacted. To see the value set config key "log.leak_sensitive_values = true" or environment variable "LOG_LEAK_SENSITIVE_VALUES=true".`),
214+
opts: opts,
215+
name: name,
216+
version: version,
217+
leakSensitive: o.leakSensitive || o.c.Bool("log.leak_sensitive_values"),
218+
redactionText: stringsx.DefaultIfEmpty(o.redactionText, `Value is sensitive and has been redacted. To see the value set config key "log.leak_sensitive_values = true" or environment variable "LOG_LEAK_SENSITIVE_VALUES=true".`),
219+
redactableHTTPHeaders: o.redactableHTTPHeaders,
207220
Entry: newLogger(o.l, o).WithFields(logrus.Fields{
208221
"audience": "application", "service_name": name, "service_version": version}),
209222
}
@@ -216,6 +229,11 @@ func NewAudit(name string, version string, opts ...Option) *Logger {
216229
func (l *Logger) UseConfig(c configurator) {
217230
l.leakSensitive = l.leakSensitive || c.Bool("log.leak_sensitive_values")
218231
l.redactionText = stringsx.DefaultIfEmpty(c.String("log.redaction_text"), l.redactionText)
232+
r := c.Strings("log.redactable_http_headers")
233+
if len(r) == 0 {
234+
r = l.redactableHTTPHeaders
235+
}
236+
l.redactableHTTPHeaders = r
219237
o := newOptions(append(l.opts, WithConfigurator(c)))
220238
setLevel(l.Entry.Logger, o)
221239
setFormatter(l.Entry.Logger, o)

logrusx/logrus_test.go

+13
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,19 @@ func TestTextLogger(t *testing.T) {
135135
l.WithRequest(fakeRequest).Error("An error occurred.")
136136
},
137137
},
138+
{
139+
l: New("logrusx-app", "v0.0.0", ForceFormat("text"), ForceLevel(logrus.TraceLevel), RedactionText("redacted"), RedactableHTTPHeaders([]string{"X-Request-ID"})),
140+
expect: []string{"logrus_test.go", "logrusx_test.TestTextLogger",
141+
"audience=application", "service_name=logrusx-app", "service_version=v0.0.0",
142+
"An error occurred.", "headers:map[", "accept:application/json", "accept-encoding:gzip",
143+
"user-agent:Go-http-client/1.1", "host:127.0.0.1:63232", "method:GET",
144+
"query:redacted",
145+
},
146+
notExpect: []string{"testing.tRunner", "bar=foo", "x-request-id:id1234"},
147+
call: func(l *Logger) {
148+
l.WithRequest(fakeRequest).Error("An error occurred.")
149+
},
150+
},
138151
{
139152
l: New("logrusx-server", "v0.0.1", ForceFormat("text"), LeakSensitive(), ForceLevel(logrus.DebugLevel)),
140153
expect: []string{

0 commit comments

Comments
 (0)