Merge pull request #96502 from 249043822/br-hsts

Fix HSTS Missing From HTTPS Server(Nessus Scanner)
This commit is contained in:
Kubernetes Prow Robot 2021-01-13 06:58:36 -08:00 committed by GitHub
commit 33518271f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 89 additions and 1 deletions

View File

@ -112,7 +112,7 @@ type Config struct {
// to set values and determine whether its allowed
AdmissionControl admission.Interface
CorsAllowedOriginList []string
HSTSDirectives []string
// FlowControl, if not nil, gives priority and fairness to request handling
FlowControl utilflowcontrol.Interface
@ -755,6 +755,7 @@ func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
handler = genericapifilters.WithAuditAnnotations(handler, c.AuditBackend, c.AuditPolicyChecker)
handler = genericapifilters.WithWarningRecorder(handler)
handler = genericapifilters.WithCacheControl(handler)
handler = genericfilters.WithHSTS(handler, c.HSTSDirectives)
handler = genericapifilters.WithRequestReceivedTimestamp(handler)
handler = genericfilters.WithPanicRecovery(handler, c.RequestInfoResolver)
return handler

View File

@ -54,6 +54,7 @@ go_library(
"cors.go",
"doc.go",
"goaway.go",
"hsts.go",
"longrunning.go",
"maxinflight.go",
"priority-and-fairness.go",

View File

@ -0,0 +1,40 @@
/*
Copyright 2020 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package filters
import (
"net/http"
"strings"
)
// WithHSTS is a simple HSTS implementation that wraps an http Handler.
// If hstsDirectives is empty or nil, no HSTS support is installed.
func WithHSTS(handler http.Handler, hstsDirectives []string) http.Handler {
if len(hstsDirectives) == 0 {
return handler
}
allDirectives := strings.Join(hstsDirectives, "; ")
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
// Chrome and Mozilla Firefox maintain an HSTS preload list
// issue : golang.org/issue/26162
// Set the Strict-Transport-Security header if it is not already set
if _, ok := w.Header()["Strict-Transport-Security"]; !ok {
w.Header().Set("Strict-Transport-Security", allDirectives)
}
handler.ServeHTTP(w, req)
})
}

View File

@ -19,10 +19,12 @@ package options
import (
"fmt"
"net"
"strings"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apiserver/pkg/server"
utilfeature "k8s.io/apiserver/pkg/util/feature"
@ -34,6 +36,7 @@ type ServerRunOptions struct {
AdvertiseAddress net.IP
CorsAllowedOriginList []string
HSTSDirectives []string
ExternalHost string
MaxRequestsInFlight int
MaxMutatingRequestsInFlight int
@ -71,6 +74,7 @@ func NewServerRunOptions() *ServerRunOptions {
// ApplyTo applies the run options to the method receiver and returns self
func (s *ServerRunOptions) ApplyTo(c *server.Config) error {
c.CorsAllowedOriginList = s.CorsAllowedOriginList
c.HSTSDirectives = s.HSTSDirectives
c.ExternalAddress = s.ExternalHost
c.MaxRequestsInFlight = s.MaxRequestsInFlight
c.MaxMutatingRequestsInFlight = s.MaxMutatingRequestsInFlight
@ -143,9 +147,31 @@ func (s *ServerRunOptions) Validate() []error {
errors = append(errors, fmt.Errorf("--max-resource-write-bytes can not be negative value"))
}
if err := validateHSTSDirectives(s.HSTSDirectives); err != nil {
errors = append(errors, err)
}
return errors
}
func validateHSTSDirectives(hstsDirectives []string) error {
// HSTS Headers format: Strict-Transport-Security:max-age=expireTime [;includeSubDomains] [;preload]
// See https://tools.ietf.org/html/rfc6797#section-6.1 for more information
allErrors := []error{}
for _, hstsDirective := range hstsDirectives {
if len(strings.TrimSpace(hstsDirective)) == 0 {
allErrors = append(allErrors, fmt.Errorf("empty value in strict-transport-security-directives"))
continue
}
if hstsDirective != "includeSubDomains" && hstsDirective != "preload" {
maxAgeDirective := strings.Split(hstsDirective, "=")
if len(maxAgeDirective) != 2 || maxAgeDirective[0] != "max-age" {
allErrors = append(allErrors, fmt.Errorf("--strict-transport-security-directives invalid, allowed values: max-age=expireTime, includeSubDomains, preload. see https://tools.ietf.org/html/rfc6797#section-6.1 for more information"))
}
}
}
return errors.NewAggregate(allErrors)
}
// AddUniversalFlags adds flags for a specific APIServer to the specified FlagSet
func (s *ServerRunOptions) AddUniversalFlags(fs *pflag.FlagSet) {
// Note: the weird ""+ in below lines seems to be the only way to get gofmt to
@ -161,6 +187,10 @@ func (s *ServerRunOptions) AddUniversalFlags(fs *pflag.FlagSet) {
"List of allowed origins for CORS, comma separated. An allowed origin can be a regular "+
"expression to support subdomain matching. If this list is empty CORS will not be enabled.")
fs.StringSliceVar(&s.HSTSDirectives, "strict-transport-security-directives", s.HSTSDirectives, ""+
"List of directives for HSTS, comma separated. If this list is empty, then HSTS directives will not "+
"be added. Example: 'max-age=31536000,includeSubDomains,preload'")
deprecatedTargetRAMMB := 0
fs.IntVar(&deprecatedTargetRAMMB, "target-ram-mb", deprecatedTargetRAMMB,
"DEPRECATED: Memory limit for apiserver in MB (used to configure sizes of caches, etc.)")

View File

@ -145,11 +145,27 @@ func TestServerRunOptionsValidate(t *testing.T) {
},
expectErr: "--shutdown-delay-duration can not be negative value",
},
{
name: "Test when HSTSHeaders is valid",
testOptions: &ServerRunOptions{
AdvertiseAddress: net.ParseIP("192.168.10.10"),
CorsAllowedOriginList: []string{"10.10.10.100", "10.10.10.200"},
HSTSDirectives: []string{"fakevalue", "includeSubDomains", "preload"},
MaxRequestsInFlight: 400,
MaxMutatingRequestsInFlight: 200,
RequestTimeout: time.Duration(2) * time.Minute,
MinRequestTimeout: 1800,
JSONPatchMaxCopyBytes: 10 * 1024 * 1024,
MaxRequestBodyBytes: 10 * 1024 * 1024,
},
expectErr: "--strict-transport-security-directives invalid, allowed values: max-age=expireTime, includeSubDomains, preload. see https://tools.ietf.org/html/rfc6797#section-6.1 for more information",
},
{
name: "Test when ServerRunOptions is valid",
testOptions: &ServerRunOptions{
AdvertiseAddress: net.ParseIP("192.168.10.10"),
CorsAllowedOriginList: []string{"10.10.10.100", "10.10.10.200"},
HSTSDirectives: []string{"max-age=31536000", "includeSubDomains", "preload"},
MaxRequestsInFlight: 400,
MaxMutatingRequestsInFlight: 200,
RequestTimeout: time.Duration(2) * time.Minute,