Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HTTP cookie capture #147

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions images/router/haproxy/conf/haproxy-config.template
Expand Up @@ -173,6 +173,9 @@ frontend public
{{- range $idx, $captureHeader := .CaptureHTTPResponseHeaders }}
capture response header {{ $captureHeader.Name }} len {{ $captureHeader.MaxLength }}
{{- end }}
{{- with $captureCookie := .CaptureHTTPCookie }}
capture cookie {{ $captureCookie.Name }}{{ if eq $captureCookie.MatchType "exact" }}={{ end }} len {{ $captureCookie.MaxLength }}
{{- end }}

# Strip off Proxy headers to prevent HTTpoxy (https://httpoxy.org/)
http-request del-header Proxy
Expand Down Expand Up @@ -253,6 +256,9 @@ frontend fe_sni
{{- range $idx, $captureHeader := .CaptureHTTPResponseHeaders }}
capture response header {{ $captureHeader.Name }} len {{ $captureHeader.MaxLength }}
{{- end }}
{{- with $captureCookie := .CaptureHTTPCookie }}
capture cookie {{ $captureCookie.Name }}{{ if eq $captureCookie.MatchType "exact" }}={{ end }} len {{ $captureCookie.MaxLength }}
{{- end }}

# Strip off Proxy headers to prevent HTTpoxy (https://httpoxy.org/)
http-request del-header Proxy
Expand Down Expand Up @@ -331,6 +337,9 @@ frontend fe_no_sni
{{- range $idx, $captureHeader := .CaptureHTTPResponseHeaders }}
capture response header {{ $captureHeader.Name }} len {{ $captureHeader.MaxLength }}
{{- end }}
{{- with $captureCookie := .CaptureHTTPCookie }}
capture cookie {{ $captureCookie.Name }}{{ if eq $captureCookie.MatchType "exact" }}={{ end }} len {{ $captureCookie.MaxLength }}
{{- end }}

# Strip off Proxy headers to prevent HTTpoxy (https://httpoxy.org/)
http-request del-header Proxy
Expand Down
42 changes: 42 additions & 0 deletions pkg/cmd/infra/router/template.go
Expand Up @@ -120,8 +120,10 @@ type TemplateRouter struct {
MetricsType string
CaptureHTTPRequestHeadersString string
CaptureHTTPResponseHeadersString string
CaptureHTTPCookieString string
CaptureHTTPRequestHeaders []templateplugin.CaptureHTTPHeader
CaptureHTTPResponseHeaders []templateplugin.CaptureHTTPHeader
CaptureHTTPCookie *templateplugin.CaptureHTTPCookie

TemplateRouterConfigManager
}
Expand Down Expand Up @@ -176,6 +178,7 @@ func (o *TemplateRouter) Bind(flag *pflag.FlagSet) {
flag.IntVar(&o.MaxDynamicServers, "max-dynamic-servers", int(envInt("ROUTER_MAX_DYNAMIC_SERVERS", 5, 1)), "Specifies the maximum number of dynamic servers added to a route for use by the router specific dynamic configuration manager.")
flag.StringVar(&o.CaptureHTTPRequestHeadersString, "capture-http-request-headers", env("ROUTER_CAPTURE_HTTP_REQUEST_HEADERS", ""), "A comma-delimited list of HTTP request header names and maximum header value lengths that should be captured for logging. Each item must have the following form: name:maxLength")
flag.StringVar(&o.CaptureHTTPResponseHeadersString, "capture-http-response-headers", env("ROUTER_CAPTURE_HTTP_RESPONSE_HEADERS", ""), "A comma-delimited list of HTTP response header names and maximum header value lengths that should be captured for logging. Each item must have the following form: name:maxLength")
flag.StringVar(&o.CaptureHTTPCookieString, "capture-http-cookie", env("ROUTER_CAPTURE_HTTP_COOKIE", ""), "Name and maximum length of HTTP cookie that should be captured for logging. The argument must have the following form: name:maxLength. Append '=' to the name to indicate that an exact match should be performed; otherwise a prefix match will be performed. The value of first cookie that matches the name is captured.")
}

type RouterStats struct {
Expand Down Expand Up @@ -277,6 +280,38 @@ func parseCaptureHeaders(in string) ([]templateplugin.CaptureHTTPHeader, error)
return captureHeaders, nil
}

func parseCaptureCookie(in string) (*templateplugin.CaptureHTTPCookie, error) {
if len(in) == 0 {
return nil, nil
}

parts := strings.Split(in, ":")
if len(parts) != 2 {
return nil, fmt.Errorf("invalid HTTP cookie capture specification: %v", in)
}
cookieName := parts[0]
matchType := templateplugin.CookieMatchTypePrefix
if strings.HasSuffix(cookieName, "=") {
cookieName = cookieName[:len(cookieName)-1]
matchType = templateplugin.CookieMatchTypeExact
}
// RFC 6265 section 4.1 states that the cookie name must be a
// valid token.
if !validTokenRE.MatchString(cookieName) {
return nil, fmt.Errorf("invalid HTTP cookie name: %v", cookieName)
}
maxLength, err := strconv.Atoi(parts[1])
if err != nil {
return nil, err
}

return &templateplugin.CaptureHTTPCookie{
Name: cookieName,
MaxLength: maxLength,
MatchType: matchType,
}, nil
}

