Skip to content

Commit

Permalink
Allow configuring HTTP header name case adjustment
Browse files Browse the repository at this point in the history
Implement the --http-header-name-case-adjustments flag and
ROUTER_H1_CASE_ADJUST environment variable to allow specifying case
adjustments for HTTP header names.

* images/router/haproxy/conf/haproxy-config.template: Add "option
h1-case-adjust-bogus-client" and "h1-case-adjust" settings to the global
configuration if any adjustments are specified.  Add "option
h1-case-adjust-bogus-server" settings to routes that have the
haproxy.router.openshift.io/h1-adjust-case annotation.
* pkg/cmd/infra/router/template.go (TemplateRouter): Add
HTTPHeaderNameCaseAdjustmentsString and HTTPHeaderNameCaseAdjustment
fields.
(Bind): Add --http-header-name-case-adjustments, which defaults to the
value of the ROUTER_H1_CASE_ADJUST environment variable.
(parseHTTPHeaderNameCaseAdjustments): New function.  Parse a string
value (such as provided with the newly added command-line flag) into a
slice of HTTPHeaderNameCaseAdjustment values.
(Complete): Use parseHTTPHeaderNameCaseAdjustments to parse
HTTPHeaderNameCaseAdjustmentsString into HTTPHeaderNameCaseAdjustments.
(Run): Specify HTTPHeaderNameCaseAdjustments in the plugin config.
* pkg/router/template/plugin.go (TemplatePluginConfig): Add
HTTPHeaderNameCaseAdjustments field.
(NewTemplatePlugin): Specify HTTPHeaderNameCaseAdjustments in the internal
template router config.
* pkg/router/template/router.go (templateRouter, templateRouterCfg): Add
httpHeaderNameCaseAdjustments field.
(templateData): Add HTTPHeaderNameCaseAdjustments field.
(newTemplateRouter): Specify httpHeaderNameCaseAdjustments in the template
router.
(writeConfig): Specify HTTPHeaderNameCaseAdjustments in the template
parameters.
* pkg/router/template/types.go (HTTPHeaderNameCaseAdjustments): New type.
Specify an HTTP header name adjustment.
  • Loading branch information
Miciah committed Nov 26, 2020
1 parent 3d2a691 commit e89d2cb
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 139 deletions.
13 changes: 13 additions & 0 deletions images/router/haproxy/conf/haproxy-config.template
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ global
tune.maxrewrite 8192
tune.bufsize 32768

{{- with $adjustments := .HTTPHeaderNameCaseAdjustments }}
option h1-case-adjust-bogus-client
{{- range $idx, $adjustment := $adjustments }}
h1-case-adjust {{ $adjustment.From }} {{ $adjustment.To }}
{{- end }}
{{- end }}

# Configure the TLS versions we support
ssl-default-bind-options ssl-min-ver {{env "SSL_MIN_VERSION" "TLSv1.2"}}
{{- if ne (env "SSL_MAX_VERSION" "") "" }} ssl-max-ver {{env "SSL_MAX_VERSION"}}{{ end }}
Expand Down Expand Up @@ -455,6 +462,12 @@ backend {{genBackendNamePrefix $cfg.TLSTermination}}:{{$cfgIdx}}
{{- end }}
{{- end}}

{{- with $adjustments := $.HTTPHeaderNameCaseAdjustments }}
{{- if isTrue (index $cfg.Annotations "haproxy.router.openshift.io/h1-adjust-case") }}
option h1-case-adjust-bogus-server
{{- end }}
{{- end }}

{{- with $balanceAlgo := firstMatch "roundrobin|leastconn|source" (index $cfg.Annotations "haproxy.router.openshift.io/balance") (env "ROUTER_LOAD_BALANCE_ALGORITHM") }}
balance {{ $balanceAlgo }}
{{- else }}
Expand Down
113 changes: 72 additions & 41 deletions pkg/cmd/infra/router/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,25 +105,27 @@ type TemplateRouterOptions struct {
}

type TemplateRouter struct {
WorkingDir string
TemplateFile string
ReloadScript string
ReloadInterval time.Duration
DefaultCertificate string
DefaultCertificatePath string
DefaultCertificateDir string
DefaultDestinationCAPath string
BindPortsAfterSync bool
MaxConnections string
Ciphers string
StrictSNI bool
MetricsType string
CaptureHTTPRequestHeadersString string
CaptureHTTPResponseHeadersString string
CaptureHTTPCookieString string
CaptureHTTPRequestHeaders []templateplugin.CaptureHTTPHeader
CaptureHTTPResponseHeaders []templateplugin.CaptureHTTPHeader
CaptureHTTPCookie *templateplugin.CaptureHTTPCookie
WorkingDir string
TemplateFile string
ReloadScript string
ReloadInterval time.Duration
DefaultCertificate string
DefaultCertificatePath string
DefaultCertificateDir string
DefaultDestinationCAPath string
BindPortsAfterSync bool
MaxConnections string
Ciphers string
StrictSNI bool
MetricsType string
CaptureHTTPRequestHeadersString string
CaptureHTTPResponseHeadersString string
CaptureHTTPCookieString string
CaptureHTTPRequestHeaders []templateplugin.CaptureHTTPHeader
CaptureHTTPResponseHeaders []templateplugin.CaptureHTTPHeader
CaptureHTTPCookie *templateplugin.CaptureHTTPCookie
HTTPHeaderNameCaseAdjustmentsString string
HTTPHeaderNameCaseAdjustments []templateplugin.HTTPHeaderNameCaseAdjustment

TemplateRouterConfigManager
}
Expand Down Expand Up @@ -179,6 +181,7 @@ func (o *TemplateRouter) Bind(flag *pflag.FlagSet) {
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.")
flag.StringVar(&o.HTTPHeaderNameCaseAdjustmentsString, "http-header-name-case-adjustments", env("ROUTER_H1_CASE_ADJUST", ""), "A comma-delimited list of HTTP header names that should have their case adjusted. Each item must be a valid HTTP header name and should have the desired capitalization.")
}

