mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-03 17:30:00 +00:00
Implement audit policy logic
This commit is contained in:
parent
ec5fd62234
commit
a5de309ee2
@ -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
|
// inherited features from generic apiserver, relisted here to get a conflict if it is changed
|
||||||
// unintentionally on either side:
|
// unintentionally on either side:
|
||||||
StreamingProxyRedirects: {Default: true, PreRelease: utilfeature.Beta},
|
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/authentication/user:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/endpoints/filters: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/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:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/server/filters: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",
|
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -25,8 +25,10 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
|
genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
|
||||||
apirequest "k8s.io/apiserver/pkg/endpoints/request"
|
apirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
|
"k8s.io/apiserver/pkg/features"
|
||||||
"k8s.io/apiserver/pkg/server"
|
"k8s.io/apiserver/pkg/server"
|
||||||
genericfilters "k8s.io/apiserver/pkg/server/filters"
|
genericfilters "k8s.io/apiserver/pkg/server/filters"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -35,7 +37,12 @@ import (
|
|||||||
// InsecureServingInfo *ServingInfo
|
// InsecureServingInfo *ServingInfo
|
||||||
|
|
||||||
func BuildInsecureHandlerChain(apiHandler http.Handler, c *server.Config) http.Handler {
|
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 = genericapifilters.WithAuthentication(handler, c.RequestContextMapper, insecureSuperuser{}, nil)
|
||||||
handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
|
handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
|
||||||
handler = genericfilters.WithPanicRecovery(handler)
|
handler = genericfilters.WithPanicRecovery(handler)
|
||||||
|
@ -36,11 +36,3 @@ func (a Level) Less(b Level) bool {
|
|||||||
func (a Level) GreaterOrEqual(b Level) bool {
|
func (a Level) GreaterOrEqual(b Level) bool {
|
||||||
return ordLevel(a) >= ordLevel(b)
|
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",
|
name = "go_default_library",
|
||||||
srcs = [
|
srcs = [
|
||||||
"request.go",
|
"request.go",
|
||||||
|
"scheme.go",
|
||||||
"types.go",
|
"types.go",
|
||||||
],
|
],
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
@ -20,9 +21,11 @@ go_library(
|
|||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_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:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema: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/types:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/net: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: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/apiserver/pkg/authorization/authorizer:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/pkg/apis/authentication/v1: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"
|
authenticationv1 "k8s.io/client-go/pkg/apis/authentication/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewEventFromRequest generates an audit event for the request.
|
func NewEventFromRequest(req *http.Request, level auditinternal.Level, attribs authorizer.Attributes) (*auditinternal.Event, error) {
|
||||||
func NewEventFromRequest(req *http.Request, policy *auditinternal.Policy, attribs authorizer.Attributes) (*auditinternal.Event, error) {
|
|
||||||
ev := &auditinternal.Event{
|
ev := &auditinternal.Event{
|
||||||
Timestamp: metav1.NewTime(time.Now()),
|
Timestamp: metav1.NewTime(time.Now()),
|
||||||
Verb: attribs.GetVerb(),
|
Verb: attribs.GetVerb(),
|
||||||
RequestURI: req.URL.RequestURI(),
|
RequestURI: req.URL.RequestURI(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the level
|
ev.Level = 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// prefer the id from the headers. If not available, create a new one.
|
// 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?
|
// 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/fuzzer:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/apis/example/v1: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: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/filters:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/endpoints/handlers:go_default_library",
|
"//vendor/k8s.io/apiserver/pkg/endpoints/handlers:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters: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"
|
examplefuzzer "k8s.io/apiserver/pkg/apis/example/fuzzer"
|
||||||
examplev1 "k8s.io/apiserver/pkg/apis/example/v1"
|
examplev1 "k8s.io/apiserver/pkg/apis/example/v1"
|
||||||
"k8s.io/apiserver/pkg/audit"
|
"k8s.io/apiserver/pkg/audit"
|
||||||
|
auditpolicy "k8s.io/apiserver/pkg/audit/policy"
|
||||||
genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
|
genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
|
||||||
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
|
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
|
||||||
"k8s.io/apiserver/pkg/endpoints/request"
|
"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
|
// simplified long-running check
|
||||||
return requestInfo.Verb == "watch" || requestInfo.Verb == "proxy"
|
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/sets:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/wait: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/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/authenticator:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer: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/apimachinery/pkg/util/runtime:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/apis/audit: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: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/authenticator:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/authentication/serviceaccount:go_default_library",
|
"//vendor/k8s.io/apiserver/pkg/authentication/serviceaccount:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||||
|
@ -19,16 +19,16 @@ package filters
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
auditinternal "k8s.io/apiserver/pkg/apis/audit"
|
auditinternal "k8s.io/apiserver/pkg/apis/audit"
|
||||||
"k8s.io/apiserver/pkg/audit"
|
"k8s.io/apiserver/pkg/audit"
|
||||||
|
"k8s.io/apiserver/pkg/audit/policy"
|
||||||
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
|
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
|
||||||
"k8s.io/apiserver/pkg/endpoints/request"
|
"k8s.io/apiserver/pkg/endpoints/request"
|
||||||
)
|
)
|
||||||
@ -49,8 +49,8 @@ import (
|
|||||||
// 2. the response line containing:
|
// 2. the response line containing:
|
||||||
// - the unique id from 1
|
// - the unique id from 1
|
||||||
// - response code
|
// - response code
|
||||||
func WithAudit(handler http.Handler, requestContextMapper request.RequestContextMapper, sink audit.Sink, policy *auditinternal.Policy, longRunningCheck request.LongRunningRequestCheck) http.Handler {
|
func WithAudit(handler http.Handler, requestContextMapper request.RequestContextMapper, sink audit.Sink, policy policy.Checker, longRunningCheck request.LongRunningRequestCheck) http.Handler {
|
||||||
if sink == nil {
|
if sink == nil || policy == nil {
|
||||||
return handler
|
return handler
|
||||||
}
|
}
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
@ -67,7 +67,13 @@ func WithAudit(handler http.Handler, requestContextMapper request.RequestContext
|
|||||||
return
|
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 {
|
if err != nil {
|
||||||
utilruntime.HandleError(fmt.Errorf("failed to complete audit event from request: %v", err))
|
utilruntime.HandleError(fmt.Errorf("failed to complete audit event from request: %v", err))
|
||||||
responsewriters.InternalError(w, req, errors.New("failed to update context"))
|
responsewriters.InternalError(w, req, errors.New("failed to update context"))
|
||||||
|
@ -31,6 +31,7 @@ import (
|
|||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
auditinternal "k8s.io/apiserver/pkg/apis/audit"
|
auditinternal "k8s.io/apiserver/pkg/apis/audit"
|
||||||
|
"k8s.io/apiserver/pkg/audit/policy"
|
||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
"k8s.io/apiserver/pkg/endpoints/request"
|
"k8s.io/apiserver/pkg/endpoints/request"
|
||||||
pluginlog "k8s.io/apiserver/plugin/pkg/audit/log"
|
pluginlog "k8s.io/apiserver/plugin/pkg/audit/log"
|
||||||
@ -321,9 +322,10 @@ func TestAudit(t *testing.T) {
|
|||||||
} {
|
} {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
backend := pluginlog.NewBackend(&buf)
|
backend := pluginlog.NewBackend(&buf)
|
||||||
|
policyChecker := policy.FakeChecker(auditinternal.LevelRequestResponse)
|
||||||
handler := WithAudit(http.HandlerFunc(test.handler), &fakeRequestContextMapper{
|
handler := WithAudit(http.HandlerFunc(test.handler), &fakeRequestContextMapper{
|
||||||
user: &user.DefaultInfo{Name: "admin"},
|
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
|
// simplified long-running check
|
||||||
return ri.Verb == "watch"
|
return ri.Verb == "watch"
|
||||||
})
|
})
|
||||||
@ -386,7 +388,8 @@ func (*fakeRequestContextMapper) Update(req *http.Request, context request.Conte
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAuditNoPanicOnNilUser(t *testing.T) {
|
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, _ := http.NewRequest("GET", "/api/v1/namespaces/default/pods", nil)
|
||||||
req.RemoteAddr = "127.0.0.1"
|
req.RemoteAddr = "127.0.0.1"
|
||||||
handler.ServeHTTP(httptest.NewRecorder(), req)
|
handler.ServeHTTP(httptest.NewRecorder(), req)
|
||||||
|
@ -33,6 +33,14 @@ const (
|
|||||||
// StreamingProxyRedirects controls whether the apiserver should intercept (and follow)
|
// StreamingProxyRedirects controls whether the apiserver should intercept (and follow)
|
||||||
// redirects from the backend (Kubelet) for streaming requests (exec/attach/port-forward).
|
// redirects from the backend (Kubelet) for streaming requests (exec/attach/port-forward).
|
||||||
StreamingProxyRedirects utilfeature.Feature = "StreamingProxyRedirects"
|
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() {
|
func init() {
|
||||||
@ -44,4 +52,5 @@ func init() {
|
|||||||
// available throughout Kubernetes binaries.
|
// available throughout Kubernetes binaries.
|
||||||
var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureSpec{
|
var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureSpec{
|
||||||
StreamingProxyRedirects: {Default: true, PreRelease: utilfeature.Beta},
|
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/apimachinery/pkg/version:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/admission: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/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: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/authenticator:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/authentication/authenticatorfactory:go_default_library",
|
"//vendor/k8s.io/apiserver/pkg/authentication/authenticatorfactory:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/authentication/request/union: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/handlers/responsewriters:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/endpoints/openapi: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/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/generic:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/registry/rest: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/filters:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/server/healthz: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/mux:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/server/routes: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/informers:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/util/cert:go_default_library",
|
"//vendor/k8s.io/client-go/util/cert:go_default_library",
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
goruntime "runtime"
|
goruntime "runtime"
|
||||||
@ -38,8 +39,8 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/apimachinery/pkg/version"
|
"k8s.io/apimachinery/pkg/version"
|
||||||
"k8s.io/apiserver/pkg/admission"
|
"k8s.io/apiserver/pkg/admission"
|
||||||
auditinternal "k8s.io/apiserver/pkg/apis/audit"
|
|
||||||
"k8s.io/apiserver/pkg/audit"
|
"k8s.io/apiserver/pkg/audit"
|
||||||
|
auditpolicy "k8s.io/apiserver/pkg/audit/policy"
|
||||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||||
"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
|
"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
|
||||||
authenticatorunion "k8s.io/apiserver/pkg/authentication/request/union"
|
authenticatorunion "k8s.io/apiserver/pkg/authentication/request/union"
|
||||||
@ -51,10 +52,12 @@ import (
|
|||||||
genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
|
genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
|
||||||
apiopenapi "k8s.io/apiserver/pkg/endpoints/openapi"
|
apiopenapi "k8s.io/apiserver/pkg/endpoints/openapi"
|
||||||
apirequest "k8s.io/apiserver/pkg/endpoints/request"
|
apirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
|
"k8s.io/apiserver/pkg/features"
|
||||||
genericregistry "k8s.io/apiserver/pkg/registry/generic"
|
genericregistry "k8s.io/apiserver/pkg/registry/generic"
|
||||||
genericfilters "k8s.io/apiserver/pkg/server/filters"
|
genericfilters "k8s.io/apiserver/pkg/server/filters"
|
||||||
"k8s.io/apiserver/pkg/server/healthz"
|
"k8s.io/apiserver/pkg/server/healthz"
|
||||||
"k8s.io/apiserver/pkg/server/routes"
|
"k8s.io/apiserver/pkg/server/routes"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
certutil "k8s.io/client-go/util/cert"
|
certutil "k8s.io/client-go/util/cert"
|
||||||
@ -101,11 +104,12 @@ type Config struct {
|
|||||||
|
|
||||||
// Version will enable the /version endpoint if non-nil
|
// Version will enable the /version endpoint if non-nil
|
||||||
Version *version.Info
|
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 is where audit events are sent to.
|
||||||
AuditBackend audit.Backend
|
AuditBackend audit.Backend
|
||||||
// AuditPolicy defines rules which determine the audit level for different requests.
|
// AuditPolicyChecker makes the decision of whether and how to audit log a request.
|
||||||
AuditPolicy *auditinternal.Policy
|
AuditPolicyChecker auditpolicy.Checker
|
||||||
|
|
||||||
// SupportsBasicAuth indicates that's at least one Authenticator supports basic auth
|
// SupportsBasicAuth indicates that's at least one Authenticator supports basic auth
|
||||||
// If this is true, a basic auth challenge is returned on authentication failure
|
// 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.
|
// 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 {
|
func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
|
||||||
handler := genericapifilters.WithAuthorization(apiHandler, c.RequestContextMapper, c.Authorizer)
|
handler := genericapifilters.WithAuthorization(apiHandler, c.RequestContextMapper, c.Authorizer)
|
||||||
handler = genericapifilters.WithImpersonation(handler, c.RequestContextMapper, c.Authorizer)
|
handler = genericapifilters.WithImpersonation(handler, c.RequestContextMapper, c.Authorizer)
|
||||||
// TODO(audit): use WithLegacyAudit if feature flag is false
|
if utilfeature.DefaultFeatureGate.Enabled(features.AdvancedAuditing) {
|
||||||
handler = genericapifilters.WithAudit(handler, c.RequestContextMapper, c.AuditBackend, c.AuditPolicy, c.LongRunningFunc)
|
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 = genericapifilters.WithAuthentication(handler, c.RequestContextMapper, c.Authenticator, genericapifilters.Unauthorized(c.SupportsBasicAuth))
|
||||||
handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
|
handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
|
||||||
handler = genericfilters.WithPanicRecovery(handler)
|
handler = genericfilters.WithPanicRecovery(handler)
|
||||||
|
@ -54,6 +54,7 @@ go_library(
|
|||||||
"//vendor/k8s.io/apimachinery/pkg/util/net:go_default_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:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/admission/initializer: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/authentication/authenticatorfactory:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizerfactory:go_default_library",
|
"//vendor/k8s.io/apiserver/pkg/authorization/authorizerfactory:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/features:go_default_library",
|
"//vendor/k8s.io/apiserver/pkg/features:go_default_library",
|
||||||
|
@ -17,13 +17,17 @@ limitations under the License.
|
|||||||
package options
|
package options
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
"gopkg.in/natefinch/lumberjack.v2"
|
"gopkg.in/natefinch/lumberjack.v2"
|
||||||
|
|
||||||
|
"k8s.io/apiserver/pkg/audit/policy"
|
||||||
|
"k8s.io/apiserver/pkg/features"
|
||||||
"k8s.io/apiserver/pkg/server"
|
"k8s.io/apiserver/pkg/server"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
pluginlog "k8s.io/apiserver/plugin/pkg/audit/log"
|
pluginlog "k8s.io/apiserver/plugin/pkg/audit/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -32,6 +36,8 @@ type AuditLogOptions struct {
|
|||||||
MaxAge int
|
MaxAge int
|
||||||
MaxBackups int
|
MaxBackups int
|
||||||
MaxSize int
|
MaxSize int
|
||||||
|
|
||||||
|
PolicyFile string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuditLogOptions() *AuditLogOptions {
|
func NewAuditLogOptions() *AuditLogOptions {
|
||||||
@ -47,9 +53,28 @@ func (o *AuditLogOptions) AddFlags(fs *pflag.FlagSet) {
|
|||||||
"The maximum number of old audit log files to retain.")
|
"The maximum number of old audit log files to retain.")
|
||||||
fs.IntVar(&o.MaxSize, "audit-log-maxsize", o.MaxSize,
|
fs.IntVar(&o.MaxSize, "audit-log-maxsize", o.MaxSize,
|
||||||
"The maximum size in megabytes of the audit log file before it gets rotated.")
|
"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 {
|
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 {
|
if len(o.Path) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -63,6 +88,7 @@ func (o *AuditLogOptions) ApplyTo(c *server.Config) error {
|
|||||||
MaxSize: o.MaxSize,
|
MaxSize: o.MaxSize,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
c.LegacyAuditWriter = w
|
||||||
c.AuditBackend = pluginlog.NewBackend(w)
|
c.AuditBackend = pluginlog.NewBackend(w)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user