func (o *TemplateRouterOptions) Complete() error {
routerSvcName := env("ROUTER_SERVICE_NAME", "")
routerSvcNamespace := env("ROUTER_SERVICE_NAMESPACE", "")
Expand Down Expand Up @@ -330,6 +365,12 @@ func (o *TemplateRouterOptions) Complete() error {
}
o.CaptureHTTPResponseHeaders = captureHTTPResponseHeaders

captureHTTPCookie, err := parseCaptureCookie(o.CaptureHTTPCookieString)
if err != nil {
return err
}
o.CaptureHTTPCookie = captureHTTPCookie

return o.RouterSelection.Complete()
}

Expand Down Expand Up @@ -559,6 +600,7 @@ func (o *TemplateRouterOptions) Run(stopCh <-chan struct{}) error {
DynamicConfigManager: cfgManager,
CaptureHTTPRequestHeaders: o.CaptureHTTPRequestHeaders,
CaptureHTTPResponseHeaders: o.CaptureHTTPResponseHeaders,
CaptureHTTPCookie: o.CaptureHTTPCookie,
}

svcFetcher := templateplugin.NewListWatchServiceLookup(kc.CoreV1(), o.ResyncInterval, o.Namespace)
Expand Down
2 changes: 2 additions & 0 deletions pkg/router/template/plugin.go
Expand Up @@ -63,6 +63,7 @@ type TemplatePluginConfig struct {
DynamicConfigManager ConfigManager
CaptureHTTPRequestHeaders []CaptureHTTPHeader
CaptureHTTPResponseHeaders []CaptureHTTPHeader
CaptureHTTPCookie *CaptureHTTPCookie
}

// RouterInterface controls the interaction of the plugin with the underlying router implementation
Expand Down Expand Up @@ -155,6 +156,7 @@ func NewTemplatePlugin(cfg TemplatePluginConfig, lookupSvc ServiceLookup) (*Temp
dynamicConfigManager: cfg.DynamicConfigManager,
captureHTTPRequestHeaders: cfg.CaptureHTTPRequestHeaders,
captureHTTPResponseHeaders: cfg.CaptureHTTPResponseHeaders,
captureHTTPCookie: cfg.CaptureHTTPCookie,
}
router, err := newTemplateRouter(templateRouterCfg)
return newDefaultTemplatePlugin(router, cfg.IncludeUDP, lookupSvc), err
Expand Down
9 changes: 9 additions & 0 deletions pkg/router/template/router.go
Expand Up @@ -112,6 +112,9 @@ type templateRouter struct {
// captureHTTPResponseHeaders specifies HTTP response headers
// that should be captured for logging.
captureHTTPResponseHeaders []CaptureHTTPHeader
// captureHTTPCookie specifies an HTTP cookie that should be
// captured for logging.
captureHTTPCookie *CaptureHTTPCookie
}

// templateRouterCfg holds all configuration items required to initialize the template router
Expand All @@ -135,6 +138,7 @@ type templateRouterCfg struct {
dynamicConfigManager ConfigManager
captureHTTPRequestHeaders []CaptureHTTPHeader
captureHTTPResponseHeaders []CaptureHTTPHeader
captureHTTPCookie *CaptureHTTPCookie
}

// templateConfig is a subset of the templateRouter information that should be passed to the template for generating
Expand Down Expand Up @@ -168,6 +172,9 @@ type templateData struct {
// CaptureHTTPResponseHeaders specifies HTTP response headers
// that should be captured for logging.
CaptureHTTPResponseHeaders []CaptureHTTPHeader
// CaptureHTTPCookie specifies an HTTP cookie that should be
// captured for logging.
CaptureHTTPCookie *CaptureHTTPCookie
}

func newTemplateRouter(cfg templateRouterCfg) (*templateRouter, error) {
Expand Down Expand Up @@ -221,6 +228,7 @@ func newTemplateRouter(cfg templateRouterCfg) (*templateRouter, error) {
dynamicConfigManager: cfg.dynamicConfigManager,
captureHTTPRequestHeaders: cfg.captureHTTPRequestHeaders,
captureHTTPResponseHeaders: cfg.captureHTTPResponseHeaders,
captureHTTPCookie: cfg.captureHTTPCookie,

metricReload: metricsReload,
metricWriteConfig: metricWriteConfig,
Expand Down Expand Up @@ -510,6 +518,7 @@ func (r *templateRouter) writeConfig() error {
DisableHTTP2: disableHTTP2,
CaptureHTTPRequestHeaders: r.captureHTTPRequestHeaders,
CaptureHTTPResponseHeaders: r.captureHTTPResponseHeaders,
CaptureHTTPCookie: r.captureHTTPCookie,
}
if err := template.Execute(file, data); err != nil {
file.Close()
Expand Down
26 changes: 26 additions & 0 deletions pkg/router/template/types.go
Expand Up @@ -235,6 +235,32 @@ type CaptureHTTPHeader struct {
MaxLength int
}

// CaptureHTTPCookie specifies an HTTP cookie that should be captured
// for access logs.
type CaptureHTTPCookie struct {
// Name specifies an HTTP cookie name.
Name string

// MaxLength specifies a maximum length for the cookie value.
MaxLength int

// MatchType specifies the type of match to be performed on the cookie
// name.
MatchType CookieMatchType
}

// CookieMatchType indicates the type of matching used against cookie names to
// select a cookie for capture.
type CookieMatchType string

const (
// CookieMatchTypeExact indicates that an exact match should be performed.
CookieMatchTypeExact CookieMatchType = "exact"

// CookieMatchTypePrefix indicates that a prefix match should be performed.
CookieMatchTypePrefix CookieMatchType = "prefix"
)

// RouterEventType indicates the type of event fired by the router.
type RouterEventType string

Expand Down