mirror of https://github.com/k3s-io/k3s
adds dynamic audit api
parent
f6b54f79fb
commit
381d0a5d14
|
@ -28,6 +28,7 @@ pkg/apis/apps/v1
|
|||
pkg/apis/apps/v1beta1
|
||||
pkg/apis/apps/v1beta2
|
||||
pkg/apis/apps/validation
|
||||
pkg/apis/auditregistration/v1alpha1
|
||||
pkg/apis/authentication
|
||||
pkg/apis/authentication/v1
|
||||
pkg/apis/authentication/v1beta1
|
||||
|
@ -283,6 +284,7 @@ pkg/registry/apps/replicaset/storage
|
|||
pkg/registry/apps/rest
|
||||
pkg/registry/apps/statefulset
|
||||
pkg/registry/apps/statefulset/storage
|
||||
pkg/registry/auditregistration/rest
|
||||
pkg/registry/authentication/rest
|
||||
pkg/registry/authentication/tokenreview
|
||||
pkg/registry/authorization/localsubjectaccessreview
|
||||
|
@ -453,6 +455,7 @@ staging/src/k8s.io/api/admissionregistration/v1beta1
|
|||
staging/src/k8s.io/api/apps/v1
|
||||
staging/src/k8s.io/api/apps/v1beta1
|
||||
staging/src/k8s.io/api/apps/v1beta2
|
||||
staging/src/k8s.io/api/auditregistration/v1alpha1
|
||||
staging/src/k8s.io/api/authentication/v1
|
||||
staging/src/k8s.io/api/authentication/v1beta1
|
||||
staging/src/k8s.io/api/authorization/v1
|
||||
|
@ -555,7 +558,6 @@ staging/src/k8s.io/apiserver/pkg/apis/audit
|
|||
staging/src/k8s.io/apiserver/pkg/apis/audit/v1
|
||||
staging/src/k8s.io/apiserver/pkg/apis/audit/v1alpha1
|
||||
staging/src/k8s.io/apiserver/pkg/apis/audit/v1beta1
|
||||
staging/src/k8s.io/apiserver/pkg/apis/audit/validation
|
||||
staging/src/k8s.io/apiserver/pkg/apis/config/v1alpha1
|
||||
staging/src/k8s.io/apiserver/pkg/apis/example
|
||||
staging/src/k8s.io/apiserver/pkg/apis/example/v1
|
||||
|
|
|
@ -62,6 +62,7 @@ admission.k8s.io/v1beta1 \
|
|||
apps/v1beta1 \
|
||||
apps/v1beta2 \
|
||||
apps/v1 \
|
||||
auditregistration.k8s.io/v1alpha1 \
|
||||
authentication.k8s.io/v1 \
|
||||
authentication.k8s.io/v1beta1 \
|
||||
authorization.k8s.io/v1 \
|
||||
|
|
|
@ -78,6 +78,7 @@ PACKAGES=(
|
|||
k8s.io/api/admissionregistration/v1alpha1
|
||||
k8s.io/api/admissionregistration/v1beta1
|
||||
k8s.io/api/admission/v1beta1
|
||||
k8s.io/api/auditregistration/v1alpha1
|
||||
k8s.io/api/networking/v1
|
||||
k8s.io/metrics/pkg/apis/metrics/v1alpha1
|
||||
k8s.io/metrics/pkg/apis/metrics/v1beta1
|
||||
|
|
|
@ -37,6 +37,7 @@ import (
|
|||
"k8s.io/kubernetes/pkg/apis/admission"
|
||||
"k8s.io/kubernetes/pkg/apis/admissionregistration"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
"k8s.io/kubernetes/pkg/apis/auditregistration"
|
||||
"k8s.io/kubernetes/pkg/apis/authorization"
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling"
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
|
@ -57,6 +58,7 @@ import (
|
|||
_ "k8s.io/kubernetes/pkg/apis/admission/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/admissionregistration/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/apps/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/auditregistration/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/authentication/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/authorization/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/autoscaling/install"
|
||||
|
@ -267,6 +269,12 @@ func init() {
|
|||
externalGroupVersion: externalGroupVersion,
|
||||
}
|
||||
}
|
||||
if _, ok := Groups[auditregistration.GroupName]; !ok {
|
||||
externalGroupVersion := schema.GroupVersion{Group: auditregistration.GroupName, Version: legacyscheme.Scheme.PrioritizedVersionsForGroup(auditregistration.GroupName)[0].Version}
|
||||
Groups[auditregistration.GroupName] = TestGroup{
|
||||
externalGroupVersion: externalGroupVersion,
|
||||
}
|
||||
}
|
||||
|
||||
Default = Groups[api.GroupName]
|
||||
Autoscaling = Groups[autoscaling.GroupName]
|
||||
|
|
|
@ -136,6 +136,8 @@ func TestDefaulting(t *testing.T) {
|
|||
{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "ValidatingWebhookConfigurationList"}: {},
|
||||
{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "MutatingWebhookConfiguration"}: {},
|
||||
{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "MutatingWebhookConfigurationList"}: {},
|
||||
{Group: "auditregistration.k8s.io", Version: "v1alpha1", Kind: "AuditSink"}: {},
|
||||
{Group: "auditregistration.k8s.io", Version: "v1alpha1", Kind: "AuditSinkList"}: {},
|
||||
{Group: "networking.k8s.io", Version: "v1", Kind: "NetworkPolicy"}: {},
|
||||
{Group: "networking.k8s.io", Version: "v1", Kind: "NetworkPolicyList"}: {},
|
||||
{Group: "storage.k8s.io", Version: "v1beta1", Kind: "StorageClass"}: {},
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
admissionregistrationfuzzer "k8s.io/kubernetes/pkg/apis/admissionregistration/fuzzer"
|
||||
appsfuzzer "k8s.io/kubernetes/pkg/apis/apps/fuzzer"
|
||||
auditregistrationfuzzer "k8s.io/kubernetes/pkg/apis/auditregistration/fuzzer"
|
||||
autoscalingfuzzer "k8s.io/kubernetes/pkg/apis/autoscaling/fuzzer"
|
||||
batchfuzzer "k8s.io/kubernetes/pkg/apis/batch/fuzzer"
|
||||
certificatesfuzzer "k8s.io/kubernetes/pkg/apis/certificates/fuzzer"
|
||||
|
@ -101,6 +102,7 @@ var FuzzerFuncs = fuzzer.MergeFuzzerFuncs(
|
|||
policyfuzzer.Funcs,
|
||||
certificatesfuzzer.Funcs,
|
||||
admissionregistrationfuzzer.Funcs,
|
||||
auditregistrationfuzzer.Funcs,
|
||||
storagefuzzer.Funcs,
|
||||
networkingfuzzer.Funcs,
|
||||
)
|
||||
|
|
|
@ -240,6 +240,7 @@ func validateWebhookClientConfig(fldPath *field.Path, cc *admissionregistration.
|
|||
return allErrors
|
||||
}
|
||||
|
||||
// note: this has copy/paste inheritance in auditregistration
|
||||
func validateWebhookService(fldPath *field.Path, svc *admissionregistration.ServiceReference) field.ErrorList {
|
||||
var allErrors field.ErrorList
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:deepcopy-gen=package
|
||||
// +groupName=auditregistration.k8s.io
|
||||
|
||||
package auditregistration // import "k8s.io/kubernetes/pkg/apis/auditregistration"
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
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 fuzzer
|
||||
|
||||
import (
|
||||
fuzz "github.com/google/gofuzz"
|
||||
|
||||
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/kubernetes/pkg/apis/auditregistration"
|
||||
)
|
||||
|
||||
// Funcs returns the fuzzer functions for the auditregistration api group.
|
||||
var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
|
||||
return []interface{}{
|
||||
func(obj *auditregistration.AuditSink, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(obj)
|
||||
v := int64(1)
|
||||
obj.Spec.Webhook.Throttle = &auditregistration.WebhookThrottleConfig{
|
||||
QPS: &v,
|
||||
Burst: &v,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
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 install adds the experimental API group, making it available as
|
||||
// an option to all of the API encoding/decoding machinery.
|
||||
package install
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/auditregistration"
|
||||
"k8s.io/kubernetes/pkg/apis/auditregistration/v1alpha1"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Install(legacyscheme.Scheme)
|
||||
}
|
||||
|
||||
// Install registers the API group and adds types to a scheme
|
||||
func Install(scheme *runtime.Scheme) {
|
||||
utilruntime.Must(auditregistration.AddToScheme(scheme))
|
||||
utilruntime.Must(v1alpha1.AddToScheme(scheme))
|
||||
utilruntime.Must(scheme.SetVersionPriority(v1alpha1.SchemeGroupVersion))
|
||||
}
|
|
@ -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 auditregistration
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// GroupName is the group name use in this package
|
||||
const GroupName = "auditregistration.k8s.io"
|
||||
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
|
||||
|
||||
// Kind takes an unqualified kind and returns a Group qualified GroupKind
|
||||
func Kind(kind string) schema.GroupKind {
|
||||
return SchemeGroupVersion.WithKind(kind).GroupKind()
|
||||
}
|
||||
|
||||
// Resource takes an unqualified resource and returns a Group qualified GroupResource
|
||||
func Resource(resource string) schema.GroupResource {
|
||||
return SchemeGroupVersion.WithResource(resource).GroupResource()
|
||||
}
|
||||
|
||||
var (
|
||||
// SchemeBuilder for audit registration
|
||||
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
|
||||
// AddToScheme audit registration
|
||||
AddToScheme = SchemeBuilder.AddToScheme
|
||||
)
|
||||
|
||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&AuditSink{},
|
||||
&AuditSinkList{},
|
||||
)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
|
||||
package auditregistration
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// Level defines the amount of information logged during auditing
|
||||
type Level string
|
||||
|
||||
// Valid audit levels
|
||||
const (
|
||||
// LevelNone disables auditing
|
||||
LevelNone Level = "None"
|
||||
// LevelMetadata provides the basic level of auditing.
|
||||
LevelMetadata Level = "Metadata"
|
||||
// LevelRequest provides Metadata level of auditing, and additionally
|
||||
// logs the request object (does not apply for non-resource requests).
|
||||
LevelRequest Level = "Request"
|
||||
// LevelRequestResponse provides Request level of auditing, and additionally
|
||||
// logs the response object (does not apply for non-resource requests and watches).
|
||||
LevelRequestResponse Level = "RequestResponse"
|
||||
)
|
||||
|
||||
// Stage defines the stages in request handling during which audit events may be generated.
|
||||
type Stage string
|
||||
|
||||
// Valid audit stages.
|
||||
const (
|
||||
// The stage for events generated after the audit handler receives the request, but before it
|
||||
// is delegated down the handler chain.
|
||||
StageRequestReceived = "RequestReceived"
|
||||
// The stage for events generated after the response headers are sent, but before the response body
|
||||
// is sent. This stage is only generated for long-running requests (e.g. watch).
|
||||
StageResponseStarted = "ResponseStarted"
|
||||
// The stage for events generated after the response body has been completed, and no more bytes
|
||||
// will be sent.
|
||||
StageResponseComplete = "ResponseComplete"
|
||||
// The stage for events generated when a panic occurred.
|
||||
StagePanic = "Panic"
|
||||
)
|
||||
|
||||
// +genclient
|
||||
// +genclient:nonNamespaced
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// AuditSink represents a cluster level sink for audit data
|
||||
type AuditSink struct {
|
||||
metav1.TypeMeta
|
||||
|
||||
// +optional
|
||||
metav1.ObjectMeta
|
||||
|
||||
// Spec defines the audit sink spec
|
||||
Spec AuditSinkSpec
|
||||
}
|
||||
|
||||
// AuditSinkSpec is the spec for the audit sink object
|
||||
type AuditSinkSpec struct {
|
||||
// Policy defines the policy for selecting which events should be sent to the backend
|
||||
// required
|
||||
Policy Policy
|
||||
|
||||
// Webhook to send events
|
||||
// required
|
||||
Webhook Webhook
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// AuditSinkList is a list of a audit sink items.
|
||||
type AuditSinkList struct {
|
||||
metav1.TypeMeta
|
||||
|
||||
// +optional
|
||||
metav1.ListMeta
|
||||
|
||||
// List of audit configurations.
|
||||
Items []AuditSink
|
||||
}
|
||||
|
||||
// Policy defines the configuration of how audit events are logged
|
||||
type Policy struct {
|
||||
// The Level that all requests are recorded at.
|
||||
// available options: None, Metadata, Request, RequestResponse
|
||||
// required
|
||||
Level Level
|
||||
|
||||
// Stages is a list of stages for which events are created.
|
||||
// +optional
|
||||
Stages []Stage
|
||||
}
|
||||
|
||||
// Webhook holds the configuration of the webhooks
|
||||
type Webhook struct {
|
||||
// Throttle holds the options for throttling the webhook
|
||||
// +optional
|
||||
Throttle *WebhookThrottleConfig
|
||||
|
||||
// ClientConfig holds the connection parameters for the webhook
|
||||
// required
|
||||
ClientConfig WebhookClientConfig
|
||||
}
|
||||
|
||||
// WebhookThrottleConfig holds the configuration for throttling
|
||||
type WebhookThrottleConfig struct {
|
||||
// QPS maximum number of batches per second
|
||||
// default 10 QPS
|
||||
// +optional
|
||||
QPS *int64
|
||||
|
||||
// Burst is the maximum number of events sent at the same moment
|
||||
// default 15 QPS
|
||||
// +optional
|
||||
Burst *int64
|
||||
}
|
||||
|
||||
// WebhookClientConfig contains the information to make a connection with the webhook
|
||||
type WebhookClientConfig struct {
|
||||
// `url` gives the location of the webhook, in standard URL form
|
||||
// (`[scheme://]host:port/path`). Exactly one of `url` or `service`
|
||||
// must be specified.
|
||||
//
|
||||
// The `host` should not refer to a service running in the cluster; use
|
||||
// the `service` field instead. The host might be resolved via external
|
||||
// DNS in some apiservers (e.g., `kube-apiserver` cannot resolve
|
||||
// in-cluster DNS as that would be a layering violation). `host` may
|
||||
// also be an IP address.
|
||||
//
|
||||
// Please note that using `localhost` or `127.0.0.1` as a `host` is
|
||||
// risky unless you take great care to run this webhook on all hosts
|
||||
// which run an apiserver which might need to make calls to this
|
||||
// webhook. Such installs are likely to be non-portable, i.e., not easy
|
||||
// to turn up in a new cluster.
|
||||
//
|
||||
// The scheme must be "https"; the URL must begin with "https://".
|
||||
//
|
||||
// A path is optional, and if present may be any string permissible in
|
||||
// a URL. You may use the path to pass an arbitrary string to the
|
||||
// webhook, for example, a cluster identifier.
|
||||
//
|
||||
// Attempting to use a user or basic auth e.g. "user:password@" is not
|
||||
// allowed. Fragments ("#...") and query parameters ("?...") are not
|
||||
// allowed, either.
|
||||
//
|
||||
// +optional
|
||||
URL *string
|
||||
|
||||
// `service` is a reference to the service for this webhook. Either
|
||||
// `service` or `url` must be specified.
|
||||
//
|
||||
// If the webhook is running within the cluster, then you should use `service`.
|
||||
//
|
||||
// Port 443 will be used if it is open, otherwise it is an error.
|
||||
//
|
||||
// +optional
|
||||
Service *ServiceReference
|
||||
|
||||
// `caBundle` is a PEM encoded CA bundle which will be used to validate
|
||||
// the webhook's server certificate.
|
||||
// defaults to the apiservers CA bundle for the endpoint type
|
||||
// +optional
|
||||
CABundle []byte
|
||||
}
|
||||
|
||||
// ServiceReference holds a reference to Service.legacy.k8s.io
|
||||
type ServiceReference struct {
|
||||
// `namespace` is the namespace of the service.
|
||||
// Required
|
||||
Namespace string
|
||||
|
||||
// `name` is the name of the service.
|
||||
// Required
|
||||
Name string
|
||||
|
||||
// `path` is an optional URL path which will be sent in any request to
|
||||
// this service.
|
||||
// +optional
|
||||
Path *string
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
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 v1alpha1
|
||||
|
||||
import (
|
||||
auditregistrationv1alpha1 "k8s.io/api/auditregistration/v1alpha1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilpointer "k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultQPS is the default QPS value
|
||||
DefaultQPS = int64(10)
|
||||
// DefaultBurst is the default burst value
|
||||
DefaultBurst = int64(15)
|
||||
)
|
||||
|
||||
// DefaultThrottle is a default throttle config
|
||||
func DefaultThrottle() *auditregistrationv1alpha1.WebhookThrottleConfig {
|
||||
return &auditregistrationv1alpha1.WebhookThrottleConfig{
|
||||
QPS: utilpointer.Int64Ptr(DefaultQPS),
|
||||
Burst: utilpointer.Int64Ptr(DefaultBurst),
|
||||
}
|
||||
}
|
||||
|
||||
func addDefaultingFuncs(scheme *runtime.Scheme) error {
|
||||
return RegisterDefaults(scheme)
|
||||
}
|
||||
|
||||
// SetDefaults_AuditSink sets defaults if the audit sink isn't present
|
||||
func SetDefaults_AuditSink(obj *auditregistrationv1alpha1.AuditSink) {
|
||||
if obj.Spec.Webhook.Throttle != nil {
|
||||
if obj.Spec.Webhook.Throttle.QPS == nil {
|
||||
obj.Spec.Webhook.Throttle.QPS = utilpointer.Int64Ptr(DefaultQPS)
|
||||
}
|
||||
if obj.Spec.Webhook.Throttle.Burst == nil {
|
||||
obj.Spec.Webhook.Throttle.Burst = utilpointer.Int64Ptr(DefaultBurst)
|
||||
}
|
||||
} else {
|
||||
obj.Spec.Webhook.Throttle = DefaultThrottle()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
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 v1alpha1_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
auditregistrationv1alpha1 "k8s.io/api/auditregistration/v1alpha1"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
_ "k8s.io/kubernetes/pkg/apis/auditregistration/install"
|
||||
. "k8s.io/kubernetes/pkg/apis/auditregistration/v1alpha1"
|
||||
utilpointer "k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
func TestSetDefaultAuditSink(t *testing.T) {
|
||||
defaultURL := "http://test"
|
||||
tests := []struct {
|
||||
original *auditregistrationv1alpha1.AuditSink
|
||||
expected *auditregistrationv1alpha1.AuditSink
|
||||
}{
|
||||
{ // Missing Throttle
|
||||
original: &auditregistrationv1alpha1.AuditSink{
|
||||
Spec: auditregistrationv1alpha1.AuditSinkSpec{
|
||||
Policy: auditregistrationv1alpha1.Policy{
|
||||
Level: auditregistrationv1alpha1.LevelMetadata,
|
||||
},
|
||||
Webhook: auditregistrationv1alpha1.Webhook{
|
||||
ClientConfig: auditregistrationv1alpha1.WebhookClientConfig{
|
||||
URL: &defaultURL,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &auditregistrationv1alpha1.AuditSink{
|
||||
Spec: auditregistrationv1alpha1.AuditSinkSpec{
|
||||
Policy: auditregistrationv1alpha1.Policy{
|
||||
Level: auditregistrationv1alpha1.LevelMetadata,
|
||||
},
|
||||
Webhook: auditregistrationv1alpha1.Webhook{
|
||||
Throttle: DefaultThrottle(),
|
||||
ClientConfig: auditregistrationv1alpha1.WebhookClientConfig{
|
||||
URL: &defaultURL,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ // Missing QPS
|
||||
original: &auditregistrationv1alpha1.AuditSink{
|
||||
Spec: auditregistrationv1alpha1.AuditSinkSpec{
|
||||
Policy: auditregistrationv1alpha1.Policy{
|
||||
Level: auditregistrationv1alpha1.LevelMetadata,
|
||||
},
|
||||
Webhook: auditregistrationv1alpha1.Webhook{
|
||||
Throttle: &auditregistrationv1alpha1.WebhookThrottleConfig{
|
||||
Burst: utilpointer.Int64Ptr(1),
|
||||
},
|
||||
ClientConfig: auditregistrationv1alpha1.WebhookClientConfig{
|
||||
URL: &defaultURL,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &auditregistrationv1alpha1.AuditSink{
|
||||
Spec: auditregistrationv1alpha1.AuditSinkSpec{
|
||||
Policy: auditregistrationv1alpha1.Policy{
|
||||
Level: auditregistrationv1alpha1.LevelMetadata,
|
||||
},
|
||||
Webhook: auditregistrationv1alpha1.Webhook{
|
||||
Throttle: &auditregistrationv1alpha1.WebhookThrottleConfig{
|
||||
QPS: DefaultThrottle().QPS,
|
||||
Burst: utilpointer.Int64Ptr(1),
|
||||
},
|
||||
ClientConfig: auditregistrationv1alpha1.WebhookClientConfig{
|
||||
URL: &defaultURL,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ // Missing Burst
|
||||
original: &auditregistrationv1alpha1.AuditSink{
|
||||
Spec: auditregistrationv1alpha1.AuditSinkSpec{
|
||||
Policy: auditregistrationv1alpha1.Policy{
|
||||
Level: auditregistrationv1alpha1.LevelMetadata,
|
||||
},
|
||||
Webhook: auditregistrationv1alpha1.Webhook{
|
||||
Throttle: &auditregistrationv1alpha1.WebhookThrottleConfig{
|
||||
QPS: utilpointer.Int64Ptr(1),
|
||||
},
|
||||
ClientConfig: auditregistrationv1alpha1.WebhookClientConfig{
|
||||
URL: &defaultURL,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &auditregistrationv1alpha1.AuditSink{
|
||||
Spec: auditregistrationv1alpha1.AuditSinkSpec{
|
||||
Policy: auditregistrationv1alpha1.Policy{
|
||||
Level: auditregistrationv1alpha1.LevelMetadata,
|
||||
},
|
||||
Webhook: auditregistrationv1alpha1.Webhook{
|
||||
Throttle: &auditregistrationv1alpha1.WebhookThrottleConfig{
|
||||
QPS: utilpointer.Int64Ptr(1),
|
||||
Burst: DefaultThrottle().Burst,
|
||||
},
|
||||
ClientConfig: auditregistrationv1alpha1.WebhookClientConfig{
|
||||
URL: &defaultURL,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
original := test.original
|
||||
expected := test.expected
|
||||
obj2 := roundTrip(t, runtime.Object(original))
|
||||
got, ok := obj2.(*auditregistrationv1alpha1.AuditSink)
|
||||
if !ok {
|
||||
t.Fatalf("(%d) unexpected object: %v", i, obj2)
|
||||
}
|
||||
if !apiequality.Semantic.DeepEqual(got.Spec, expected.Spec) {
|
||||
t.Errorf("(%d) got different than expected\ngot:\n\t%+v\nexpected:\n\t%+v", i, got.Spec, expected.Spec)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func roundTrip(t *testing.T, obj runtime.Object) runtime.Object {
|
||||
data, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(SchemeGroupVersion), obj)
|
||||
if err != nil {
|
||||
t.Errorf("%v\n %#v", err, obj)
|
||||
return nil
|
||||
}
|
||||
obj2, err := runtime.Decode(legacyscheme.Codecs.UniversalDecoder(), data)
|
||||
if err != nil {
|
||||
t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), obj)
|
||||
return nil
|
||||
}
|
||||
obj3 := reflect.New(reflect.TypeOf(obj).Elem()).Interface().(runtime.Object)
|
||||
err = legacyscheme.Scheme.Convert(obj2, obj3, nil)
|
||||
if err != nil {
|
||||
t.Errorf("%v\nSource: %#v", err, obj2)
|
||||
return nil
|
||||
}
|
||||
return obj3
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/auditregistration
|
||||
// +k8s:conversion-gen-external-types=k8s.io/api/auditregistration/v1alpha1
|
||||
// +k8s:defaulter-gen=TypeMeta
|
||||
// +k8s:defaulter-gen-input=../../../../vendor/k8s.io/api/auditregistration/v1alpha1
|
||||
|
||||
// +groupName=auditregistration.k8s.io
|
||||
|
||||
package v1alpha1 // import "k8s.io/kubernetes/pkg/apis/auditregistration/v1alpha1"
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
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 v1alpha1
|
||||
|
||||
import (
|
||||
auditregistrationv1alpha1 "k8s.io/api/auditregistration/v1alpha1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// GroupName for audit registration
|
||||
const GroupName = "auditregistration.k8s.io"
|
||||
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"}
|
||||
|
||||
// Resource takes an unqualified resource and returns a Group qualified GroupResource
|
||||
func Resource(resource string) schema.GroupResource {
|
||||
return SchemeGroupVersion.WithResource(resource).GroupResource()
|
||||
}
|
||||
|
||||
var (
|
||||
localSchemeBuilder = &auditregistrationv1alpha1.SchemeBuilder
|
||||
// AddToScheme audit registration
|
||||
AddToScheme = localSchemeBuilder.AddToScheme
|
||||
)
|
||||
|
||||
func init() {
|
||||
// We only register manually written functions here. The registration of the
|
||||
// generated functions takes place in the generated files. The separation
|
||||
// makes the code compile even when the generated files are missing.
|
||||
localSchemeBuilder.Register(addDefaultingFuncs)
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
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 validation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
genericvalidation "k8s.io/apimachinery/pkg/api/validation"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/kubernetes/pkg/apis/auditregistration"
|
||||
)
|
||||
|
||||
// ValidateAuditSink validates the AuditSinks
|
||||
func ValidateAuditSink(as *auditregistration.AuditSink) field.ErrorList {
|
||||
allErrs := genericvalidation.ValidateObjectMeta(&as.ObjectMeta, false, genericvalidation.NameIsDNSSubdomain, field.NewPath("metadata"))
|
||||
allErrs = append(allErrs, ValidateAuditSinkSpec(as.Spec, field.NewPath("spec"))...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateAuditSinkSpec validates the sink spec for audit
|
||||
func ValidateAuditSinkSpec(s auditregistration.AuditSinkSpec, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
allErrs = append(allErrs, ValidatePolicy(s.Policy, field.NewPath("policy"))...)
|
||||
allErrs = append(allErrs, ValidateWebhook(s.Webhook, field.NewPath("webhook"))...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateWebhook validates the webhook
|
||||
func ValidateWebhook(w auditregistration.Webhook, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
if w.Throttle != nil {
|
||||
allErrs = append(allErrs, ValidateWebhookThrottleConfig(w.Throttle, fldPath.Child("throttle"))...)
|
||||
}
|
||||
allErrs = append(allErrs, ValidateWebhookClientConfig(&w.ClientConfig, fldPath.Child("clientConfig"))...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateWebhookThrottleConfig validates the throttle config
|
||||
func ValidateWebhookThrottleConfig(c *auditregistration.WebhookThrottleConfig, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
if c.QPS != nil && *c.QPS <= 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("qps"), c.QPS, "qps must be a positive number"))
|
||||
}
|
||||
if c.Burst != nil && *c.Burst <= 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("burst"), c.Burst, "burst must be a positive number"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateWebhookClientConfig validates the WebhookClientConfig
|
||||
// note: this is largely copy/paste inheritance from admissionregistration with subtle changes
|
||||
func ValidateWebhookClientConfig(cc *auditregistration.WebhookClientConfig, fldPath *field.Path) field.ErrorList {
|
||||
var allErrors field.ErrorList
|
||||
if (cc.URL == nil) == (cc.Service == nil) {
|
||||
allErrors = append(allErrors, field.Required(fldPath.Child("url"), "exactly one of url or service is required"))
|
||||
}
|
||||
|
||||
if cc.URL != nil {
|
||||
const form = "; desired format: https://host[/path]"
|
||||
if u, err := url.Parse(*cc.URL); err != nil {
|
||||
allErrors = append(allErrors, field.Required(fldPath.Child("url"), "url must be a valid URL: "+err.Error()+form))
|
||||
} else {
|
||||
if len(u.Host) == 0 {
|
||||
allErrors = append(allErrors, field.Invalid(fldPath.Child("url"), u.Host, "host must be provided"+form))
|
||||
}
|
||||
if u.User != nil {
|
||||
allErrors = append(allErrors, field.Invalid(fldPath.Child("url"), u.User.String(), "user information is not permitted in the URL"))
|
||||
}
|
||||
if len(u.Fragment) != 0 {
|
||||
allErrors = append(allErrors, field.Invalid(fldPath.Child("url"), u.Fragment, "fragments are not permitted in the URL"))
|
||||
}
|
||||
if len(u.RawQuery) != 0 {
|
||||
allErrors = append(allErrors, field.Invalid(fldPath.Child("url"), u.RawQuery, "query parameters are not permitted in the URL"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cc.Service != nil {
|
||||
allErrors = append(allErrors, validateWebhookService(cc.Service, fldPath.Child("service"))...)
|
||||
}
|
||||
return allErrors
|
||||
}
|
||||
|
||||
// note: this is copy/paste inheritance from admissionregistration
|
||||
func validateWebhookService(svc *auditregistration.ServiceReference, fldPath *field.Path) field.ErrorList {
|
||||
var allErrors field.ErrorList
|
||||
|
||||
if len(svc.Name) == 0 {
|
||||
allErrors = append(allErrors, field.Required(fldPath.Child("name"), "service name is required"))
|
||||
}
|
||||
|
||||
if len(svc.Namespace) == 0 {
|
||||
allErrors = append(allErrors, field.Required(fldPath.Child("namespace"), "service namespace is required"))
|
||||
}
|
||||
|
||||
if svc.Path == nil {
|
||||
return allErrors
|
||||
}
|
||||
|
||||
// TODO: replace below with url.Parse + verifying that host is empty?
|
||||
|
||||
urlPath := *svc.Path
|
||||
if urlPath == "/" || len(urlPath) == 0 {
|
||||
return allErrors
|
||||
}
|
||||
if urlPath == "//" {
|
||||
allErrors = append(allErrors, field.Invalid(fldPath.Child("path"), urlPath, "segment[0] may not be empty"))
|
||||
return allErrors
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(urlPath, "/") {
|
||||
allErrors = append(allErrors, field.Invalid(fldPath.Child("path"), urlPath, "must start with a '/'"))
|
||||
}
|
||||
|
||||
urlPathToCheck := urlPath[1:]
|
||||
if strings.HasSuffix(urlPathToCheck, "/") {
|
||||
urlPathToCheck = urlPathToCheck[:len(urlPathToCheck)-1]
|
||||
}
|
||||
steps := strings.Split(urlPathToCheck, "/")
|
||||
for i, step := range steps {
|
||||
if len(step) == 0 {
|
||||
allErrors = append(allErrors, field.Invalid(fldPath.Child("path"), urlPath, fmt.Sprintf("segment[%d] may not be empty", i)))
|
||||
continue
|
||||
}
|
||||
failures := validation.IsDNS1123Subdomain(step)
|
||||
for _, failure := range failures {
|
||||
allErrors = append(allErrors, field.Invalid(fldPath.Child("path"), urlPath, fmt.Sprintf("segment[%d]: %v", i, failure)))
|
||||
}
|
||||
}
|
||||
|
||||
return allErrors
|
||||
}
|
||||
|
||||
// ValidatePolicy validates the audit policy
|
||||
func ValidatePolicy(policy auditregistration.Policy, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
allErrs = append(allErrs, validateStages(policy.Stages, fldPath.Child("stages"))...)
|
||||
allErrs = append(allErrs, validateLevel(policy.Level, fldPath.Child("level"))...)
|
||||
if policy.Level != auditregistration.LevelNone && len(policy.Stages) == 0 {
|
||||
return field.ErrorList{field.Required(fldPath.Child("stages"), "")}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
var validLevels = sets.NewString(
|
||||
string(auditregistration.LevelNone),
|
||||
string(auditregistration.LevelMetadata),
|
||||
string(auditregistration.LevelRequest),
|
||||
string(auditregistration.LevelRequestResponse),
|
||||
)
|
||||
|
||||
var validStages = sets.NewString(
|
||||
string(auditregistration.StageRequestReceived),
|
||||
string(auditregistration.StageResponseStarted),
|
||||
string(auditregistration.StageResponseComplete),
|
||||
string(auditregistration.StagePanic),
|
||||
)
|
||||
|
||||
func validateLevel(level auditregistration.Level, fldPath *field.Path) field.ErrorList {
|
||||
if string(level) == "" {
|
||||
return field.ErrorList{field.Required(fldPath, "")}
|
||||
}
|
||||
if !validLevels.Has(string(level)) {
|
||||
return field.ErrorList{field.NotSupported(fldPath, level, validLevels.List())}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateStages(stages []auditregistration.Stage, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
for i, stage := range stages {
|
||||
if !validStages.Has(string(stage)) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Index(i), string(stage), "allowed stages are "+strings.Join(validStages.List(), ",")))
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateAuditSinkUpdate validates an update to the object
|
||||
func ValidateAuditSinkUpdate(newC, oldC *auditregistration.AuditSink) field.ErrorList {
|
||||
return ValidateAuditSink(newC)
|
||||
}
|
|
@ -0,0 +1,324 @@
|
|||
/*
|
||||
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 validation
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/kubernetes/pkg/apis/auditregistration"
|
||||
)
|
||||
|
||||
func TestValidateAuditSink(t *testing.T) {
|
||||
testQPS := int64(10)
|
||||
testURL := "http://localhost"
|
||||
testCases := []struct {
|
||||
name string
|
||||
conf auditregistration.AuditSink
|
||||
numErr int
|
||||
}{
|
||||
{
|
||||
name: "should pass full config",
|
||||
conf: auditregistration.AuditSink{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "myconf",
|
||||
},
|
||||
Spec: auditregistration.AuditSinkSpec{
|
||||
Policy: auditregistration.Policy{
|
||||
Level: auditregistration.LevelRequest,
|
||||
Stages: []auditregistration.Stage{
|
||||
auditregistration.StageRequestReceived,
|
||||
},
|
||||
},
|
||||
Webhook: auditregistration.Webhook{
|
||||
Throttle: &auditregistration.WebhookThrottleConfig{
|
||||
QPS: &testQPS,
|
||||
},
|
||||
ClientConfig: auditregistration.WebhookClientConfig{
|
||||
URL: &testURL,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
numErr: 0,
|
||||
},
|
||||
{
|
||||
name: "should fail no policy",
|
||||
conf: auditregistration.AuditSink{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "myconf",
|
||||
},
|
||||
Spec: auditregistration.AuditSinkSpec{
|
||||
Webhook: auditregistration.Webhook{
|
||||
ClientConfig: auditregistration.WebhookClientConfig{
|
||||
URL: &testURL,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
numErr: 1,
|
||||
},
|
||||
{
|
||||
name: "should fail no webhook",
|
||||
conf: auditregistration.AuditSink{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "myconf",
|
||||
},
|
||||
Spec: auditregistration.AuditSinkSpec{
|
||||
Policy: auditregistration.Policy{
|
||||
Level: auditregistration.LevelMetadata,
|
||||
Stages: []auditregistration.Stage{
|
||||
auditregistration.StageRequestReceived,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
numErr: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
errs := ValidateAuditSink(&test.conf)
|
||||
require.Len(t, errs, test.numErr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePolicy(t *testing.T) {
|
||||
successCases := []auditregistration.Policy{}
|
||||
successCases = append(successCases, auditregistration.Policy{ // Policy with omitStages and level
|
||||
Level: auditregistration.LevelRequest,
|
||||
Stages: []auditregistration.Stage{
|
||||
auditregistration.Stage("RequestReceived"),
|
||||
auditregistration.Stage("ResponseStarted"),
|
||||
},
|
||||
})
|
||||
successCases = append(successCases, auditregistration.Policy{Level: auditregistration.LevelNone}) // Policy with none level only
|
||||
|
||||
for i, policy := range successCases {
|
||||
if errs := ValidatePolicy(policy, field.NewPath("policy")); len(errs) != 0 {
|
||||
t.Errorf("[%d] Expected policy %#v to be valid: %v", i, policy, errs)
|
||||
}
|
||||
}
|
||||
|
||||
errorCases := []auditregistration.Policy{}
|
||||
errorCases = append(errorCases, auditregistration.Policy{}) // Empty policy // Policy with missing level
|
||||
errorCases = append(errorCases, auditregistration.Policy{Stages: []auditregistration.Stage{ // Policy with invalid stages
|
||||
auditregistration.Stage("Bad")}})
|
||||
errorCases = append(errorCases, auditregistration.Policy{Level: auditregistration.Level("invalid")}) // Policy with bad level
|
||||
errorCases = append(errorCases, auditregistration.Policy{Level: auditregistration.LevelMetadata}) // Policy without stages
|
||||
|
||||
for i, policy := range errorCases {
|
||||
if errs := ValidatePolicy(policy, field.NewPath("policy")); len(errs) == 0 {
|
||||
t.Errorf("[%d] Expected policy %#v to be invalid!", i, policy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func strPtr(s string) *string { return &s }
|
||||
|
||||
func TestValidateWebhookConfiguration(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config auditregistration.Webhook
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "both service and URL missing",
|
||||
config: auditregistration.Webhook{
|
||||
ClientConfig: auditregistration.WebhookClientConfig{},
|
||||
},
|
||||
expectedError: `exactly one of`,
|
||||
},
|
||||
{
|
||||
name: "both service and URL provided",
|
||||
config: auditregistration.Webhook{
|
||||
ClientConfig: auditregistration.WebhookClientConfig{
|
||||
Service: &auditregistration.ServiceReference{
|
||||
Namespace: "ns",
|
||||
Name: "n",
|
||||
},
|
||||
URL: strPtr("example.com/k8s/webhook"),
|
||||
},
|
||||
},
|
||||
expectedError: `webhook.clientConfig.url: Required value: exactly one of url or service is required`,
|
||||
},
|
||||
{
|
||||
name: "blank URL",
|
||||
config: auditregistration.Webhook{
|
||||
ClientConfig: auditregistration.WebhookClientConfig{
|
||||
URL: strPtr(""),
|
||||
},
|
||||
},
|
||||
expectedError: `webhook.clientConfig.url: Invalid value: "": host must be provided`,
|
||||
},
|
||||
{
|
||||
name: "missing host",
|
||||
config: auditregistration.Webhook{
|
||||
ClientConfig: auditregistration.WebhookClientConfig{
|
||||
URL: strPtr("https:///fancy/webhook"),
|
||||
},
|
||||
},
|
||||
expectedError: `host must be provided`,
|
||||
},
|
||||
{
|
||||
name: "fragment",
|
||||
config: auditregistration.Webhook{
|
||||
ClientConfig: auditregistration.WebhookClientConfig{
|
||||
URL: strPtr("https://example.com/#bookmark"),
|
||||
},
|
||||
},
|
||||
expectedError: `"bookmark": fragments are not permitted`,
|
||||
},
|
||||
{
|
||||
name: "query",
|
||||
config: auditregistration.Webhook{
|
||||
ClientConfig: auditregistration.WebhookClientConfig{
|
||||
URL: strPtr("https://example.com?arg=value"),
|
||||
},
|
||||
},
|
||||
expectedError: `"arg=value": query parameters are not permitted`,
|
||||
},
|
||||
{
|
||||
name: "user",
|
||||
config: auditregistration.Webhook{
|
||||
ClientConfig: auditregistration.WebhookClientConfig{
|
||||
URL: strPtr("https://harry.potter@example.com/"),
|
||||
},
|
||||
},
|
||||
expectedError: `"harry.potter": user information is not permitted`,
|
||||
},
|
||||
{
|
||||
name: "just totally wrong",
|
||||
config: auditregistration.Webhook{
|
||||
ClientConfig: auditregistration.WebhookClientConfig{
|
||||
URL: strPtr("arg#backwards=thisis?html.index/port:host//:https"),
|
||||
},
|
||||
},
|
||||
expectedError: `host must be provided`,
|
||||
},
|
||||
{
|
||||
name: "path must start with slash",
|
||||
config: auditregistration.Webhook{
|
||||
ClientConfig: auditregistration.WebhookClientConfig{
|
||||
Service: &auditregistration.ServiceReference{
|
||||
Namespace: "ns",
|
||||
Name: "n",
|
||||
Path: strPtr("foo/"),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: `clientConfig.service.path: Invalid value: "foo/": must start with a '/'`,
|
||||
},
|
||||
{
|
||||
name: "path accepts slash",
|
||||
config: auditregistration.Webhook{
|
||||
ClientConfig: auditregistration.WebhookClientConfig{
|
||||
Service: &auditregistration.ServiceReference{
|
||||
Namespace: "ns",
|
||||
Name: "n",
|
||||
Path: strPtr("/"),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: ``,
|
||||
},
|
||||
{
|
||||
name: "path accepts no trailing slash",
|
||||
config: auditregistration.Webhook{
|
||||
ClientConfig: auditregistration.WebhookClientConfig{
|
||||
Service: &auditregistration.ServiceReference{
|
||||
Namespace: "ns",
|
||||
Name: "n",
|
||||
Path: strPtr("/foo"),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: ``,
|
||||
},
|
||||
{
|
||||
name: "path fails //",
|
||||
config: auditregistration.Webhook{
|
||||
ClientConfig: auditregistration.WebhookClientConfig{
|
||||
Service: &auditregistration.ServiceReference{
|
||||
Namespace: "ns",
|
||||
Name: "n",
|
||||
Path: strPtr("//"),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: `clientConfig.service.path: Invalid value: "//": segment[0] may not be empty`,
|
||||
},
|
||||
{
|
||||
name: "path no empty step",
|
||||
config: auditregistration.Webhook{
|
||||
ClientConfig: auditregistration.WebhookClientConfig{
|
||||
Service: &auditregistration.ServiceReference{
|
||||
Namespace: "ns",
|
||||
Name: "n",
|
||||
Path: strPtr("/foo//bar/"),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: `clientConfig.service.path: Invalid value: "/foo//bar/": segment[1] may not be empty`,
|
||||
}, {
|
||||
name: "path no empty step 2",
|
||||
config: auditregistration.Webhook{
|
||||
ClientConfig: auditregistration.WebhookClientConfig{
|
||||
Service: &auditregistration.ServiceReference{
|
||||
Namespace: "ns",
|
||||
Name: "n",
|
||||
Path: strPtr("/foo/bar//"),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: `clientConfig.service.path: Invalid value: "/foo/bar//": segment[2] may not be empty`,
|
||||
},
|
||||
{
|
||||
name: "path no non-subdomain",
|
||||
config: auditregistration.Webhook{
|
||||
ClientConfig: auditregistration.WebhookClientConfig{
|
||||
Service: &auditregistration.ServiceReference{
|
||||
Namespace: "ns",
|
||||
Name: "n",
|
||||
Path: strPtr("/apis/foo.bar/v1alpha1/--bad"),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: `clientConfig.service.path: Invalid value: "/apis/foo.bar/v1alpha1/--bad": segment[3]: a DNS-1123 subdomain`,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
errs := ValidateWebhook(test.config, field.NewPath("webhook"))
|
||||
err := errs.ToAggregate()
|
||||
if err != nil {
|
||||
if e, a := test.expectedError, err.Error(); !strings.Contains(a, e) || e == "" {
|
||||
t.Errorf("expected to contain \nerr: %s \ngot: %s", e, a)
|
||||
}
|
||||
} else {
|
||||
if test.expectedError != "" {
|
||||
t.Errorf("unexpected no error, expected to contain %s", test.expectedError)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ import (
|
|||
_ "k8s.io/kubernetes/pkg/apis/admission/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/admissionregistration/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/apps/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/auditregistration/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/authentication/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/authorization/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/autoscaling/install"
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
appsv1 "k8s.io/api/apps/v1"
|
||||
appsv1beta1 "k8s.io/api/apps/v1beta1"
|
||||
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
||||
auditregistrationv1alpha1 "k8s.io/api/auditregistration/v1alpha1"
|
||||
authenticationv1 "k8s.io/api/authentication/v1"
|
||||
authenticationv1beta1 "k8s.io/api/authentication/v1beta1"
|
||||
authorizationapiv1 "k8s.io/api/authorization/v1"
|
||||
|
@ -83,6 +84,7 @@ import (
|
|||
// RESTStorage installers
|
||||
admissionregistrationrest "k8s.io/kubernetes/pkg/registry/admissionregistration/rest"
|
||||
appsrest "k8s.io/kubernetes/pkg/registry/apps/rest"
|
||||
auditregistrationrest "k8s.io/kubernetes/pkg/registry/auditregistration/rest"
|
||||
authenticationrest "k8s.io/kubernetes/pkg/registry/authentication/rest"
|
||||
authorizationrest "k8s.io/kubernetes/pkg/registry/authorization/rest"
|
||||
autoscalingrest "k8s.io/kubernetes/pkg/registry/autoscaling/rest"
|
||||
|
@ -346,6 +348,7 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget)
|
|||
// TODO: describe the priority all the way down in the RESTStorageProviders and plumb it back through the various discovery
|
||||
// handlers that we have.
|
||||
restStorageProviders := []RESTStorageProvider{
|
||||
auditregistrationrest.RESTStorageProvider{},
|
||||
authenticationrest.RESTStorageProvider{Authenticator: c.GenericConfig.Authentication.Authenticator},
|
||||
authorizationrest.RESTStorageProvider{Authorizer: c.GenericConfig.Authorization.Authorizer, RuleResolver: c.GenericConfig.RuleResolver},
|
||||
autoscalingrest.RESTStorageProvider{},
|
||||
|
@ -505,6 +508,7 @@ func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig {
|
|||
)
|
||||
// disable alpha versions explicitly so we have a full list of what's possible to serve
|
||||
ret.DisableVersions(
|
||||
auditregistrationv1alpha1.SchemeGroupVersion,
|
||||
admissionregistrationv1alpha1.SchemeGroupVersion,
|
||||
batchapiv2alpha1.SchemeGroupVersion,
|
||||
rbacv1alpha1.SchemeGroupVersion,
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
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 auditsink // import "k8s.io/kubernetes/pkg/registry/auditregistration/auditsink"
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
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 storage
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
"k8s.io/kubernetes/pkg/apis/auditregistration"
|
||||
auditstrategy "k8s.io/kubernetes/pkg/registry/auditregistration/auditsink"
|
||||
)
|
||||
|
||||
// REST implements a RESTStorage for audit sink against etcd
|
||||
type REST struct {
|
||||
*genericregistry.Store
|
||||
}
|
||||
|
||||
// NewREST returns a RESTStorage object that will work against audit sinks
|
||||
func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
|
||||
store := &genericregistry.Store{
|
||||
NewFunc: func() runtime.Object { return &auditregistration.AuditSink{} },
|
||||
NewListFunc: func() runtime.Object { return &auditregistration.AuditSinkList{} },
|
||||
ObjectNameFunc: func(obj runtime.Object) (string, error) {
|
||||
return obj.(*auditregistration.AuditSink).Name, nil
|
||||
},
|
||||
DefaultQualifiedResource: auditregistration.Resource("auditsinks"),
|
||||
|
||||
CreateStrategy: auditstrategy.Strategy,
|
||||
UpdateStrategy: auditstrategy.Strategy,
|
||||
DeleteStrategy: auditstrategy.Strategy,
|
||||
}
|
||||
options := &generic.StoreOptions{RESTOptions: optsGetter}
|
||||
if err := store.CompleteWithOptions(options); err != nil {
|
||||
panic(err) // TODO: Propagate error up
|
||||
}
|
||||
return &REST{store}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
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 auditsink
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/apiserver/pkg/storage/names"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
audit "k8s.io/kubernetes/pkg/apis/auditregistration"
|
||||
"k8s.io/kubernetes/pkg/apis/auditregistration/validation"
|
||||
)
|
||||
|
||||
// auditSinkStrategy implements verification logic for AuditSink.
|
||||
type auditSinkStrategy struct {
|
||||
runtime.ObjectTyper
|
||||
names.NameGenerator
|
||||
}
|
||||
|
||||
// Strategy is the default logic that applies when creating and updating AuditSink objects.
|
||||
var Strategy = auditSinkStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
||||
|
||||
// NamespaceScoped returns false because all AuditSink's need to be cluster scoped
|
||||
func (auditSinkStrategy) NamespaceScoped() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// PrepareForCreate clears the status of an AuditSink before creation.
|
||||
func (auditSinkStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
ic := obj.(*audit.AuditSink)
|
||||
ic.Generation = 1
|
||||
}
|
||||
|
||||
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
|
||||
func (auditSinkStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
||||
newIC := obj.(*audit.AuditSink)
|
||||
oldIC := old.(*audit.AuditSink)
|
||||
|
||||
// Any changes to the policy or backend increment the generation number
|
||||
// See metav1.ObjectMeta description for more information on Generation.
|
||||
if !reflect.DeepEqual(oldIC.Spec, newIC.Spec) {
|
||||
newIC.Generation = oldIC.Generation + 1
|
||||
}
|
||||
}
|
||||
|
||||
// Validate validates a new auditSink.
|
||||
func (auditSinkStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
||||
ic := obj.(*audit.AuditSink)
|
||||
return validation.ValidateAuditSink(ic)
|
||||
}
|
||||
|
||||
// Canonicalize normalizes the object after validation.
|
||||
func (auditSinkStrategy) Canonicalize(obj runtime.Object) {
|
||||
}
|
||||
|
||||
// AllowCreateOnUpdate is true for auditSink; this means you may create one with a PUT request.
|
||||
func (auditSinkStrategy) AllowCreateOnUpdate() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ValidateUpdate is the default update validation for an end user.
|
||||
func (auditSinkStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
||||
validationErrorList := validation.ValidateAuditSink(obj.(*audit.AuditSink))
|
||||
updateErrorList := validation.ValidateAuditSinkUpdate(obj.(*audit.AuditSink), old.(*audit.AuditSink))
|
||||
return append(validationErrorList, updateErrorList...)
|
||||
}
|
||||
|
||||
// AllowUnconditionalUpdate is the default update policy for auditSink objects. Status update should
|
||||
// only be allowed if version match.
|
||||
func (auditSinkStrategy) AllowUnconditionalUpdate() bool {
|
||||
return false
|
||||
}
|
|
@ -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 rest
|
||||
|
||||
import (
|
||||
auditv1alpha1 "k8s.io/api/auditregistration/v1alpha1"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
auditstorage "k8s.io/kubernetes/pkg/registry/auditregistration/auditsink/storage"
|
||||
)
|
||||
|
||||
// RESTStorageProvider is a REST storage provider for audit.k8s.io
|
||||
type RESTStorageProvider struct{}
|
||||
|
||||
// NewRESTStorage returns a RESTStorageProvider
|
||||
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {
|
||||
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(auditv1alpha1.GroupName, legacyscheme.Scheme, legacyscheme.ParameterCodec, legacyscheme.Codecs)
|
||||
|
||||
if apiResourceConfigSource.VersionEnabled(auditv1alpha1.SchemeGroupVersion) {
|
||||
apiGroupInfo.VersionedResourcesStorageMap[auditv1alpha1.SchemeGroupVersion.Version] = p.v1alpha1Storage(apiResourceConfigSource, restOptionsGetter)
|
||||
}
|
||||
return apiGroupInfo, true
|
||||
}
|
||||
|
||||
func (p RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
|
||||
storage := map[string]rest.Storage{}
|
||||
s := auditstorage.NewREST(restOptionsGetter)
|
||||
storage["auditsinks"] = s
|
||||
|
||||
return storage
|
||||
}
|
||||
|
||||
// GroupName is the group name for the storage provider
|
||||
func (p RESTStorageProvider) GroupName() string {
|
||||
return auditv1alpha1.GroupName
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
reviewers:
|
||||
- lavalamp
|
||||
- sttts
|
||||
- tallclair
|
||||
- pbarker
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:deepcopy-gen=package
|
||||
// +k8s:openapi-gen=true
|
||||
|
||||
// +groupName=auditregistration.k8s.io
|
||||
|
||||
package v1alpha1 // import "k8s.io/api/auditregistration/v1alpha1"
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
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 v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// GroupName is the group name use in this package
|
||||
const GroupName = "auditregistration.k8s.io"
|
||||
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"}
|
||||
|
||||
// Resource takes an unqualified resource and returns a Group qualified GroupResource
|
||||
func Resource(resource string) schema.GroupResource {
|
||||
return SchemeGroupVersion.WithResource(resource).GroupResource()
|
||||
}
|
||||
|
||||
var (
|
||||
SchemeBuilder runtime.SchemeBuilder
|
||||
localSchemeBuilder = &SchemeBuilder
|
||||
AddToScheme = localSchemeBuilder.AddToScheme
|
||||
)
|
||||
|
||||
func init() {
|
||||
// We only register manually written functions here. The registration of the
|
||||
// generated functions takes place in the generated files. The separation
|
||||
// makes the code compile even when the generated files are missing.
|
||||
localSchemeBuilder.Register(addKnownTypes)
|
||||
}
|
||||
|
||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&AuditSink{},
|
||||
&AuditSinkList{},
|
||||
)
|
||||
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// Level defines the amount of information logged during auditing
|
||||
type Level string
|
||||
|
||||
// Valid audit levels
|
||||
const (
|
||||
// LevelNone disables auditing
|
||||
LevelNone Level = "None"
|
||||
// LevelMetadata provides the basic level of auditing.
|
||||
LevelMetadata Level = "Metadata"
|
||||
// LevelRequest provides Metadata level of auditing, and additionally
|
||||
// logs the request object (does not apply for non-resource requests).
|
||||
LevelRequest Level = "Request"
|
||||
// LevelRequestResponse provides Request level of auditing, and additionally
|
||||
// logs the response object (does not apply for non-resource requests and watches).
|
||||
LevelRequestResponse Level = "RequestResponse"
|
||||
)
|
||||
|
||||
// Stage defines the stages in request handling during which audit events may be generated.
|
||||
type Stage string
|
||||
|
||||
// Valid audit stages.
|
||||
const (
|
||||
// The stage for events generated after the audit handler receives the request, but before it
|
||||
// is delegated down the handler chain.
|
||||
StageRequestReceived = "RequestReceived"
|
||||
// The stage for events generated after the response headers are sent, but before the response body
|
||||
// is sent. This stage is only generated for long-running requests (e.g. watch).
|
||||
StageResponseStarted = "ResponseStarted"
|
||||
// The stage for events generated after the response body has been completed, and no more bytes
|
||||
// will be sent.
|
||||
StageResponseComplete = "ResponseComplete"
|
||||
// The stage for events generated when a panic occurred.
|
||||
StagePanic = "Panic"
|
||||
)
|
||||
|
||||
// +genclient
|
||||
// +genclient:nonNamespaced
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// AuditSink represents a cluster level audit sink
|
||||
type AuditSink struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
|
||||
// Spec defines the audit configuration spec
|
||||
Spec AuditSinkSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
|
||||
}
|
||||
|
||||
// AuditSinkSpec holds the spec for the audit sink
|
||||
type AuditSinkSpec struct {
|
||||
// Policy defines the policy for selecting which events should be sent to the webhook
|
||||
// required
|
||||
Policy Policy `json:"policy" protobuf:"bytes,1,opt,name=policy"`
|
||||
|
||||
// Webhook to send events
|
||||
// required
|
||||
Webhook Webhook `json:"webhook" protobuf:"bytes,2,opt,name=webhook"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// AuditSinkList is a list of AuditSink items.
|
||||
type AuditSinkList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// +optional
|
||||
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
|
||||
// List of audit configurations.
|
||||
Items []AuditSink `json:"items" protobuf:"bytes,2,rep,name=items"`
|
||||
}
|
||||
|
||||
// Policy defines the configuration of how audit events are logged
|
||||
type Policy struct {
|
||||
// The Level that all requests are recorded at.
|
||||
// available options: None, Metadata, Request, RequestResponse
|
||||
// required
|
||||
Level Level `json:"level" protobuf:"bytes,1,opt,name=level"`
|
||||
|
||||
// Stages is a list of stages for which events are created.
|
||||
// +optional
|
||||
Stages []Stage `json:"stages" protobuf:"bytes,2,opt,name=stages"`
|
||||
}
|
||||
|
||||
// Webhook holds the configuration of the webhook
|
||||
type Webhook struct {
|
||||
// Throttle holds the options for throttling the webhook
|
||||
// +optional
|
||||
Throttle *WebhookThrottleConfig `json:"throttle,omitempty" protobuf:"bytes,1,opt,name=throttle"`
|
||||
|
||||
// ClientConfig holds the connection parameters for the webhook
|
||||
// required
|
||||
ClientConfig WebhookClientConfig `json:"clientConfig" protobuf:"bytes,2,opt,name=clientConfig"`
|
||||
}
|
||||
|
||||
// WebhookThrottleConfig holds the configuration for throttling events
|
||||
type WebhookThrottleConfig struct {
|
||||
// ThrottleQPS maximum number of batches per second
|
||||
// default 10 QPS
|
||||
// +optional
|
||||
QPS *int64 `json:"qps,omitempty" protobuf:"bytes,1,opt,name=qps"`
|
||||
|
||||
// ThrottleBurst is the maximum number of events sent at the same moment
|
||||
// default 15 QPS
|
||||
// +optional
|
||||
Burst *int64 `json:"burst,omitempty" protobuf:"bytes,2,opt,name=burst"`
|
||||
}
|
||||
|
||||
// WebhookClientConfig contains the information to make a connection with the webhook
|
||||
type WebhookClientConfig struct {
|
||||
// `url` gives the location of the webhook, in standard URL form
|
||||
// (`[scheme://]host:port/path`). Exactly one of `url` or `service`
|
||||
// must be specified.
|
||||
//
|
||||
// The `host` should not refer to a service running in the cluster; use
|
||||
// the `service` field instead. The host might be resolved via external
|
||||
// DNS in some apiservers (e.g., `kube-apiserver` cannot resolve
|
||||
// in-cluster DNS as that would be a layering violation). `host` may
|
||||
// also be an IP address.
|
||||
//
|
||||
// Please note that using `localhost` or `127.0.0.1` as a `host` is
|
||||
// risky unless you take great care to run this webhook on all hosts
|
||||
// which run an apiserver which might need to make calls to this
|
||||
// webhook. Such installs are likely to be non-portable, i.e., not easy
|
||||
// to turn up in a new cluster.
|
||||
//
|
||||
// The scheme must be "https"; the URL must begin with "https://".
|
||||
//
|
||||
// A path is optional, and if present may be any string permissible in
|
||||
// a URL. You may use the path to pass an arbitrary string to the
|
||||
// webhook, for example, a cluster identifier.
|
||||
//
|
||||
// Attempting to use a user or basic auth e.g. "user:password@" is not
|
||||
// allowed. Fragments ("#...") and query parameters ("?...") are not
|
||||
// allowed, either.
|
||||
//
|
||||
// +optional
|
||||
URL *string `json:"url,omitempty" protobuf:"bytes,1,opt,name=url"`
|
||||
|
||||
// `service` is a reference to the service for this webhook. Either
|
||||
// `service` or `url` must be specified.
|
||||
//
|
||||
// If the webhook is running within the cluster, then you should use `service`.
|
||||
//
|
||||
// Port 443 will be used if it is open, otherwise it is an error.
|
||||
//
|
||||
// +optional
|
||||
Service *ServiceReference `json:"service" protobuf:"bytes,2,opt,name=service"`
|
||||
|
||||
// `caBundle` is a PEM encoded CA bundle which will be used to validate
|
||||
// the webhook's server certificate.
|
||||
// defaults to the apiservers CA bundle for the endpoint type
|
||||
// +optional
|
||||
CABundle []byte `json:"caBundle" protobuf:"bytes,3,opt,name=caBundle"`
|
||||
}
|
||||
|
||||
// ServiceReference holds a reference to Service.legacy.k8s.io
|
||||
type ServiceReference struct {
|
||||
// `namespace` is the namespace of the service.
|
||||
// Required
|
||||
Namespace string `json:"namespace" protobuf:"bytes,1,opt,name=namespace"`
|
||||
|
||||
// `name` is the name of the service.
|
||||
// Required
|
||||
Name string `json:"name" protobuf:"bytes,2,opt,name=name"`
|
||||
|
||||
// `path` is an optional URL path which will be sent in any request to
|
||||
// this service.
|
||||
// +optional
|
||||
Path *string `json:"path,omitempty" protobuf:"bytes,3,opt,name=path"`
|
||||
}
|
|
@ -24,6 +24,7 @@ import (
|
|||
"k8s.io/apiserver/pkg/apis/audit"
|
||||
)
|
||||
|
||||
// ValidatePolicy validates the audit policy
|
||||
func ValidatePolicy(policy *audit.Policy) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
allErrs = append(allErrs, validateOmitStages(policy.OmitStages, field.NewPath("omitStages"))...)
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
auditregv1alpha1 "k8s.io/api/auditregistration/v1alpha1"
|
||||
batchv2alpha1 "k8s.io/api/batch/v2alpha1"
|
||||
rbacv1alpha1 "k8s.io/api/rbac/v1alpha1"
|
||||
schedulerapi "k8s.io/api/scheduling/v1beta1"
|
||||
|
@ -130,11 +131,13 @@ var missingHanlders = sets.NewString(
|
|||
"VolumeAttachment",
|
||||
"PriorityClass",
|
||||
"PodPreset",
|
||||
"AuditSink",
|
||||
)
|
||||
|
||||
func TestServerSidePrint(t *testing.T) {
|
||||
s, _, closeFn := setup(t,
|
||||
// additional groupversions needed for the test to run
|
||||
auditregv1alpha1.SchemeGroupVersion,
|
||||
batchv2alpha1.SchemeGroupVersion,
|
||||
rbacv1alpha1.SchemeGroupVersion,
|
||||
settingsv1alpha1.SchemeGroupVersion,
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/pborman/uuid"
|
||||
|
||||
apps "k8s.io/api/apps/v1beta1"
|
||||
auditreg "k8s.io/api/auditregistration/v1alpha1"
|
||||
autoscaling "k8s.io/api/autoscaling/v1"
|
||||
certificates "k8s.io/api/certificates/v1beta1"
|
||||
"k8s.io/api/core/v1"
|
||||
|
@ -290,6 +291,10 @@ func NewMasterConfig() *master.Config {
|
|||
schema.GroupResource{Group: storage.GroupName, Resource: serverstorage.AllResources},
|
||||
"",
|
||||
ns)
|
||||
storageFactory.SetSerializer(
|
||||
schema.GroupResource{Group: auditreg.GroupName, Resource: serverstorage.AllResources},
|
||||
"",
|
||||
ns)
|
||||
|
||||
genericConfig := genericapiserver.NewConfig(legacyscheme.Codecs)
|
||||
kubeVersion := version.Get()
|
||||
|
|
Loading…
Reference in New Issue