type RouterStats struct {
Expand Down Expand Up @@ -312,6 +315,27 @@ func parseCaptureCookie(in string) (*templateplugin.CaptureHTTPCookie, error) {
}, nil
}

func parseHTTPHeaderNameCaseAdjustments(in string) ([]templateplugin.HTTPHeaderNameCaseAdjustment, error) {
var adjustments []templateplugin.HTTPHeaderNameCaseAdjustment

if len(in) > 0 {
for _, headerName := range strings.Split(in, ",") {
// RFC 2616, section 4.2, states that the header name
// must be a valid token.
if !validTokenRE.MatchString(headerName) {
return adjustments, fmt.Errorf("invalid HTTP header name: %v", headerName)
}
adjustment := templateplugin.HTTPHeaderNameCaseAdjustment{
From: strings.ToLower(headerName),
To: headerName,
}
adjustments = append(adjustments, adjustment)
}
}

return adjustments, nil
}

func (o *TemplateRouterOptions) Complete() error {
routerSvcName := env("ROUTER_SERVICE_NAME", "")
routerSvcNamespace := env("ROUTER_SERVICE_NAMESPACE", "")
Expand Down Expand Up @@ -371,6 +395,12 @@ func (o *TemplateRouterOptions) Complete() error {
}
o.CaptureHTTPCookie = captureHTTPCookie

httpHeaderNameCaseAdjustments, err := parseHTTPHeaderNameCaseAdjustments(o.HTTPHeaderNameCaseAdjustmentsString)
if err != nil {
return err
}
o.HTTPHeaderNameCaseAdjustments = httpHeaderNameCaseAdjustments

return o.RouterSelection.Complete()
}

Expand Down Expand Up @@ -579,28 +609,29 @@ func (o *TemplateRouterOptions) Run(stopCh <-chan struct{}) error {
}

pluginCfg := templateplugin.TemplatePluginConfig{
WorkingDir: o.WorkingDir,
TemplatePath: o.TemplateFile,
ReloadScriptPath: o.ReloadScript,
ReloadInterval: o.ReloadInterval,
ReloadCallbacks: reloadCallbacks,
DefaultCertificate: o.DefaultCertificate,
DefaultCertificatePath: o.DefaultCertificatePath,
DefaultCertificateDir: o.DefaultCertificateDir,
DefaultDestinationCAPath: o.DefaultDestinationCAPath,
StatsPort: statsPort,
StatsUsername: o.StatsUsername,
StatsPassword: o.StatsPassword,
BindPortsAfterSync: o.BindPortsAfterSync,
IncludeUDP: o.RouterSelection.IncludeUDP,
AllowWildcardRoutes: o.RouterSelection.AllowWildcardRoutes,
MaxConnections: o.MaxConnections,
Ciphers: o.Ciphers,
StrictSNI: o.StrictSNI,
DynamicConfigManager: cfgManager,
CaptureHTTPRequestHeaders: o.CaptureHTTPRequestHeaders,
CaptureHTTPResponseHeaders: o.CaptureHTTPResponseHeaders,
CaptureHTTPCookie: o.CaptureHTTPCookie,
WorkingDir: o.WorkingDir,
TemplatePath: o.TemplateFile,
ReloadScriptPath: o.ReloadScript,
ReloadInterval: o.ReloadInterval,
ReloadCallbacks: reloadCallbacks,
DefaultCertificate: o.DefaultCertificate,
DefaultCertificatePath: o.DefaultCertificatePath,
DefaultCertificateDir: o.DefaultCertificateDir,
DefaultDestinationCAPath: o.DefaultDestinationCAPath,
StatsPort: statsPort,
StatsUsername: o.StatsUsername,
StatsPassword: o.StatsPassword,
BindPortsAfterSync: o.BindPortsAfterSync,
IncludeUDP: o.RouterSelection.IncludeUDP,
AllowWildcardRoutes: o.RouterSelection.AllowWildcardRoutes,
MaxConnections: o.MaxConnections,
Ciphers: o.Ciphers,
StrictSNI: o.StrictSNI,
DynamicConfigManager: cfgManager,
CaptureHTTPRequestHeaders: o.CaptureHTTPRequestHeaders,
CaptureHTTPResponseHeaders: o.CaptureHTTPResponseHeaders,
CaptureHTTPCookie: o.CaptureHTTPCookie,
HTTPHeaderNameCaseAdjustments: o.HTTPHeaderNameCaseAdjustments,
}

