feat: add crd gen

Signed-off-by: runzexia <runzexia@yunify.com>
This commit is contained in:
runzexia
2019-07-30 13:36:54 +08:00
parent 9ce52aeb00
commit 1c10391faf
817 changed files with 203429 additions and 27953 deletions

7
vendor/k8s.io/apiserver/pkg/audit/OWNERS generated vendored Normal file
View File

@@ -0,0 +1,7 @@
approvers:
- sig-auth-audit-approvers
reviewers:
- sig-auth-audit-reviewers
labels:
- sig/auth

147
vendor/k8s.io/apiserver/pkg/audit/event/attributes.go generated vendored Normal file
View File

@@ -0,0 +1,147 @@
/*
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 event
import (
"fmt"
"net/url"
"k8s.io/apiserver/pkg/apis/audit"
authuser "k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
)
var _ authorizer.Attributes = &attributes{}
// attributes implements the authorizer attributes interface
// with event data. This is used for enforced audit backends
type attributes struct {
event *audit.Event
path string
}
// NewAttributes returns a new attributes struct and parsed request uri
// if needed
func NewAttributes(event *audit.Event) (authorizer.Attributes, error) {
a := attributes{
event: event,
}
if event.ObjectRef == nil {
u, err := url.ParseRequestURI(a.event.RequestURI)
if err != nil {
return nil, fmt.Errorf("could not parse url: %v", err)
}
a.path = u.Path
}
return &a, nil
}
// GetUser returns the user. This is only used for checking audit policy,
// and the audit policy user check is based off the original user,
// not the impersonated user.
func (a *attributes) GetUser() authuser.Info {
return user(a.event.User)
}
// GetVerb returns the verb
func (a *attributes) GetVerb() string {
return a.event.Verb
}
// IsReadOnly determines if the verb is a read only action
func (a *attributes) IsReadOnly() bool {
return a.event.Verb == "get" || a.event.Verb == "list" || a.event.Verb == "watch"
}
// GetNamespace returns the object namespace if present
func (a *attributes) GetNamespace() string {
if a.event.ObjectRef == nil {
return ""
}
return a.event.ObjectRef.Namespace
}
// GetResource returns the object resource if present
func (a *attributes) GetResource() string {
if a.event.ObjectRef == nil {
return ""
}
return a.event.ObjectRef.Resource
}
// GetSubresource returns the object subresource if present
func (a *attributes) GetSubresource() string {
if a.event.ObjectRef == nil {
return ""
}
return a.event.ObjectRef.Subresource
}
// GetName returns the object name if present
func (a *attributes) GetName() string {
if a.event.ObjectRef == nil {
return ""
}
return a.event.ObjectRef.Name
}
// GetAPIGroup returns the object api group if present
func (a *attributes) GetAPIGroup() string {
if a.event.ObjectRef == nil {
return ""
}
return a.event.ObjectRef.APIGroup
}
// GetAPIVersion returns the object api version if present
func (a *attributes) GetAPIVersion() string {
if a.event.ObjectRef == nil {
return ""
}
return a.event.ObjectRef.APIVersion
}
// IsResourceRequest determines if the request was acted on a resource
func (a *attributes) IsResourceRequest() bool {
return a.event.ObjectRef != nil
}
// GetPath returns the path uri accessed
func (a *attributes) GetPath() string {
return a.path
}
// user represents the event user
type user audit.UserInfo
// GetName returns the user name
func (u user) GetName() string { return u.Username }
// GetUID returns the user uid
func (u user) GetUID() string { return u.UID }
// GetGroups returns the user groups
func (u user) GetGroups() []string { return u.Groups }
// GetExtra returns the user extra data
func (u user) GetExtra() map[string][]string {
m := map[string][]string{}
for k, v := range u.Extra {
m[k] = []string(v)
}
return m
}

73
vendor/k8s.io/apiserver/pkg/audit/format.go generated vendored Normal file
View File

@@ -0,0 +1,73 @@
/*
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 audit
import (
"fmt"
"strconv"
"strings"
"time"
auditinternal "k8s.io/apiserver/pkg/apis/audit"
)
// EventString creates a 1-line text representation of an audit event, using a subset of the
// information in the event struct.
func EventString(ev *auditinternal.Event) string {
username := "<none>"
groups := "<none>"
if len(ev.User.Username) > 0 {
username = ev.User.Username
if len(ev.User.Groups) > 0 {
groups = auditStringSlice(ev.User.Groups)
}
}
asuser := "<self>"
asgroups := "<lookup>"
if ev.ImpersonatedUser != nil {
asuser = ev.ImpersonatedUser.Username
if ev.ImpersonatedUser.Groups != nil {
asgroups = auditStringSlice(ev.ImpersonatedUser.Groups)
}
}
namespace := "<none>"
if ev.ObjectRef != nil && len(ev.ObjectRef.Namespace) != 0 {
namespace = ev.ObjectRef.Namespace
}
response := "<deferred>"
if ev.ResponseStatus != nil {
response = strconv.Itoa(int(ev.ResponseStatus.Code))
}
ip := "<unknown>"
if len(ev.SourceIPs) > 0 {
ip = ev.SourceIPs[0]
}
return fmt.Sprintf("%s AUDIT: id=%q stage=%q ip=%q method=%q user=%q groups=%q as=%q asgroups=%q namespace=%q uri=%q response=\"%s\"",
ev.RequestReceivedTimestamp.Format(time.RFC3339Nano), ev.AuditID, ev.Stage, ip, ev.Verb, username, groups, asuser, asgroups, namespace, ev.RequestURI, response)
}
func auditStringSlice(inList []string) string {
quotedElements := make([]string, len(inList))
for i, in := range inList {
quotedElements[i] = fmt.Sprintf("%q", in)
}
return strings.Join(quotedElements, ",")
}

97
vendor/k8s.io/apiserver/pkg/audit/metrics.go generated vendored Normal file
View File

@@ -0,0 +1,97 @@
/*
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 audit
import (
"fmt"
"github.com/prometheus/client_golang/prometheus"
auditinternal "k8s.io/apiserver/pkg/apis/audit"
"k8s.io/klog"
)
const (
subsystem = "apiserver_audit"
)
var (
eventCounter = prometheus.NewCounter(
prometheus.CounterOpts{
Subsystem: subsystem,
Name: "event_total",
Help: "Counter of audit events generated and sent to the audit backend.",
})
errorCounter = prometheus.NewCounterVec(
prometheus.CounterOpts{
Subsystem: subsystem,
Name: "error_total",
Help: "Counter of audit events that failed to be audited properly. " +
"Plugin identifies the plugin affected by the error.",
},
[]string{"plugin"},
)
levelCounter = prometheus.NewCounterVec(
prometheus.CounterOpts{
Subsystem: subsystem,
Name: "level_total",
Help: "Counter of policy levels for audit events (1 per request).",
},
[]string{"level"},
)
ApiserverAuditDroppedCounter = prometheus.NewCounter(
prometheus.CounterOpts{
Subsystem: subsystem,
Name: "requests_rejected_total",
Help: "Counter of apiserver requests rejected due to an error " +
"in audit logging backend.",
},
)
)
func init() {
prometheus.MustRegister(eventCounter)
prometheus.MustRegister(errorCounter)
prometheus.MustRegister(levelCounter)
prometheus.MustRegister(ApiserverAuditDroppedCounter)
}
// ObserveEvent updates the relevant prometheus metrics for the generated audit event.
func ObserveEvent() {
eventCounter.Inc()
}
// ObservePolicyLevel updates the relevant prometheus metrics with the audit level for a request.
func ObservePolicyLevel(level auditinternal.Level) {
levelCounter.WithLabelValues(string(level)).Inc()
}
// HandlePluginError handles an error that occurred in an audit plugin. This method should only be
// used if the error may have prevented the audit event from being properly recorded. The events are
// logged to the debug log.
func HandlePluginError(plugin string, err error, impacted ...*auditinternal.Event) {
// Count the error.
errorCounter.WithLabelValues(plugin).Add(float64(len(impacted)))
// Log the audit events to the debug log.
msg := fmt.Sprintf("Error in audit plugin '%s' affecting %d audit events: %v\nImpacted events:\n",
plugin, len(impacted), err)
for _, ev := range impacted {
msg = msg + EventString(ev) + "\n"
}
klog.Error(msg)
}

219
vendor/k8s.io/apiserver/pkg/audit/policy/checker.go generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
}

250
vendor/k8s.io/apiserver/pkg/audit/request.go generated vendored Normal file
View File

@@ -0,0 +1,250 @@
/*
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 audit
import (
"bytes"
"fmt"
"net/http"
"time"
"github.com/pborman/uuid"
"k8s.io/klog"
"reflect"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
utilnet "k8s.io/apimachinery/pkg/util/net"
auditinternal "k8s.io/apiserver/pkg/apis/audit"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
)
const (
maxUserAgentLength = 1024
userAgentTruncateSuffix = "...TRUNCATED"
)
func NewEventFromRequest(req *http.Request, level auditinternal.Level, attribs authorizer.Attributes) (*auditinternal.Event, error) {
ev := &auditinternal.Event{
RequestReceivedTimestamp: metav1.NewMicroTime(time.Now()),
Verb: attribs.GetVerb(),
RequestURI: req.URL.RequestURI(),
UserAgent: maybeTruncateUserAgent(req),
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?
ids := req.Header.Get(auditinternal.HeaderAuditID)
if ids != "" {
ev.AuditID = types.UID(ids)
} else {
ev.AuditID = types.UID(uuid.NewRandom().String())
}
ips := utilnet.SourceIPs(req)
ev.SourceIPs = make([]string, len(ips))
for i := range ips {
ev.SourceIPs[i] = ips[i].String()
}
if user := attribs.GetUser(); user != nil {
ev.User.Username = user.GetName()
ev.User.Extra = map[string]auditinternal.ExtraValue{}
for k, v := range user.GetExtra() {
ev.User.Extra[k] = auditinternal.ExtraValue(v)
}
ev.User.Groups = user.GetGroups()
ev.User.UID = user.GetUID()
}
if attribs.IsResourceRequest() {
ev.ObjectRef = &auditinternal.ObjectReference{
Namespace: attribs.GetNamespace(),
Name: attribs.GetName(),
Resource: attribs.GetResource(),
Subresource: attribs.GetSubresource(),
APIGroup: attribs.GetAPIGroup(),
APIVersion: attribs.GetAPIVersion(),
}
}
return ev, nil
}
// LogImpersonatedUser fills in the impersonated user attributes into an audit event.
func LogImpersonatedUser(ae *auditinternal.Event, user user.Info) {
if ae == nil || ae.Level.Less(auditinternal.LevelMetadata) {
return
}
ae.ImpersonatedUser = &auditinternal.UserInfo{
Username: user.GetName(),
}
ae.ImpersonatedUser.Groups = user.GetGroups()
ae.ImpersonatedUser.UID = user.GetUID()
ae.ImpersonatedUser.Extra = map[string]auditinternal.ExtraValue{}
for k, v := range user.GetExtra() {
ae.ImpersonatedUser.Extra[k] = auditinternal.ExtraValue(v)
}
}
// LogRequestObject fills in the request object into an audit event. The passed runtime.Object
// will be converted to the given gv.
func LogRequestObject(ae *auditinternal.Event, obj runtime.Object, gvr schema.GroupVersionResource, subresource string, s runtime.NegotiatedSerializer) {
if ae == nil || ae.Level.Less(auditinternal.LevelMetadata) {
return
}
// complete ObjectRef
if ae.ObjectRef == nil {
ae.ObjectRef = &auditinternal.ObjectReference{}
}
if acc, ok := obj.(metav1.ObjectMetaAccessor); ok {
meta := acc.GetObjectMeta()
if len(ae.ObjectRef.Namespace) == 0 {
ae.ObjectRef.Namespace = meta.GetNamespace()
}
if len(ae.ObjectRef.Name) == 0 {
ae.ObjectRef.Name = meta.GetName()
}
if len(ae.ObjectRef.UID) == 0 {
ae.ObjectRef.UID = meta.GetUID()
}
if len(ae.ObjectRef.ResourceVersion) == 0 {
ae.ObjectRef.ResourceVersion = meta.GetResourceVersion()
}
}
if len(ae.ObjectRef.APIVersion) == 0 {
ae.ObjectRef.APIGroup = gvr.Group
ae.ObjectRef.APIVersion = gvr.Version
}
if len(ae.ObjectRef.Resource) == 0 {
ae.ObjectRef.Resource = gvr.Resource
}
if len(ae.ObjectRef.Subresource) == 0 {
ae.ObjectRef.Subresource = subresource
}
if ae.Level.Less(auditinternal.LevelRequest) {
return
}
// TODO(audit): hook into the serializer to avoid double conversion
var err error
ae.RequestObject, err = encodeObject(obj, gvr.GroupVersion(), s)
if err != nil {
// TODO(audit): add error slice to audit event struct
klog.Warningf("Auditing failed of %v request: %v", reflect.TypeOf(obj).Name(), err)
return
}
}
// LogRequestPatch fills in the given patch as the request object into an audit event.
func LogRequestPatch(ae *auditinternal.Event, patch []byte) {
if ae == nil || ae.Level.Less(auditinternal.LevelRequest) {
return
}
ae.RequestObject = &runtime.Unknown{
Raw: patch,
ContentType: runtime.ContentTypeJSON,
}
}
// LogResponseObject fills in the response object into an audit event. The passed runtime.Object
// will be converted to the given gv.
func LogResponseObject(ae *auditinternal.Event, obj runtime.Object, gv schema.GroupVersion, s runtime.NegotiatedSerializer) {
if ae == nil || ae.Level.Less(auditinternal.LevelMetadata) {
return
}
if status, ok := obj.(*metav1.Status); ok {
// selectively copy the bounded fields.
ae.ResponseStatus = &metav1.Status{
Status: status.Status,
Reason: status.Reason,
Code: status.Code,
}
}
if ae.Level.Less(auditinternal.LevelRequestResponse) {
return
}
// TODO(audit): hook into the serializer to avoid double conversion
var err error
ae.ResponseObject, err = encodeObject(obj, gv, s)
if err != nil {
klog.Warningf("Audit failed for %q response: %v", reflect.TypeOf(obj).Name(), err)
}
}
func encodeObject(obj runtime.Object, gv schema.GroupVersion, serializer runtime.NegotiatedSerializer) (*runtime.Unknown, error) {
supported := serializer.SupportedMediaTypes()
for i := range supported {
if supported[i].MediaType == "application/json" {
enc := serializer.EncoderForVersion(supported[i].Serializer, gv)
var buf bytes.Buffer
if err := enc.Encode(obj, &buf); err != nil {
return nil, fmt.Errorf("encoding failed: %v", err)
}
return &runtime.Unknown{
Raw: buf.Bytes(),
ContentType: runtime.ContentTypeJSON,
}, nil
}
}
return nil, fmt.Errorf("no json encoder found")
}
// LogAnnotation fills in the Annotations according to the key value pair.
func LogAnnotation(ae *auditinternal.Event, key, value string) {
if ae == nil || ae.Level.Less(auditinternal.LevelMetadata) {
return
}
if ae.Annotations == nil {
ae.Annotations = make(map[string]string)
}
if v, ok := ae.Annotations[key]; ok && v != value {
klog.Warningf("Failed to set annotations[%q] to %q for audit:%q, it has already been set to %q", key, value, ae.AuditID, ae.Annotations[key])
return
}
ae.Annotations[key] = value
}
// LogAnnotations fills in the Annotations according to the annotations map.
func LogAnnotations(ae *auditinternal.Event, annotations map[string]string) {
if ae == nil || ae.Level.Less(auditinternal.LevelMetadata) {
return
}
for key, value := range annotations {
LogAnnotation(ae, key, value)
}
}
// truncate User-Agent if too long, otherwise return it directly.
func maybeTruncateUserAgent(req *http.Request) string {
ua := req.UserAgent()
if len(ua) > maxUserAgentLength {
ua = ua[:maxUserAgentLength] + userAgentTruncateSuffix
}
return ua
}

39
vendor/k8s.io/apiserver/pkg/audit/scheme.go generated vendored Normal file
View File

@@ -0,0 +1,39 @@
/*
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 (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apiserver/pkg/apis/audit/v1"
"k8s.io/apiserver/pkg/apis/audit/v1alpha1"
"k8s.io/apiserver/pkg/apis/audit/v1beta1"
)
var Scheme = runtime.NewScheme()
var Codecs = serializer.NewCodecFactory(Scheme)
func init() {
metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
utilruntime.Must(v1.AddToScheme(Scheme))
utilruntime.Must(v1alpha1.AddToScheme(Scheme))
utilruntime.Must(v1beta1.AddToScheme(Scheme))
}

46
vendor/k8s.io/apiserver/pkg/audit/types.go generated vendored Normal file
View File

@@ -0,0 +1,46 @@
/*
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 audit
import (
auditinternal "k8s.io/apiserver/pkg/apis/audit"
)
type Sink interface {
// ProcessEvents handles events. Per audit ID it might be that ProcessEvents is called up to three times.
// Errors might be logged by the sink itself. If an error should be fatal, leading to an internal
// error, ProcessEvents is supposed to panic. The event must not be mutated and is reused by the caller
// after the call returns, i.e. the sink has to make a deepcopy to keep a copy around if necessary.
// Returns true on success, may return false on error.
ProcessEvents(events ...*auditinternal.Event) bool
}
type Backend interface {
Sink
// Run will initialize the backend. It must not block, but may run go routines in the background. If
// stopCh is closed, it is supposed to stop them. Run will be called before the first call to ProcessEvents.
Run(stopCh <-chan struct{}) error
// Shutdown will synchronously shut down the backend while making sure that all pending
// events are delivered. It can be assumed that this method is called after
// the stopCh channel passed to the Run method has been closed.
Shutdown()
// Returns the backend PluginName.
String() string
}

70
vendor/k8s.io/apiserver/pkg/audit/union.go generated vendored Normal file
View File

@@ -0,0 +1,70 @@
/*
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 audit
import (
"fmt"
"strings"
"k8s.io/apimachinery/pkg/util/errors"
auditinternal "k8s.io/apiserver/pkg/apis/audit"
)
// Union returns an audit Backend which logs events to a set of backends. The returned
// Sink implementation blocks in turn for each call to ProcessEvents.
func Union(backends ...Backend) Backend {
if len(backends) == 1 {
return backends[0]
}
return union{backends}
}
type union struct {
backends []Backend
}
func (u union) ProcessEvents(events ...*auditinternal.Event) bool {
success := true
for _, backend := range u.backends {
success = backend.ProcessEvents(events...) && success
}
return success
}
func (u union) Run(stopCh <-chan struct{}) error {
var funcs []func() error
for _, backend := range u.backends {
funcs = append(funcs, func() error {
return backend.Run(stopCh)
})
}
return errors.AggregateGoroutines(funcs...)
}
func (u union) Shutdown() {
for _, backend := range u.backends {
backend.Shutdown()
}
}
func (u union) String() string {
var backendStrings []string
for _, backend := range u.backends {
backendStrings = append(backendStrings, fmt.Sprintf("%s", backend))
}
return fmt.Sprintf("union[%s]", strings.Join(backendStrings, ","))
}

43
vendor/k8s.io/apiserver/pkg/audit/util/conversion.go generated vendored Normal file
View File

@@ -0,0 +1,43 @@
/*
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 util
import (
"k8s.io/api/auditregistration/v1alpha1"
"k8s.io/apiserver/pkg/util/webhook"
)
// HookClientConfigForSink constructs a webhook.ClientConfig using a v1alpha1.AuditSink API object.
// webhook.ClientConfig is used to create a HookClient and the purpose of the config struct is to
// share that with other packages that need to create a HookClient.
func HookClientConfigForSink(a *v1alpha1.AuditSink) webhook.ClientConfig {
c := a.Spec.Webhook.ClientConfig
ret := webhook.ClientConfig{Name: a.Name, CABundle: c.CABundle}
if c.URL != nil {
ret.URL = *c.URL
}
if c.Service != nil {
ret.Service = &webhook.ClientConfigService{
Name: c.Service.Name,
Namespace: c.Service.Namespace,
}
if c.Service.Path != nil {
ret.Service.Path = *c.Service.Path
}
}
return ret
}