219
vendor/k8s.io/apiserver/pkg/audit/policy/checker.go
generated
vendored
Normal file
219
vendor/k8s.io/apiserver/pkg/audit/policy/checker.go
generated
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
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.
|
||||
LevelAndStages(authorizer.Attributes) (audit.Level, []audit.Stage)
|
||||
}
|
||||
|
||||
// NewChecker creates a new policy checker.
|
||||
func NewChecker(policy *audit.Policy) Checker {
|
||||
for i, rule := range policy.Rules {
|
||||
policy.Rules[i].OmitStages = unionStages(policy.OmitStages, rule.OmitStages)
|
||||
}
|
||||
return &policyChecker{*policy}
|
||||
}
|
||||
|
||||
func unionStages(stageLists ...[]audit.Stage) []audit.Stage {
|
||||
m := make(map[audit.Stage]bool)
|
||||
for _, sl := range stageLists {
|
||||
for _, s := range sl {
|
||||
m[s] = true
|
||||
}
|
||||
}
|
||||
result := make([]audit.Stage, 0, len(m))
|
||||
for key := range m {
|
||||
result = append(result, key)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// FakeChecker creates a checker that returns a constant level for all requests (for testing).
|
||||
func FakeChecker(level audit.Level, stage []audit.Stage) Checker {
|
||||
return &fakeChecker{level, stage}
|
||||
}
|
||||
|
||||
type policyChecker struct {
|
||||
audit.Policy
|
||||
}
|
||||
|
||||
func (p *policyChecker) LevelAndStages(attrs authorizer.Attributes) (audit.Level, []audit.Stage) {
|
||||
for _, rule := range p.Rules {
|
||||
if ruleMatches(&rule, attrs) {
|
||||
return rule.Level, rule.OmitStages
|
||||
}
|
||||
}
|
||||
return DefaultAuditLevel, p.OmitStages
|
||||
}
|
||||
|
||||
// Check whether the rule matches the request attrs.
|
||||
func ruleMatches(r *audit.PolicyRule, attrs authorizer.Attributes) bool {
|
||||
user := attrs.GetUser()
|
||||
if len(r.Users) > 0 {
|
||||
if user == nil || !hasString(r.Users, user.GetName()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(r.UserGroups) > 0 {
|
||||
if user == nil {
|
||||
return false
|
||||
}
|
||||
matched := false
|
||||
for _, group := range user.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()
|
||||
subresource := attrs.GetSubresource()
|
||||
combinedResource := resource
|
||||
// If subresource, the resource in the policy must match "(resource)/(subresource)"
|
||||
if subresource != "" {
|
||||
combinedResource = resource + "/" + subresource
|
||||
}
|
||||
|
||||
name := attrs.GetName()
|
||||
|
||||
for _, gr := range r.Resources {
|
||||
if gr.Group == apiGroup {
|
||||
if len(gr.Resources) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, res := range gr.Resources {
|
||||
if len(gr.ResourceNames) == 0 || hasString(gr.ResourceNames, name) {
|
||||
// match "*"
|
||||
if res == combinedResource || res == "*" {
|
||||
return true
|
||||
}
|
||||
// match "*/subresource"
|
||||
if len(subresource) > 0 && strings.HasPrefix(res, "*/") && subresource == strings.TrimLeft(res, "*/") {
|
||||
return true
|
||||
}
|
||||
// match "resource/*"
|
||||
if strings.HasSuffix(res, "/*") && resource == strings.TrimRight(res, "/*") {
|
||||
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
|
||||
stage []audit.Stage
|
||||
}
|
||||
|
||||
func (f *fakeChecker) LevelAndStages(_ authorizer.Attributes) (audit.Level, []audit.Stage) {
|
||||
return f.level, f.stage
|
||||
}
|
||||
54
vendor/k8s.io/apiserver/pkg/audit/policy/dynamic.go
generated
vendored
Normal file
54
vendor/k8s.io/apiserver/pkg/audit/policy/dynamic.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
Copyright 2018 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 (
|
||||
"k8s.io/api/auditregistration/v1alpha1"
|
||||
"k8s.io/apiserver/pkg/apis/audit"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
)
|
||||
|
||||
// ConvertDynamicPolicyToInternal constructs an internal policy type from a
|
||||
// v1alpha1 dynamic type
|
||||
func ConvertDynamicPolicyToInternal(p *v1alpha1.Policy) *audit.Policy {
|
||||
stages := make([]audit.Stage, len(p.Stages))
|
||||
for i, stage := range p.Stages {
|
||||
stages[i] = audit.Stage(stage)
|
||||
}
|
||||
return &audit.Policy{
|
||||
Rules: []audit.PolicyRule{
|
||||
{
|
||||
Level: audit.Level(p.Level),
|
||||
},
|
||||
},
|
||||
OmitStages: InvertStages(stages),
|
||||
}
|
||||
}
|
||||
|
||||
// NewDynamicChecker returns a new dynamic policy checker
|
||||
func NewDynamicChecker() Checker {
|
||||
return &dynamicPolicyChecker{}
|
||||
}
|
||||
|
||||
type dynamicPolicyChecker struct{}
|
||||
|
||||
// LevelAndStages returns returns a fixed level of the full event, this is so that the downstream policy
|
||||
// can be applied per sink.
|
||||
// TODO: this needs benchmarking before the API moves to beta to determine the effect this has on the apiserver
|
||||
func (d *dynamicPolicyChecker) LevelAndStages(authorizer.Attributes) (audit.Level, []audit.Stage) {
|
||||
return audit.LevelRequestResponse, []audit.Stage{}
|
||||
}
|
||||
53
vendor/k8s.io/apiserver/pkg/audit/policy/enforce.go
generated
vendored
Normal file
53
vendor/k8s.io/apiserver/pkg/audit/policy/enforce.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
Copyright 2018 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"
|
||||
|
||||
"k8s.io/apiserver/pkg/apis/audit"
|
||||
)
|
||||
|
||||
// EnforcePolicy drops any part of the event that doesn't conform to a policy level
|
||||
// or omitStages and sets the event level accordingly
|
||||
func EnforcePolicy(event *audit.Event, level audit.Level, omitStages []audit.Stage) (*audit.Event, error) {
|
||||
for _, stage := range omitStages {
|
||||
if event.Stage == stage {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
return enforceLevel(event, level)
|
||||
}
|
||||
|
||||
func enforceLevel(event *audit.Event, level audit.Level) (*audit.Event, error) {
|
||||
switch level {
|
||||
case audit.LevelMetadata:
|
||||
event.Level = audit.LevelMetadata
|
||||
event.ResponseObject = nil
|
||||
event.RequestObject = nil
|
||||
case audit.LevelRequest:
|
||||
event.Level = audit.LevelRequest
|
||||
event.ResponseObject = nil
|
||||
case audit.LevelRequestResponse:
|
||||
event.Level = audit.LevelRequestResponse
|
||||
case audit.LevelNone:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("level unknown: %s", level)
|
||||
}
|
||||
return event, nil
|
||||
}
|
||||
90
vendor/k8s.io/apiserver/pkg/audit/policy/reader.go
generated
vendored
Normal file
90
vendor/k8s.io/apiserver/pkg/audit/policy/reader.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
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/schema"
|
||||
auditinternal "k8s.io/apiserver/pkg/apis/audit"
|
||||
auditv1 "k8s.io/apiserver/pkg/apis/audit/v1"
|
||||
auditv1alpha1 "k8s.io/apiserver/pkg/apis/audit/v1alpha1"
|
||||
auditv1beta1 "k8s.io/apiserver/pkg/apis/audit/v1beta1"
|
||||
"k8s.io/apiserver/pkg/apis/audit/validation"
|
||||
"k8s.io/apiserver/pkg/audit"
|
||||
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
var (
|
||||
apiGroupVersions = []schema.GroupVersion{
|
||||
auditv1beta1.SchemeGroupVersion,
|
||||
auditv1alpha1.SchemeGroupVersion,
|
||||
auditv1.SchemeGroupVersion,
|
||||
}
|
||||
apiGroupVersionSet = map[schema.GroupVersion]bool{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
for _, gv := range apiGroupVersions {
|
||||
apiGroupVersionSet[gv] = true
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
ret, err := LoadPolicyFromBytes(policyDef)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%v: from file %v", err.Error(), filePath)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func LoadPolicyFromBytes(policyDef []byte) (*auditinternal.Policy, error) {
|
||||
policy := &auditinternal.Policy{}
|
||||
decoder := audit.Codecs.UniversalDecoder(apiGroupVersions...)
|
||||
|
||||
_, gvk, err := decoder.Decode(policyDef, nil, policy)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed decoding: %v", err)
|
||||
}
|
||||
|
||||
// Ensure the policy file contained an apiVersion and kind.
|
||||
if !apiGroupVersionSet[schema.GroupVersion{Group: gvk.Group, Version: gvk.Version}] {
|
||||
return nil, fmt.Errorf("unknown group version field %v in policy", gvk)
|
||||
}
|
||||
|
||||
if err := validation.ValidatePolicy(policy); err != nil {
|
||||
return nil, err.ToAggregate()
|
||||
}
|
||||
|
||||
policyCnt := len(policy.Rules)
|
||||
if policyCnt == 0 {
|
||||
return nil, fmt.Errorf("loaded illegal policy with 0 rules")
|
||||
}
|
||||
klog.V(4).Infof("Loaded %d audit policy rules", policyCnt)
|
||||
return policy, nil
|
||||
}
|
||||
68
vendor/k8s.io/apiserver/pkg/audit/policy/util.go
generated
vendored
Normal file
68
vendor/k8s.io/apiserver/pkg/audit/policy/util.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
Copyright 2018 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 (
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apiserver/pkg/apis/audit"
|
||||
)
|
||||
|
||||
// AllStages returns all possible stages
|
||||
func AllStages() sets.String {
|
||||
return sets.NewString(
|
||||
audit.StageRequestReceived,
|
||||
audit.StageResponseStarted,
|
||||
audit.StageResponseComplete,
|
||||
audit.StagePanic,
|
||||
)
|
||||
}
|
||||
|
||||
// AllLevels returns all possible levels
|
||||
func AllLevels() sets.String {
|
||||
return sets.NewString(
|
||||
string(audit.LevelNone),
|
||||
string(audit.LevelMetadata),
|
||||
string(audit.LevelRequest),
|
||||
string(audit.LevelRequestResponse),
|
||||
)
|
||||
}
|
||||
|
||||
// InvertStages subtracts the given array of stages from all stages
|
||||
func InvertStages(stages []audit.Stage) []audit.Stage {
|
||||
s := ConvertStagesToStrings(stages)
|
||||
a := AllStages()
|
||||
a.Delete(s...)
|
||||
return ConvertStringSetToStages(a)
|
||||
}
|
||||
|
||||
// ConvertStagesToStrings converts an array of stages to a string array
|
||||
func ConvertStagesToStrings(stages []audit.Stage) []string {
|
||||
s := make([]string, len(stages))
|
||||
for i, stage := range stages {
|
||||
s[i] = string(stage)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// ConvertStringSetToStages converts a string set to an array of stages
|
||||
func ConvertStringSetToStages(set sets.String) []audit.Stage {
|
||||
stages := make([]audit.Stage, len(set))
|
||||
for i, stage := range set.List() {
|
||||
stages[i] = audit.Stage(stage)
|
||||
}
|
||||
return stages
|
||||
}
|
||||
Reference in New Issue
Block a user