mirror of
https://github.com/pomerium/pomerium.git
synced 2025-07-27 13:39:04 +02:00
proxy: add support for logging http request headers
This commit is contained in:
parent
86a2fc6807
commit
4d7140a8a2
3 changed files with 45 additions and 1 deletions
|
@ -10,6 +10,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
@ -92,6 +93,13 @@ func buildAccessLogs(options *config.Options) []*envoy_config_accesslog_v3.Acces
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var additionalRequestHeaders []string
|
||||||
|
for _, field := range options.AccessLogFields {
|
||||||
|
if headerName, ok := field.IsForHeader(); ok {
|
||||||
|
additionalRequestHeaders = append(additionalRequestHeaders, http.CanonicalHeaderKey(headerName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tc := marshalAny(&envoy_extensions_access_loggers_grpc_v3.HttpGrpcAccessLogConfig{
|
tc := marshalAny(&envoy_extensions_access_loggers_grpc_v3.HttpGrpcAccessLogConfig{
|
||||||
CommonConfig: &envoy_extensions_access_loggers_grpc_v3.CommonGrpcAccessLogConfig{
|
CommonConfig: &envoy_extensions_access_loggers_grpc_v3.CommonGrpcAccessLogConfig{
|
||||||
LogName: "ingress-http",
|
LogName: "ingress-http",
|
||||||
|
@ -104,6 +112,7 @@ func buildAccessLogs(options *config.Options) []*envoy_config_accesslog_v3.Acces
|
||||||
},
|
},
|
||||||
TransportApiVersion: envoy_config_core_v3.ApiVersion_V3,
|
TransportApiVersion: envoy_config_core_v3.ApiVersion_V3,
|
||||||
},
|
},
|
||||||
|
AdditionalRequestHeadersToLog: additionalRequestHeaders,
|
||||||
})
|
})
|
||||||
return []*envoy_config_accesslog_v3.AccessLog{{
|
return []*envoy_config_accesslog_v3.AccessLog{{
|
||||||
Name: "envoy.access_loggers.http_grpc",
|
Name: "envoy.access_loggers.http_grpc",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package controlplane
|
package controlplane
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
envoy_data_accesslog_v3 "github.com/envoyproxy/go-control-plane/envoy/data/accesslog/v3"
|
envoy_data_accesslog_v3 "github.com/envoyproxy/go-control-plane/envoy/data/accesslog/v3"
|
||||||
|
@ -24,6 +25,7 @@ func (srv *Server) StreamAccessLogs(stream envoy_service_accesslog_v3.AccessLogS
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, entry := range msg.GetHttpLogs().LogEntry {
|
for _, entry := range msg.GetHttpLogs().LogEntry {
|
||||||
|
requestHeaders := getRequestHeaders(entry)
|
||||||
reqPath := entry.GetRequest().GetPath()
|
reqPath := entry.GetRequest().GetPath()
|
||||||
var evt *zerolog.Event
|
var evt *zerolog.Event
|
||||||
if reqPath == "/ping" || reqPath == "/healthz" {
|
if reqPath == "/ping" || reqPath == "/healthz" {
|
||||||
|
@ -33,7 +35,7 @@ func (srv *Server) StreamAccessLogs(stream envoy_service_accesslog_v3.AccessLogS
|
||||||
}
|
}
|
||||||
evt = evt.Str("service", "envoy")
|
evt = evt.Str("service", "envoy")
|
||||||
for _, field := range srv.currentConfig.Load().Config.Options.GetAccessLogFields() {
|
for _, field := range srv.currentConfig.Load().Config.Options.GetAccessLogFields() {
|
||||||
evt = populateLogEvent(field, evt, entry)
|
evt = populateLogEvent(field, evt, entry, requestHeaders)
|
||||||
}
|
}
|
||||||
evt.Msg("http-request")
|
evt.Msg("http-request")
|
||||||
}
|
}
|
||||||
|
@ -44,7 +46,12 @@ func populateLogEvent(
|
||||||
field log.AccessLogField,
|
field log.AccessLogField,
|
||||||
evt *zerolog.Event,
|
evt *zerolog.Event,
|
||||||
entry *envoy_data_accesslog_v3.HTTPAccessLogEntry,
|
entry *envoy_data_accesslog_v3.HTTPAccessLogEntry,
|
||||||
|
requestHeaders map[string]string,
|
||||||
) *zerolog.Event {
|
) *zerolog.Event {
|
||||||
|
if headerName, ok := field.IsForHeader(); ok {
|
||||||
|
return evt.Str("header."+headerName, requestHeaders[http.CanonicalHeaderKey(headerName)])
|
||||||
|
}
|
||||||
|
|
||||||
switch field {
|
switch field {
|
||||||
case log.AccessLogFieldAuthority:
|
case log.AccessLogFieldAuthority:
|
||||||
return evt.Str(string(field), entry.GetRequest().GetAuthority())
|
return evt.Str(string(field), entry.GetRequest().GetAuthority())
|
||||||
|
@ -76,6 +83,14 @@ func populateLogEvent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getRequestHeaders(entry *envoy_data_accesslog_v3.HTTPAccessLogEntry) map[string]string {
|
||||||
|
m := map[string]string{}
|
||||||
|
for k, v := range entry.GetRequest().GetRequestHeaders() {
|
||||||
|
m[http.CanonicalHeaderKey(k)] = v
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
func stripQueryString(str string) string {
|
func stripQueryString(str string) string {
|
||||||
if idx := strings.Index(str, "?"); idx != -1 {
|
if idx := strings.Index(str, "?"); idx != -1 {
|
||||||
str = str[:idx]
|
str = str[:idx]
|
||||||
|
|
|
@ -3,6 +3,7 @@ package log
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// An AccessLogField is a field in the access logs.
|
// An AccessLogField is a field in the access logs.
|
||||||
|
@ -42,6 +43,21 @@ func DefaultAccessLogFields() []AccessLogField {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const accessLogFieldHeaderPrefix = "header."
|
||||||
|
|
||||||
|
// AccessLogFieldForHeader returns an access log field for the given header name.
|
||||||
|
func AccessLogFieldForHeader(header string) AccessLogField {
|
||||||
|
return AccessLogField(accessLogFieldHeaderPrefix + header)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsForHeader returns true if the access log field is for a header.
|
||||||
|
func (field AccessLogField) IsForHeader() (headerName string, ok bool) {
|
||||||
|
if strings.HasPrefix(string(field), accessLogFieldHeaderPrefix) {
|
||||||
|
return string(field[len(accessLogFieldHeaderPrefix):]), true
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
// ErrUnknownAccessLogField indicates that an access log field is unknown.
|
// ErrUnknownAccessLogField indicates that an access log field is unknown.
|
||||||
var ErrUnknownAccessLogField = errors.New("unknown access log field")
|
var ErrUnknownAccessLogField = errors.New("unknown access log field")
|
||||||
|
|
||||||
|
@ -62,6 +78,10 @@ var accessLogFieldLookup = map[AccessLogField]struct{}{
|
||||||
|
|
||||||
// Validate returns an error if the access log field is invalid.
|
// Validate returns an error if the access log field is invalid.
|
||||||
func (field AccessLogField) Validate() error {
|
func (field AccessLogField) Validate() error {
|
||||||
|
if _, ok := field.IsForHeader(); ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
_, ok := accessLogFieldLookup[field]
|
_, ok := accessLogFieldLookup[field]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("%w: %s", ErrUnknownAccessLogField, field)
|
return fmt.Errorf("%w: %s", ErrUnknownAccessLogField, field)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue