Implement audit policy logic
This commit is contained in:
		@@ -118,4 +118,5 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
 | 
			
		||||
	// inherited features from generic apiserver, relisted here to get a conflict if it is changed
 | 
			
		||||
	// unintentionally on either side:
 | 
			
		||||
	StreamingProxyRedirects:          {Default: true, PreRelease: utilfeature.Beta},
 | 
			
		||||
	genericfeatures.AdvancedAuditing: {Default: false, PreRelease: utilfeature.Alpha},
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -16,8 +16,10 @@ go_library(
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/endpoints/filters:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/features:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/server:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/server/filters:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/client-go/rest:go_default_library",
 | 
			
		||||
    ],
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -25,8 +25,10 @@ import (
 | 
			
		||||
	"k8s.io/apiserver/pkg/authentication/user"
 | 
			
		||||
	genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
 | 
			
		||||
	apirequest "k8s.io/apiserver/pkg/endpoints/request"
 | 
			
		||||
	"k8s.io/apiserver/pkg/features"
 | 
			
		||||
	"k8s.io/apiserver/pkg/server"
 | 
			
		||||
	genericfilters "k8s.io/apiserver/pkg/server/filters"
 | 
			
		||||
	utilfeature "k8s.io/apiserver/pkg/util/feature"
 | 
			
		||||
	"k8s.io/client-go/rest"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -35,7 +37,12 @@ import (
 | 
			
		||||
// InsecureServingInfo *ServingInfo
 | 
			
		||||
 | 
			
		||||
func BuildInsecureHandlerChain(apiHandler http.Handler, c *server.Config) http.Handler {
 | 
			
		||||
	handler := genericapifilters.WithAudit(apiHandler, c.RequestContextMapper, c.AuditBackend, c.AuditPolicy, c.LongRunningFunc)
 | 
			
		||||
	handler := apiHandler
 | 
			
		||||
	if utilfeature.DefaultFeatureGate.Enabled(features.AdvancedAuditing) {
 | 
			
		||||
		handler = genericapifilters.WithAudit(handler, c.RequestContextMapper, c.AuditBackend, c.AuditPolicyChecker, c.LongRunningFunc)
 | 
			
		||||
	} else {
 | 
			
		||||
		handler = genericapifilters.WithLegacyAudit(handler, c.RequestContextMapper, c.LegacyAuditWriter)
 | 
			
		||||
	}
 | 
			
		||||
	handler = genericapifilters.WithAuthentication(handler, c.RequestContextMapper, insecureSuperuser{}, nil)
 | 
			
		||||
	handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
 | 
			
		||||
	handler = genericfilters.WithPanicRecovery(handler)
 | 
			
		||||
 
 | 
			
		||||
@@ -36,11 +36,3 @@ func (a Level) Less(b Level) bool {
 | 
			
		||||
func (a Level) GreaterOrEqual(b Level) bool {
 | 
			
		||||
	return ordLevel(a) >= ordLevel(b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewConstantPolicy(level Level) *Policy {
 | 
			
		||||
	return &Policy{
 | 
			
		||||
		Rules: []PolicyRule{
 | 
			
		||||
			{Level: level},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								staging/src/k8s.io/apiserver/pkg/apis/audit/validation/BUILD
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								staging/src/k8s.io/apiserver/pkg/apis/audit/validation/BUILD
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
package(default_visibility = ["//visibility:public"])
 | 
			
		||||
 | 
			
		||||
licenses(["notice"])
 | 
			
		||||
 | 
			
		||||
load(
 | 
			
		||||
    "@io_bazel_rules_go//go:def.bzl",
 | 
			
		||||
    "go_library",
 | 
			
		||||
    "go_test",
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
go_test(
 | 
			
		||||
    name = "go_default_test",
 | 
			
		||||
    srcs = ["validation_test.go"],
 | 
			
		||||
    library = ":go_default_library",
 | 
			
		||||
    tags = ["automanaged"],
 | 
			
		||||
    deps = ["//vendor/k8s.io/apiserver/pkg/apis/audit:go_default_library"],
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
go_library(
 | 
			
		||||
    name = "go_default_library",
 | 
			
		||||
    srcs = ["validation.go"],
 | 
			
		||||
    tags = ["automanaged"],
 | 
			
		||||
    deps = [
 | 
			
		||||
        "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/apis/audit:go_default_library",
 | 
			
		||||
    ],
 | 
			
		||||
)
 | 
			
		||||
@@ -0,0 +1,62 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2017 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 validation
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/validation/field"
 | 
			
		||||
	"k8s.io/apiserver/pkg/apis/audit"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func ValidatePolicy(policy *audit.Policy) field.ErrorList {
 | 
			
		||||
	var allErrs field.ErrorList
 | 
			
		||||
	rulePath := field.NewPath("rules")
 | 
			
		||||
	for i, rule := range policy.Rules {
 | 
			
		||||
		allErrs = append(allErrs, validatePolicyRule(rule, rulePath.Index(i))...)
 | 
			
		||||
	}
 | 
			
		||||
	return allErrs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validatePolicyRule(rule audit.PolicyRule, fldPath *field.Path) field.ErrorList {
 | 
			
		||||
	var allErrs field.ErrorList
 | 
			
		||||
	allErrs = append(allErrs, validateLevel(rule.Level, fldPath.Child("level"))...)
 | 
			
		||||
 | 
			
		||||
	if len(rule.NonResourceURLs) > 0 {
 | 
			
		||||
		if len(rule.Resources) > 0 || len(rule.Namespaces) > 0 {
 | 
			
		||||
			allErrs = append(allErrs, field.Invalid(fldPath.Child("nonResourceURLs"), rule.NonResourceURLs, "rules cannot apply to both regular resources and non-resource URLs"))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return allErrs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var validLevels = []string{
 | 
			
		||||
	string(audit.LevelNone),
 | 
			
		||||
	string(audit.LevelMetadata),
 | 
			
		||||
	string(audit.LevelRequest),
 | 
			
		||||
	string(audit.LevelRequestResponse),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateLevel(level audit.Level, fldPath *field.Path) field.ErrorList {
 | 
			
		||||
	switch level {
 | 
			
		||||
	case audit.LevelNone, audit.LevelMetadata, audit.LevelRequest, audit.LevelRequestResponse:
 | 
			
		||||
		return nil
 | 
			
		||||
	case "":
 | 
			
		||||
		return field.ErrorList{field.Required(fldPath, "")}
 | 
			
		||||
	default:
 | 
			
		||||
		return field.ErrorList{field.NotSupported(fldPath, level, validLevels)}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,89 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2017 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 validation
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apiserver/pkg/apis/audit"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestValidatePolicy(t *testing.T) {
 | 
			
		||||
	validRules := []audit.PolicyRule{
 | 
			
		||||
		{ // Defaulting rule
 | 
			
		||||
			Level: audit.LevelMetadata,
 | 
			
		||||
		}, { // Matching non-humans
 | 
			
		||||
			Level:      audit.LevelNone,
 | 
			
		||||
			UserGroups: []string{"system:serviceaccounts", "system:nodes"},
 | 
			
		||||
		}, { // Specific request
 | 
			
		||||
			Level:      audit.LevelRequestResponse,
 | 
			
		||||
			Verbs:      []string{"get"},
 | 
			
		||||
			Resources:  []audit.GroupResources{{Resources: []string{"secrets"}}},
 | 
			
		||||
			Namespaces: []string{"kube-system"},
 | 
			
		||||
		}, { // Some non-resource URLs
 | 
			
		||||
			Level:      audit.LevelMetadata,
 | 
			
		||||
			UserGroups: []string{"developers"},
 | 
			
		||||
			NonResourceURLs: []string{
 | 
			
		||||
				"/logs*",
 | 
			
		||||
				"/healthz*",
 | 
			
		||||
				"/metrics",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	successCases := []audit.Policy{}
 | 
			
		||||
	for _, rule := range validRules {
 | 
			
		||||
		successCases = append(successCases, audit.Policy{Rules: []audit.PolicyRule{rule}})
 | 
			
		||||
	}
 | 
			
		||||
	successCases = append(successCases, audit.Policy{})                  // Empty policy is valid.
 | 
			
		||||
	successCases = append(successCases, audit.Policy{Rules: validRules}) // Multiple rules.
 | 
			
		||||
 | 
			
		||||
	for i, policy := range successCases {
 | 
			
		||||
		if errs := ValidatePolicy(&policy); len(errs) != 0 {
 | 
			
		||||
			t.Errorf("[%d] Expected policy %#v to be valid: %v", i, policy, errs)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	invalidRules := []audit.PolicyRule{
 | 
			
		||||
		{}, // Empty rule (missing Level)
 | 
			
		||||
		{ // Missing level
 | 
			
		||||
			Verbs:      []string{"get"},
 | 
			
		||||
			Resources:  []audit.GroupResources{{Resources: []string{"secrets"}}},
 | 
			
		||||
			Namespaces: []string{"kube-system"},
 | 
			
		||||
		}, { // Invalid Level
 | 
			
		||||
			Level: "FooBar",
 | 
			
		||||
		}, { // NonResourceURLs + Namespaces
 | 
			
		||||
			Level:           audit.LevelMetadata,
 | 
			
		||||
			Namespaces:      []string{"default"},
 | 
			
		||||
			NonResourceURLs: []string{"/logs*"},
 | 
			
		||||
		}, { // NonResourceURLs + ResourceKinds
 | 
			
		||||
			Level:           audit.LevelMetadata,
 | 
			
		||||
			Resources:       []audit.GroupResources{{Resources: []string{"secrets"}}},
 | 
			
		||||
			NonResourceURLs: []string{"/logs*"},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	errorCases := []audit.Policy{}
 | 
			
		||||
	for _, rule := range invalidRules {
 | 
			
		||||
		errorCases = append(errorCases, audit.Policy{Rules: []audit.PolicyRule{rule}})
 | 
			
		||||
	}
 | 
			
		||||
	errorCases = append(errorCases, audit.Policy{Rules: append(validRules, audit.PolicyRule{})}) // Multiple rules.
 | 
			
		||||
 | 
			
		||||
	for i, policy := range errorCases {
 | 
			
		||||
		if errs := ValidatePolicy(&policy); len(errs) == 0 {
 | 
			
		||||
			t.Errorf("[%d] Expected policy %#v to be invalid!", i, policy)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -11,6 +11,7 @@ go_library(
 | 
			
		||||
    name = "go_default_library",
 | 
			
		||||
    srcs = [
 | 
			
		||||
        "request.go",
 | 
			
		||||
        "scheme.go",
 | 
			
		||||
        "types.go",
 | 
			
		||||
    ],
 | 
			
		||||
    tags = ["automanaged"],
 | 
			
		||||
@@ -20,9 +21,11 @@ go_library(
 | 
			
		||||
        "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/apis/audit:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/apis/audit/v1alpha1:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/client-go/pkg/apis/authentication/v1:go_default_library",
 | 
			
		||||
    ],
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										44
									
								
								staging/src/k8s.io/apiserver/pkg/audit/policy/BUILD
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								staging/src/k8s.io/apiserver/pkg/audit/policy/BUILD
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
package(default_visibility = ["//visibility:public"])
 | 
			
		||||
 | 
			
		||||
licenses(["notice"])
 | 
			
		||||
 | 
			
		||||
load(
 | 
			
		||||
    "@io_bazel_rules_go//go:def.bzl",
 | 
			
		||||
    "go_library",
 | 
			
		||||
    "go_test",
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
go_test(
 | 
			
		||||
    name = "go_default_test",
 | 
			
		||||
    srcs = [
 | 
			
		||||
        "checker_test.go",
 | 
			
		||||
        "reader_test.go",
 | 
			
		||||
    ],
 | 
			
		||||
    library = ":go_default_library",
 | 
			
		||||
    tags = ["automanaged"],
 | 
			
		||||
    deps = [
 | 
			
		||||
        "//vendor/github.com/stretchr/testify/assert:go_default_library",
 | 
			
		||||
        "//vendor/github.com/stretchr/testify/require:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/apis/audit:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
 | 
			
		||||
    ],
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
go_library(
 | 
			
		||||
    name = "go_default_library",
 | 
			
		||||
    srcs = [
 | 
			
		||||
        "checker.go",
 | 
			
		||||
        "reader.go",
 | 
			
		||||
    ],
 | 
			
		||||
    tags = ["automanaged"],
 | 
			
		||||
    deps = [
 | 
			
		||||
        "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/apis/audit:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/apis/audit/v1alpha1:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/apis/audit/validation:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/audit:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
 | 
			
		||||
    ],
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										177
									
								
								staging/src/k8s.io/apiserver/pkg/audit/policy/checker.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								staging/src/k8s.io/apiserver/pkg/audit/policy/checker.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,177 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2017 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 policy
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apiserver/pkg/apis/audit"
 | 
			
		||||
	"k8s.io/apiserver/pkg/authorization/authorizer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// DefaultAuditLevel is the default level to audit at, if no policy rules are matched.
 | 
			
		||||
	DefaultAuditLevel = audit.LevelNone
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Checker exposes methods for checking the policy rules.
 | 
			
		||||
type Checker interface {
 | 
			
		||||
	// Check the audit level for a request with the given authorizer attributes.
 | 
			
		||||
	Level(authorizer.Attributes) audit.Level
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewChecker creates a new policy checker.
 | 
			
		||||
func NewChecker(policy *audit.Policy) Checker {
 | 
			
		||||
	return &policyChecker{*policy}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FakeChecker creates a checker that returns a constant level for all requests (for testing).
 | 
			
		||||
func FakeChecker(level audit.Level) Checker {
 | 
			
		||||
	return &fakeChecker{level}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type policyChecker struct {
 | 
			
		||||
	audit.Policy
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *policyChecker) Level(attrs authorizer.Attributes) audit.Level {
 | 
			
		||||
	for _, rule := range p.Rules {
 | 
			
		||||
		if ruleMatches(&rule, attrs) {
 | 
			
		||||
			return rule.Level
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return DefaultAuditLevel
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Check whether the rule matches the request attrs.
 | 
			
		||||
func ruleMatches(r *audit.PolicyRule, attrs authorizer.Attributes) bool {
 | 
			
		||||
	if len(r.Users) > 0 {
 | 
			
		||||
		if !hasString(r.Users, attrs.GetUser().GetName()) {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(r.UserGroups) > 0 {
 | 
			
		||||
		matched := false
 | 
			
		||||
		for _, group := range attrs.GetUser().GetGroups() {
 | 
			
		||||
			if hasString(r.UserGroups, group) {
 | 
			
		||||
				matched = true
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !matched {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(r.Verbs) > 0 {
 | 
			
		||||
		if !hasString(r.Verbs, attrs.GetVerb()) {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(r.Namespaces) > 0 || len(r.Resources) > 0 {
 | 
			
		||||
		return ruleMatchesResource(r, attrs)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(r.NonResourceURLs) > 0 {
 | 
			
		||||
		return ruleMatchesNonResource(r, attrs)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Check whether the rule's non-resource URLs match the request attrs.
 | 
			
		||||
func ruleMatchesNonResource(r *audit.PolicyRule, attrs authorizer.Attributes) bool {
 | 
			
		||||
	if attrs.IsResourceRequest() {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	path := attrs.GetPath()
 | 
			
		||||
	for _, spec := range r.NonResourceURLs {
 | 
			
		||||
		if pathMatches(path, spec) {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Check whether the path matches the path specification.
 | 
			
		||||
func pathMatches(path, spec string) bool {
 | 
			
		||||
	// Allow wildcard match
 | 
			
		||||
	if spec == "*" {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	// Allow exact match
 | 
			
		||||
	if spec == path {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	// Allow a trailing * subpath match
 | 
			
		||||
	if strings.HasSuffix(spec, "*") && strings.HasPrefix(path, strings.TrimRight(spec, "*")) {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Check whether the rule's resource fields match the request attrs.
 | 
			
		||||
func ruleMatchesResource(r *audit.PolicyRule, attrs authorizer.Attributes) bool {
 | 
			
		||||
	if !attrs.IsResourceRequest() {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(r.Namespaces) > 0 {
 | 
			
		||||
		if !hasString(r.Namespaces, attrs.GetNamespace()) { // Non-namespaced resources use the empty string.
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(r.Resources) == 0 {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	apiGroup := attrs.GetAPIGroup()
 | 
			
		||||
	resource := attrs.GetResource()
 | 
			
		||||
	for _, gr := range r.Resources {
 | 
			
		||||
		if gr.Group == apiGroup {
 | 
			
		||||
			if len(gr.Resources) == 0 {
 | 
			
		||||
				return true
 | 
			
		||||
			}
 | 
			
		||||
			for _, res := range gr.Resources {
 | 
			
		||||
				if res == resource {
 | 
			
		||||
					return true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Utility function to check whether a string slice contains a string.
 | 
			
		||||
func hasString(slice []string, value string) bool {
 | 
			
		||||
	for _, s := range slice {
 | 
			
		||||
		if s == value {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type fakeChecker struct {
 | 
			
		||||
	level audit.Level
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *fakeChecker) Level(_ authorizer.Attributes) audit.Level {
 | 
			
		||||
	return f.level
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										162
									
								
								staging/src/k8s.io/apiserver/pkg/audit/policy/checker_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								staging/src/k8s.io/apiserver/pkg/audit/policy/checker_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,162 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2017 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 policy
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apiserver/pkg/apis/audit"
 | 
			
		||||
	"k8s.io/apiserver/pkg/authentication/user"
 | 
			
		||||
	"k8s.io/apiserver/pkg/authorization/authorizer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestChecker(t *testing.T) {
 | 
			
		||||
	tim := &user.DefaultInfo{
 | 
			
		||||
		Name:   "tim@k8s.io",
 | 
			
		||||
		Groups: []string{"humans", "developers"},
 | 
			
		||||
	}
 | 
			
		||||
	attrs := map[string]authorizer.Attributes{
 | 
			
		||||
		"namespaced": &authorizer.AttributesRecord{
 | 
			
		||||
			User:            tim,
 | 
			
		||||
			Verb:            "get",
 | 
			
		||||
			Namespace:       "default",
 | 
			
		||||
			APIGroup:        "", // Core
 | 
			
		||||
			APIVersion:      "v1",
 | 
			
		||||
			Resource:        "pods",
 | 
			
		||||
			Name:            "busybox",
 | 
			
		||||
			ResourceRequest: true,
 | 
			
		||||
			Path:            "/api/v1/namespaces/default/pods/busybox",
 | 
			
		||||
		},
 | 
			
		||||
		"cluster": &authorizer.AttributesRecord{
 | 
			
		||||
			User:            tim,
 | 
			
		||||
			Verb:            "get",
 | 
			
		||||
			APIGroup:        "rbac.authorization.k8s.io", // Core
 | 
			
		||||
			APIVersion:      "v1beta1",
 | 
			
		||||
			Resource:        "clusterroles",
 | 
			
		||||
			Name:            "edit",
 | 
			
		||||
			ResourceRequest: true,
 | 
			
		||||
			Path:            "/apis/rbac.authorization.k8s.io/v1beta1/clusterroles/edit",
 | 
			
		||||
		},
 | 
			
		||||
		"nonResource": &authorizer.AttributesRecord{
 | 
			
		||||
			User:            tim,
 | 
			
		||||
			Verb:            "get",
 | 
			
		||||
			ResourceRequest: false,
 | 
			
		||||
			Path:            "/logs/kubelet.log",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rules := map[string]audit.PolicyRule{
 | 
			
		||||
		"default": {
 | 
			
		||||
			Level: audit.LevelMetadata,
 | 
			
		||||
		},
 | 
			
		||||
		"create": {
 | 
			
		||||
			Level: audit.LevelRequest,
 | 
			
		||||
			Verbs: []string{"create"},
 | 
			
		||||
		},
 | 
			
		||||
		"tims": {
 | 
			
		||||
			Level: audit.LevelMetadata,
 | 
			
		||||
			Users: []string{"tim@k8s.io"},
 | 
			
		||||
		},
 | 
			
		||||
		"humans": {
 | 
			
		||||
			Level:      audit.LevelMetadata,
 | 
			
		||||
			UserGroups: []string{"humans"},
 | 
			
		||||
		},
 | 
			
		||||
		"serviceAccounts": {
 | 
			
		||||
			Level:      audit.LevelRequest,
 | 
			
		||||
			UserGroups: []string{"system:serviceaccounts"},
 | 
			
		||||
		},
 | 
			
		||||
		"getPods": {
 | 
			
		||||
			Level:     audit.LevelRequestResponse,
 | 
			
		||||
			Verbs:     []string{"get"},
 | 
			
		||||
			Resources: []audit.GroupResources{{Resources: []string{"pods"}}},
 | 
			
		||||
		},
 | 
			
		||||
		"getClusterRoles": {
 | 
			
		||||
			Level: audit.LevelRequestResponse,
 | 
			
		||||
			Verbs: []string{"get"},
 | 
			
		||||
			Resources: []audit.GroupResources{{
 | 
			
		||||
				Group:     "rbac.authorization.k8s.io",
 | 
			
		||||
				Resources: []string{"clusterroles"},
 | 
			
		||||
			}},
 | 
			
		||||
			Namespaces: []string{""},
 | 
			
		||||
		},
 | 
			
		||||
		"getLogs": {
 | 
			
		||||
			Level: audit.LevelRequestResponse,
 | 
			
		||||
			Verbs: []string{"get"},
 | 
			
		||||
			NonResourceURLs: []string{
 | 
			
		||||
				"/logs*",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		"getMetrics": {
 | 
			
		||||
			Level: audit.LevelRequest,
 | 
			
		||||
			Verbs: []string{"get"},
 | 
			
		||||
			NonResourceURLs: []string{
 | 
			
		||||
				"/metrics",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	test := func(req string, expected audit.Level, ruleNames ...string) {
 | 
			
		||||
		policy := audit.Policy{}
 | 
			
		||||
		for _, rule := range ruleNames {
 | 
			
		||||
			require.Contains(t, rules, rule)
 | 
			
		||||
			policy.Rules = append(policy.Rules, rules[rule])
 | 
			
		||||
		}
 | 
			
		||||
		require.Contains(t, attrs, req)
 | 
			
		||||
		actual := NewChecker(&policy).Level(attrs[req])
 | 
			
		||||
		assert.Equal(t, expected, actual, "request:%s rules:%s", req, strings.Join(ruleNames, ","))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	test("namespaced", audit.LevelMetadata, "default")
 | 
			
		||||
	test("namespaced", audit.LevelNone, "create")
 | 
			
		||||
	test("namespaced", audit.LevelMetadata, "tims")
 | 
			
		||||
	test("namespaced", audit.LevelMetadata, "humans")
 | 
			
		||||
	test("namespaced", audit.LevelNone, "serviceAccounts")
 | 
			
		||||
	test("namespaced", audit.LevelRequestResponse, "getPods")
 | 
			
		||||
	test("namespaced", audit.LevelNone, "getClusterRoles")
 | 
			
		||||
	test("namespaced", audit.LevelNone, "getLogs")
 | 
			
		||||
	test("namespaced", audit.LevelNone, "getMetrics")
 | 
			
		||||
	test("namespaced", audit.LevelMetadata, "getMetrics", "serviceAccounts", "default")
 | 
			
		||||
	test("namespaced", audit.LevelRequestResponse, "getMetrics", "getPods", "default")
 | 
			
		||||
 | 
			
		||||
	test("cluster", audit.LevelMetadata, "default")
 | 
			
		||||
	test("cluster", audit.LevelNone, "create")
 | 
			
		||||
	test("cluster", audit.LevelMetadata, "tims")
 | 
			
		||||
	test("cluster", audit.LevelMetadata, "humans")
 | 
			
		||||
	test("cluster", audit.LevelNone, "serviceAccounts")
 | 
			
		||||
	test("cluster", audit.LevelNone, "getPods")
 | 
			
		||||
	test("cluster", audit.LevelRequestResponse, "getClusterRoles")
 | 
			
		||||
	test("cluster", audit.LevelNone, "getLogs")
 | 
			
		||||
	test("cluster", audit.LevelNone, "getMetrics")
 | 
			
		||||
	test("cluster", audit.LevelMetadata, "getMetrics", "serviceAccounts", "default")
 | 
			
		||||
	test("cluster", audit.LevelRequestResponse, "getMetrics", "getClusterRoles", "default")
 | 
			
		||||
 | 
			
		||||
	test("nonResource", audit.LevelMetadata, "default")
 | 
			
		||||
	test("nonResource", audit.LevelNone, "create")
 | 
			
		||||
	test("nonResource", audit.LevelMetadata, "tims")
 | 
			
		||||
	test("nonResource", audit.LevelMetadata, "humans")
 | 
			
		||||
	test("nonResource", audit.LevelNone, "serviceAccounts")
 | 
			
		||||
	test("nonResource", audit.LevelNone, "getPods")
 | 
			
		||||
	test("nonResource", audit.LevelNone, "getClusterRoles")
 | 
			
		||||
	test("nonResource", audit.LevelRequestResponse, "getLogs")
 | 
			
		||||
	test("nonResource", audit.LevelNone, "getMetrics")
 | 
			
		||||
	test("nonResource", audit.LevelMetadata, "getMetrics", "serviceAccounts", "default")
 | 
			
		||||
	test("nonResource", audit.LevelRequestResponse, "getLogs", "getClusterRoles", "default")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										57
									
								
								staging/src/k8s.io/apiserver/pkg/audit/policy/reader.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								staging/src/k8s.io/apiserver/pkg/audit/policy/reader.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2017 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 policy
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	auditinternal "k8s.io/apiserver/pkg/apis/audit"
 | 
			
		||||
	auditv1alpha1 "k8s.io/apiserver/pkg/apis/audit/v1alpha1"
 | 
			
		||||
	"k8s.io/apiserver/pkg/apis/audit/validation"
 | 
			
		||||
	"k8s.io/apiserver/pkg/audit"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func LoadPolicyFromFile(filePath string) (*auditinternal.Policy, error) {
 | 
			
		||||
	if filePath == "" {
 | 
			
		||||
		return nil, fmt.Errorf("file path not specified")
 | 
			
		||||
	}
 | 
			
		||||
	policyDef, err := ioutil.ReadFile(filePath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed to read file path %q: %+v", filePath, err)
 | 
			
		||||
	}
 | 
			
		||||
	if len(policyDef) == 0 {
 | 
			
		||||
		return nil, fmt.Errorf("file %q was empty", filePath)
 | 
			
		||||
	}
 | 
			
		||||
	policyVersioned := &auditv1alpha1.Policy{}
 | 
			
		||||
 | 
			
		||||
	decoder := audit.Codecs.UniversalDecoder(auditv1alpha1.SchemeGroupVersion)
 | 
			
		||||
	if err := runtime.DecodeInto(decoder, policyDef, policyVersioned); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed decoding file %q: %v", filePath, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	policy := &auditinternal.Policy{}
 | 
			
		||||
	if err := audit.Scheme.Convert(policyVersioned, policy, nil); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed converting policy: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := validation.ValidatePolicy(policy); err != nil {
 | 
			
		||||
		return nil, err.ToAggregate()
 | 
			
		||||
	}
 | 
			
		||||
	return policy, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										86
									
								
								staging/src/k8s.io/apiserver/pkg/audit/policy/reader_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								staging/src/k8s.io/apiserver/pkg/audit/policy/reader_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2017 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 policy
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/diff"
 | 
			
		||||
	"k8s.io/apiserver/pkg/apis/audit"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const policyDef = `
 | 
			
		||||
rules:
 | 
			
		||||
  - level: None
 | 
			
		||||
    nonResourceURLs:
 | 
			
		||||
      - /healthz*
 | 
			
		||||
      - /version
 | 
			
		||||
  - level: RequestResponse
 | 
			
		||||
    users: ["tim"]
 | 
			
		||||
    userGroups: ["testers", "developers"]
 | 
			
		||||
    verbs: ["patch", "delete", "create"]
 | 
			
		||||
    resources:
 | 
			
		||||
      - group: ""
 | 
			
		||||
      - group: "rbac.authorization.k8s.io"
 | 
			
		||||
        resources: ["clusterroles", "clusterrolebindings"]
 | 
			
		||||
    namespaces: ["default", "kube-system"]
 | 
			
		||||
  - level: Metadata
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
var expectedPolicy = &audit.Policy{
 | 
			
		||||
	Rules: []audit.PolicyRule{{
 | 
			
		||||
		Level:           audit.LevelNone,
 | 
			
		||||
		NonResourceURLs: []string{"/healthz*", "/version"},
 | 
			
		||||
	}, {
 | 
			
		||||
		Level:      audit.LevelRequestResponse,
 | 
			
		||||
		Users:      []string{"tim"},
 | 
			
		||||
		UserGroups: []string{"testers", "developers"},
 | 
			
		||||
		Verbs:      []string{"patch", "delete", "create"},
 | 
			
		||||
		Resources: []audit.GroupResources{{}, {
 | 
			
		||||
			Group:     "rbac.authorization.k8s.io",
 | 
			
		||||
			Resources: []string{"clusterroles", "clusterrolebindings"},
 | 
			
		||||
		}},
 | 
			
		||||
		Namespaces: []string{"default", "kube-system"},
 | 
			
		||||
	}, {
 | 
			
		||||
		Level: audit.LevelMetadata,
 | 
			
		||||
	}},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParser(t *testing.T) {
 | 
			
		||||
	// Create a policy file.
 | 
			
		||||
	f, err := ioutil.TempFile("", "policy.yaml")
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
	defer os.Remove(f.Name())
 | 
			
		||||
 | 
			
		||||
	_, err = f.WriteString(policyDef)
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
	require.NoError(t, f.Close())
 | 
			
		||||
 | 
			
		||||
	policy, err := LoadPolicyFromFile(f.Name())
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	assert.Len(t, policy.Rules, 3) // Sanity check.
 | 
			
		||||
	if !reflect.DeepEqual(policy, expectedPolicy) {
 | 
			
		||||
		t.Errorf("Unexpected policy! Diff:\n%s", diff.ObjectDiff(policy, expectedPolicy))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -40,21 +40,14 @@ import (
 | 
			
		||||
	authenticationv1 "k8s.io/client-go/pkg/apis/authentication/v1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NewEventFromRequest generates an audit event for the request.
 | 
			
		||||
func NewEventFromRequest(req *http.Request, policy *auditinternal.Policy, attribs authorizer.Attributes) (*auditinternal.Event, error) {
 | 
			
		||||
func NewEventFromRequest(req *http.Request, level auditinternal.Level, attribs authorizer.Attributes) (*auditinternal.Event, error) {
 | 
			
		||||
	ev := &auditinternal.Event{
 | 
			
		||||
		Timestamp:  metav1.NewTime(time.Now()),
 | 
			
		||||
		Verb:       attribs.GetVerb(),
 | 
			
		||||
		RequestURI: req.URL.RequestURI(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// set the level
 | 
			
		||||
	ev.Level = auditinternal.LevelNone
 | 
			
		||||
	if policy != nil && len(policy.Rules) > 0 {
 | 
			
		||||
		// This is just a hack to get through the test without setting a high level by default.
 | 
			
		||||
		// TODO(audit): add the policy evalutation here
 | 
			
		||||
		ev.Level = policy.Rules[0].Level
 | 
			
		||||
	}
 | 
			
		||||
	ev.Level = level
 | 
			
		||||
 | 
			
		||||
	// prefer the id from the headers. If not available, create a new one.
 | 
			
		||||
	// TODO(audit): do we want to forbid the header for non-front-proxy users?
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										34
									
								
								staging/src/k8s.io/apiserver/pkg/audit/scheme.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								staging/src/k8s.io/apiserver/pkg/audit/scheme.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2017 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.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// TODO: Delete this file if we generate a clientset.
 | 
			
		||||
package audit
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/serializer"
 | 
			
		||||
	"k8s.io/apiserver/pkg/apis/audit/v1alpha1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var Scheme = runtime.NewScheme()
 | 
			
		||||
var Codecs = serializer.NewCodecFactory(Scheme)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
 | 
			
		||||
	v1alpha1.AddToScheme(Scheme)
 | 
			
		||||
}
 | 
			
		||||
@@ -45,6 +45,7 @@ go_test(
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/apis/example/fuzzer:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/apis/example/v1:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/audit:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/audit/policy:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/endpoints/filters:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/endpoints/handlers:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:go_default_library",
 | 
			
		||||
 
 | 
			
		||||
@@ -57,6 +57,7 @@ import (
 | 
			
		||||
	examplefuzzer "k8s.io/apiserver/pkg/apis/example/fuzzer"
 | 
			
		||||
	examplev1 "k8s.io/apiserver/pkg/apis/example/v1"
 | 
			
		||||
	"k8s.io/apiserver/pkg/audit"
 | 
			
		||||
	auditpolicy "k8s.io/apiserver/pkg/audit/policy"
 | 
			
		||||
	genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
 | 
			
		||||
	"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
 | 
			
		||||
	"k8s.io/apiserver/pkg/endpoints/request"
 | 
			
		||||
@@ -334,7 +335,7 @@ func handleInternal(storage map[string]rest.Storage, admissionControl admission.
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	handler := genericapifilters.WithAudit(mux, requestContextMapper, auditSink, auditinternal.NewConstantPolicy(auditinternal.LevelRequestResponse), func(r *http.Request, requestInfo *request.RequestInfo) bool {
 | 
			
		||||
	handler := genericapifilters.WithAudit(mux, requestContextMapper, auditSink, auditpolicy.FakeChecker(auditinternal.LevelRequestResponse), func(r *http.Request, requestInfo *request.RequestInfo) bool {
 | 
			
		||||
		// simplified long-running check
 | 
			
		||||
		return requestInfo.Verb == "watch" || requestInfo.Verb == "proxy"
 | 
			
		||||
	})
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ go_test(
 | 
			
		||||
        "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/apis/audit:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/audit/policy:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
 | 
			
		||||
@@ -56,6 +57,7 @@ go_library(
 | 
			
		||||
        "//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/apis/audit:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/audit:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/audit/policy:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/authentication/serviceaccount:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
 | 
			
		||||
 
 | 
			
		||||
@@ -19,16 +19,16 @@ package filters
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 | 
			
		||||
	auditinternal "k8s.io/apiserver/pkg/apis/audit"
 | 
			
		||||
	"k8s.io/apiserver/pkg/audit"
 | 
			
		||||
	"k8s.io/apiserver/pkg/audit/policy"
 | 
			
		||||
	"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
 | 
			
		||||
	"k8s.io/apiserver/pkg/endpoints/request"
 | 
			
		||||
)
 | 
			
		||||
@@ -49,8 +49,8 @@ import (
 | 
			
		||||
// 2. the response line containing:
 | 
			
		||||
//    - the unique id from 1
 | 
			
		||||
//    - response code
 | 
			
		||||
func WithAudit(handler http.Handler, requestContextMapper request.RequestContextMapper, sink audit.Sink, policy *auditinternal.Policy, longRunningCheck request.LongRunningRequestCheck) http.Handler {
 | 
			
		||||
	if sink == nil {
 | 
			
		||||
func WithAudit(handler http.Handler, requestContextMapper request.RequestContextMapper, sink audit.Sink, policy policy.Checker, longRunningCheck request.LongRunningRequestCheck) http.Handler {
 | 
			
		||||
	if sink == nil || policy == nil {
 | 
			
		||||
		return handler
 | 
			
		||||
	}
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
@@ -67,7 +67,13 @@ func WithAudit(handler http.Handler, requestContextMapper request.RequestContext
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ev, err := audit.NewEventFromRequest(req, policy, attribs)
 | 
			
		||||
		level := policy.Level(attribs)
 | 
			
		||||
		if level == auditinternal.LevelNone {
 | 
			
		||||
			// Don't audit.
 | 
			
		||||
			handler.ServeHTTP(w, req)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ev, err := audit.NewEventFromRequest(req, level, attribs)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			utilruntime.HandleError(fmt.Errorf("failed to complete audit event from request: %v", err))
 | 
			
		||||
			responsewriters.InternalError(w, req, errors.New("failed to update context"))
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/wait"
 | 
			
		||||
	auditinternal "k8s.io/apiserver/pkg/apis/audit"
 | 
			
		||||
	"k8s.io/apiserver/pkg/audit/policy"
 | 
			
		||||
	"k8s.io/apiserver/pkg/authentication/user"
 | 
			
		||||
	"k8s.io/apiserver/pkg/endpoints/request"
 | 
			
		||||
	pluginlog "k8s.io/apiserver/plugin/pkg/audit/log"
 | 
			
		||||
@@ -321,9 +322,10 @@ func TestAudit(t *testing.T) {
 | 
			
		||||
	} {
 | 
			
		||||
		var buf bytes.Buffer
 | 
			
		||||
		backend := pluginlog.NewBackend(&buf)
 | 
			
		||||
		policyChecker := policy.FakeChecker(auditinternal.LevelRequestResponse)
 | 
			
		||||
		handler := WithAudit(http.HandlerFunc(test.handler), &fakeRequestContextMapper{
 | 
			
		||||
			user: &user.DefaultInfo{Name: "admin"},
 | 
			
		||||
		}, backend, auditinternal.NewConstantPolicy(auditinternal.LevelRequestResponse), func(r *http.Request, ri *request.RequestInfo) bool {
 | 
			
		||||
		}, backend, policyChecker, func(r *http.Request, ri *request.RequestInfo) bool {
 | 
			
		||||
			// simplified long-running check
 | 
			
		||||
			return ri.Verb == "watch"
 | 
			
		||||
		})
 | 
			
		||||
@@ -386,7 +388,8 @@ func (*fakeRequestContextMapper) Update(req *http.Request, context request.Conte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestAuditNoPanicOnNilUser(t *testing.T) {
 | 
			
		||||
	handler := WithAudit(&fakeHTTPHandler{}, &fakeRequestContextMapper{}, &fakeAuditSink{}, auditinternal.NewConstantPolicy(auditinternal.LevelRequestResponse), nil)
 | 
			
		||||
	policyChecker := policy.FakeChecker(auditinternal.LevelRequestResponse)
 | 
			
		||||
	handler := WithAudit(&fakeHTTPHandler{}, &fakeRequestContextMapper{}, &fakeAuditSink{}, policyChecker, nil)
 | 
			
		||||
	req, _ := http.NewRequest("GET", "/api/v1/namespaces/default/pods", nil)
 | 
			
		||||
	req.RemoteAddr = "127.0.0.1"
 | 
			
		||||
	handler.ServeHTTP(httptest.NewRecorder(), req)
 | 
			
		||||
 
 | 
			
		||||
@@ -33,6 +33,14 @@ const (
 | 
			
		||||
	// StreamingProxyRedirects controls whether the apiserver should intercept (and follow)
 | 
			
		||||
	// redirects from the backend (Kubelet) for streaming requests (exec/attach/port-forward).
 | 
			
		||||
	StreamingProxyRedirects utilfeature.Feature = "StreamingProxyRedirects"
 | 
			
		||||
 | 
			
		||||
	// owner: timstclair
 | 
			
		||||
	// alpha: v1.7
 | 
			
		||||
	//
 | 
			
		||||
	// AdvancedAuditing enables a much more general API auditing pipeline, which includes support for
 | 
			
		||||
	// pluggable output backends and an audit policy specifying how different requests should be
 | 
			
		||||
	// audited.
 | 
			
		||||
	AdvancedAuditing utilfeature.Feature = "AdvancedAuditing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
@@ -44,4 +52,5 @@ func init() {
 | 
			
		||||
// available throughout Kubernetes binaries.
 | 
			
		||||
var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureSpec{
 | 
			
		||||
	StreamingProxyRedirects: {Default: true, PreRelease: utilfeature.Beta},
 | 
			
		||||
	AdvancedAuditing:        {Default: false, PreRelease: utilfeature.Alpha},
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -77,8 +77,8 @@ go_library(
 | 
			
		||||
        "//vendor/k8s.io/apimachinery/pkg/version:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/apis/apiserver/install:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/apis/audit:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/audit:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/audit/policy:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/authentication/authenticatorfactory:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/authentication/request/union:go_default_library",
 | 
			
		||||
@@ -92,12 +92,14 @@ go_library(
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/endpoints/openapi:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/features:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/server/filters:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/server/healthz:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/server/mux:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/server/routes:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/client-go/informers:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/client-go/rest:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/client-go/util/cert:go_default_library",
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"crypto/x509"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	goruntime "runtime"
 | 
			
		||||
@@ -38,8 +39,8 @@ import (
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/sets"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/version"
 | 
			
		||||
	"k8s.io/apiserver/pkg/admission"
 | 
			
		||||
	auditinternal "k8s.io/apiserver/pkg/apis/audit"
 | 
			
		||||
	"k8s.io/apiserver/pkg/audit"
 | 
			
		||||
	auditpolicy "k8s.io/apiserver/pkg/audit/policy"
 | 
			
		||||
	"k8s.io/apiserver/pkg/authentication/authenticator"
 | 
			
		||||
	"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
 | 
			
		||||
	authenticatorunion "k8s.io/apiserver/pkg/authentication/request/union"
 | 
			
		||||
@@ -51,10 +52,12 @@ import (
 | 
			
		||||
	genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
 | 
			
		||||
	apiopenapi "k8s.io/apiserver/pkg/endpoints/openapi"
 | 
			
		||||
	apirequest "k8s.io/apiserver/pkg/endpoints/request"
 | 
			
		||||
	"k8s.io/apiserver/pkg/features"
 | 
			
		||||
	genericregistry "k8s.io/apiserver/pkg/registry/generic"
 | 
			
		||||
	genericfilters "k8s.io/apiserver/pkg/server/filters"
 | 
			
		||||
	"k8s.io/apiserver/pkg/server/healthz"
 | 
			
		||||
	"k8s.io/apiserver/pkg/server/routes"
 | 
			
		||||
	utilfeature "k8s.io/apiserver/pkg/util/feature"
 | 
			
		||||
	"k8s.io/client-go/informers"
 | 
			
		||||
	restclient "k8s.io/client-go/rest"
 | 
			
		||||
	certutil "k8s.io/client-go/util/cert"
 | 
			
		||||
@@ -101,11 +104,12 @@ type Config struct {
 | 
			
		||||
 | 
			
		||||
	// Version will enable the /version endpoint if non-nil
 | 
			
		||||
	Version *version.Info
 | 
			
		||||
	// LegacyAuditWriter is the destination for audit logs.  If nil, they will not be written.
 | 
			
		||||
	LegacyAuditWriter io.Writer
 | 
			
		||||
	// AuditBackend is where audit events are sent to.
 | 
			
		||||
	AuditBackend audit.Backend
 | 
			
		||||
	// AuditPolicy defines rules which determine the audit level for different requests.
 | 
			
		||||
	AuditPolicy *auditinternal.Policy
 | 
			
		||||
 | 
			
		||||
	// AuditPolicyChecker makes the decision of whether and how to audit log a request.
 | 
			
		||||
	AuditPolicyChecker auditpolicy.Checker
 | 
			
		||||
	// SupportsBasicAuth indicates that's at least one Authenticator supports basic auth
 | 
			
		||||
	// If this is true, a basic auth challenge is returned on authentication failure
 | 
			
		||||
	// TODO(roberthbailey): Remove once the server no longer supports http basic auth.
 | 
			
		||||
@@ -457,8 +461,11 @@ func (c completedConfig) New(delegationTarget DelegationTarget) (*GenericAPIServ
 | 
			
		||||
func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
 | 
			
		||||
	handler := genericapifilters.WithAuthorization(apiHandler, c.RequestContextMapper, c.Authorizer)
 | 
			
		||||
	handler = genericapifilters.WithImpersonation(handler, c.RequestContextMapper, c.Authorizer)
 | 
			
		||||
	// TODO(audit): use WithLegacyAudit if feature flag is false
 | 
			
		||||
	handler = genericapifilters.WithAudit(handler, c.RequestContextMapper, c.AuditBackend, c.AuditPolicy, c.LongRunningFunc)
 | 
			
		||||
	if utilfeature.DefaultFeatureGate.Enabled(features.AdvancedAuditing) {
 | 
			
		||||
		handler = genericapifilters.WithAudit(handler, c.RequestContextMapper, c.AuditBackend, c.AuditPolicyChecker, c.LongRunningFunc)
 | 
			
		||||
	} else {
 | 
			
		||||
		handler = genericapifilters.WithLegacyAudit(handler, c.RequestContextMapper, c.LegacyAuditWriter)
 | 
			
		||||
	}
 | 
			
		||||
	handler = genericapifilters.WithAuthentication(handler, c.RequestContextMapper, c.Authenticator, genericapifilters.Unauthorized(c.SupportsBasicAuth))
 | 
			
		||||
	handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
 | 
			
		||||
	handler = genericfilters.WithPanicRecovery(handler)
 | 
			
		||||
 
 | 
			
		||||
@@ -54,6 +54,7 @@ go_library(
 | 
			
		||||
        "//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/admission/initializer:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/audit/policy:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/authentication/authenticatorfactory:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/authorization/authorizerfactory:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apiserver/pkg/features:go_default_library",
 | 
			
		||||
 
 | 
			
		||||
@@ -17,13 +17,17 @@ limitations under the License.
 | 
			
		||||
package options
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/spf13/pflag"
 | 
			
		||||
	"gopkg.in/natefinch/lumberjack.v2"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apiserver/pkg/audit/policy"
 | 
			
		||||
	"k8s.io/apiserver/pkg/features"
 | 
			
		||||
	"k8s.io/apiserver/pkg/server"
 | 
			
		||||
	utilfeature "k8s.io/apiserver/pkg/util/feature"
 | 
			
		||||
	pluginlog "k8s.io/apiserver/plugin/pkg/audit/log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -32,6 +36,8 @@ type AuditLogOptions struct {
 | 
			
		||||
	MaxAge     int
 | 
			
		||||
	MaxBackups int
 | 
			
		||||
	MaxSize    int
 | 
			
		||||
 | 
			
		||||
	PolicyFile string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewAuditLogOptions() *AuditLogOptions {
 | 
			
		||||
@@ -47,9 +53,28 @@ func (o *AuditLogOptions) AddFlags(fs *pflag.FlagSet) {
 | 
			
		||||
		"The maximum number of old audit log files to retain.")
 | 
			
		||||
	fs.IntVar(&o.MaxSize, "audit-log-maxsize", o.MaxSize,
 | 
			
		||||
		"The maximum size in megabytes of the audit log file before it gets rotated.")
 | 
			
		||||
 | 
			
		||||
	fs.StringVar(&o.PolicyFile, "audit-policy-file", o.PolicyFile,
 | 
			
		||||
		"Path to the file that defines the audit policy configuration. Requires the 'AdvancedAuditing' feature gate."+
 | 
			
		||||
			" With AdvancedAuditing, a profile is required to enable auditing.")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *AuditLogOptions) ApplyTo(c *server.Config) error {
 | 
			
		||||
	if utilfeature.DefaultFeatureGate.Enabled(features.AdvancedAuditing) {
 | 
			
		||||
		if o.PolicyFile != "" {
 | 
			
		||||
			p, err := policy.LoadPolicyFromFile(o.PolicyFile)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			c.AuditPolicyChecker = policy.NewChecker(p)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if o.PolicyFile != "" {
 | 
			
		||||
			return fmt.Errorf("feature '%s' must be enabled to set an audit policy", features.AdvancedAuditing)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO: Generalize for alternative audit backends.
 | 
			
		||||
	if len(o.Path) == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -63,6 +88,7 @@ func (o *AuditLogOptions) ApplyTo(c *server.Config) error {
 | 
			
		||||
			MaxSize:    o.MaxSize,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	c.LegacyAuditWriter = w
 | 
			
		||||
	c.AuditBackend = pluginlog.NewBackend(w)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user