svcFetcher := templateplugin.NewListWatchServiceLookup(kc.CoreV1(), o.ResyncInterval, o.Namespace)
Expand Down
86 changes: 44 additions & 42 deletions pkg/router/template/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,29 +42,30 @@ func newDefaultTemplatePlugin(router RouterInterface, includeUDP bool, lookupSvc
}

type TemplatePluginConfig struct {
WorkingDir string
TemplatePath string
ReloadScriptPath string
ReloadFn func(shutdown bool) error
ReloadInterval time.Duration
ReloadCallbacks []func()
DefaultCertificate string
DefaultCertificatePath string
DefaultCertificateDir string
DefaultDestinationCAPath string
StatsPort int
StatsUsername string
StatsPassword string
IncludeUDP bool
AllowWildcardRoutes bool
BindPortsAfterSync bool
MaxConnections string
Ciphers string
StrictSNI bool
DynamicConfigManager ConfigManager
CaptureHTTPRequestHeaders []CaptureHTTPHeader
CaptureHTTPResponseHeaders []CaptureHTTPHeader
CaptureHTTPCookie *CaptureHTTPCookie
WorkingDir string
TemplatePath string
ReloadScriptPath string
ReloadFn func(shutdown bool) error
ReloadInterval time.Duration
ReloadCallbacks []func()
DefaultCertificate string
DefaultCertificatePath string
DefaultCertificateDir string
DefaultDestinationCAPath string
StatsPort int
StatsUsername string
StatsPassword string
IncludeUDP bool
AllowWildcardRoutes bool
BindPortsAfterSync bool
MaxConnections string
Ciphers string
StrictSNI bool
DynamicConfigManager ConfigManager
CaptureHTTPRequestHeaders []CaptureHTTPHeader
CaptureHTTPResponseHeaders []CaptureHTTPHeader
CaptureHTTPCookie *CaptureHTTPCookie
HTTPHeaderNameCaseAdjustments []HTTPHeaderNameCaseAdjustment
}

// RouterInterface controls the interaction of the plugin with the underlying router implementation
Expand Down Expand Up @@ -139,25 +140,26 @@ func NewTemplatePlugin(cfg TemplatePluginConfig, lookupSvc ServiceLookup) (*Temp
}

templateRouterCfg := templateRouterCfg{
dir: cfg.WorkingDir,
templates: templates,
reloadScriptPath: cfg.ReloadScriptPath,
reloadFn: cfg.ReloadFn,
reloadInterval: cfg.ReloadInterval,
reloadCallbacks: cfg.ReloadCallbacks,
defaultCertificate: cfg.DefaultCertificate,
defaultCertificatePath: cfg.DefaultCertificatePath,
defaultCertificateDir: cfg.DefaultCertificateDir,
defaultDestinationCAPath: cfg.DefaultDestinationCAPath,
statsUser: cfg.StatsUsername,
statsPassword: cfg.StatsPassword,
statsPort: cfg.StatsPort,
allowWildcardRoutes: cfg.AllowWildcardRoutes,
bindPortsAfterSync: cfg.BindPortsAfterSync,
dynamicConfigManager: cfg.DynamicConfigManager,
captureHTTPRequestHeaders: cfg.CaptureHTTPRequestHeaders,
captureHTTPResponseHeaders: cfg.CaptureHTTPResponseHeaders,
captureHTTPCookie: cfg.CaptureHTTPCookie,
dir: cfg.WorkingDir,
templates: templates,
reloadScriptPath: cfg.ReloadScriptPath,
reloadFn: cfg.ReloadFn,
reloadInterval: cfg.ReloadInterval,
reloadCallbacks: cfg.ReloadCallbacks,
defaultCertificate: cfg.DefaultCertificate,
defaultCertificatePath: cfg.DefaultCertificatePath,
defaultCertificateDir: cfg.DefaultCertificateDir,
defaultDestinationCAPath: cfg.DefaultDestinationCAPath,
statsUser: cfg.StatsUsername,
statsPassword: cfg.StatsPassword,
statsPort: cfg.StatsPort,
allowWildcardRoutes: cfg.AllowWildcardRoutes,
bindPortsAfterSync: cfg.BindPortsAfterSync,
dynamicConfigManager: cfg.DynamicConfigManager,
captureHTTPRequestHeaders: cfg.CaptureHTTPRequestHeaders,
captureHTTPResponseHeaders: cfg.CaptureHTTPResponseHeaders,
captureHTTPCookie: cfg.CaptureHTTPCookie,
httpHeaderNameCaseAdjustments: cfg.HTTPHeaderNameCaseAdjustments,
}
router, err := newTemplateRouter(templateRouterCfg)
return newDefaultTemplatePlugin(router, cfg.IncludeUDP, lookupSvc), err
Expand Down

0 comments on commit e89d2cb

Please sign in to comment.