From 4cb84de44d2ac1c447f127e1cd10c59a5537b022 Mon Sep 17 00:00:00 2001 From: wanjunlei Date: Wed, 10 Jun 2020 15:59:09 +0800 Subject: [PATCH 1/6] add audit components Signed-off-by: wanjunlei debug add test add test add test --- cmd/controller-manager/app/controllers.go | 4 + hack/generate_client.sh | 2 +- pkg/apis/addtoscheme_audting_v1alpha1.go | 25 ++ pkg/apis/auditing/group.go | 18 ++ pkg/apis/auditing/v1alpha1/doc.go | 19 ++ pkg/apis/auditing/v1alpha1/register.go | 41 +++ pkg/apis/auditing/v1alpha1/rule_types.go | 89 ++++++ .../auditing/v1alpha1/v1alpha1_suite_test.go | 55 ++++ pkg/apis/auditing/v1alpha1/webhook_types.go | 155 ++++++++++ .../v1alpha1/zz_generated.deepcopy.go | 202 +++++++++++++ pkg/apiserver/apiserver.go | 5 + pkg/apiserver/auditing/backend.go | 118 ++++++++ pkg/apiserver/auditing/types.go | 235 +++++++++++++++ pkg/apiserver/auditing/types_test.go | 273 ++++++++++++++++++ pkg/apiserver/filters/auditing.go | 42 +++ pkg/client/clientset/versioned/clientset.go | 14 + .../versioned/fake/clientset_generated.go | 7 + .../clientset/versioned/fake/register.go | 2 + .../clientset/versioned/scheme/register.go | 2 + .../auditing/v1alpha1/auditing_client.go | 94 ++++++ .../versioned/typed/auditing/v1alpha1/doc.go | 20 ++ .../typed/auditing/v1alpha1/fake/doc.go | 20 ++ .../v1alpha1/fake/fake_auditing_client.go | 44 +++ .../typed/auditing/v1alpha1/fake/fake_rule.go | 120 ++++++++ .../auditing/v1alpha1/fake/fake_webhook.go | 120 ++++++++ .../auditing/v1alpha1/generated_expansion.go | 23 ++ .../versioned/typed/auditing/v1alpha1/rule.go | 164 +++++++++++ .../typed/auditing/v1alpha1/webhook.go | 164 +++++++++++ .../externalversions/auditing/interface.go | 46 +++ .../auditing/v1alpha1/interface.go | 52 ++++ .../auditing/v1alpha1/rule.go | 88 ++++++ .../auditing/v1alpha1/webhook.go | 88 ++++++ .../informers/externalversions/factory.go | 6 + .../informers/externalversions/generic.go | 13 +- .../auditing/v1alpha1/expansion_generated.go | 27 ++ pkg/client/listers/auditing/v1alpha1/rule.go | 65 +++++ .../listers/auditing/v1alpha1/webhook.go | 65 +++++ .../auditing/auditing_controller.go | 144 +++++++++ .../client/auditing/elasticsearch/options.go | 2 + 39 files changed, 2669 insertions(+), 4 deletions(-) create mode 100644 pkg/apis/addtoscheme_audting_v1alpha1.go create mode 100644 pkg/apis/auditing/group.go create mode 100644 pkg/apis/auditing/v1alpha1/doc.go create mode 100644 pkg/apis/auditing/v1alpha1/register.go create mode 100644 pkg/apis/auditing/v1alpha1/rule_types.go create mode 100644 pkg/apis/auditing/v1alpha1/v1alpha1_suite_test.go create mode 100644 pkg/apis/auditing/v1alpha1/webhook_types.go create mode 100644 pkg/apis/auditing/v1alpha1/zz_generated.deepcopy.go create mode 100644 pkg/apiserver/auditing/backend.go create mode 100644 pkg/apiserver/auditing/types.go create mode 100644 pkg/apiserver/auditing/types_test.go create mode 100644 pkg/apiserver/filters/auditing.go create mode 100644 pkg/client/clientset/versioned/typed/auditing/v1alpha1/auditing_client.go create mode 100644 pkg/client/clientset/versioned/typed/auditing/v1alpha1/doc.go create mode 100644 pkg/client/clientset/versioned/typed/auditing/v1alpha1/fake/doc.go create mode 100644 pkg/client/clientset/versioned/typed/auditing/v1alpha1/fake/fake_auditing_client.go create mode 100644 pkg/client/clientset/versioned/typed/auditing/v1alpha1/fake/fake_rule.go create mode 100644 pkg/client/clientset/versioned/typed/auditing/v1alpha1/fake/fake_webhook.go create mode 100644 pkg/client/clientset/versioned/typed/auditing/v1alpha1/generated_expansion.go create mode 100644 pkg/client/clientset/versioned/typed/auditing/v1alpha1/rule.go create mode 100644 pkg/client/clientset/versioned/typed/auditing/v1alpha1/webhook.go create mode 100644 pkg/client/informers/externalversions/auditing/interface.go create mode 100644 pkg/client/informers/externalversions/auditing/v1alpha1/interface.go create mode 100644 pkg/client/informers/externalversions/auditing/v1alpha1/rule.go create mode 100644 pkg/client/informers/externalversions/auditing/v1alpha1/webhook.go create mode 100644 pkg/client/listers/auditing/v1alpha1/expansion_generated.go create mode 100644 pkg/client/listers/auditing/v1alpha1/rule.go create mode 100644 pkg/client/listers/auditing/v1alpha1/webhook.go create mode 100644 pkg/controller/auditing/auditing_controller.go diff --git a/cmd/controller-manager/app/controllers.go b/cmd/controller-manager/app/controllers.go index 5b45cd98d..94064994c 100644 --- a/cmd/controller-manager/app/controllers.go +++ b/cmd/controller-manager/app/controllers.go @@ -20,6 +20,7 @@ import ( "fmt" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/controller/application" + "kubesphere.io/kubesphere/pkg/controller/auditing" "kubesphere.io/kubesphere/pkg/controller/certificatesigningrequest" "kubesphere.io/kubesphere/pkg/controller/cluster" "kubesphere.io/kubesphere/pkg/controller/clusterrolebinding" @@ -161,6 +162,8 @@ func AddControllers( kubesphereInformer.Tenant().V1alpha1().Workspaces(), kubernetesInformer.Core().V1().Namespaces(), nsnpProvider) + auditingController := auditing.NewController(kubesphereInformer.Auditing().V1alpha1().Webhooks()) + controllers := map[string]manager.Runnable{ "virtualservice-controller": vsController, "destinationrule-controller": drController, @@ -178,6 +181,7 @@ func AddControllers( "csr-controller": csrController, "clusterrolebinding-controller": clusterRoleBindingController, "globalrolebinding-controller": globalRoleBindingController, + "auditing-controller": auditingController, } if storageCapabilityController.IsValidKubernetesVersion() { diff --git a/hack/generate_client.sh b/hack/generate_client.sh index 6e4b753fa..cc157212c 100755 --- a/hack/generate_client.sh +++ b/hack/generate_client.sh @@ -2,7 +2,7 @@ set -e -GV="network:v1alpha1 servicemesh:v1alpha2 tenant:v1alpha1 tenant:v1alpha2 devops:v1alpha1 iam:v1alpha2 devops:v1alpha3 cluster:v1alpha1 storage:v1alpha1" +GV="network:v1alpha1 servicemesh:v1alpha2 tenant:v1alpha1 tenant:v1alpha2 devops:v1alpha1 iam:v1alpha2 devops:v1alpha3 cluster:v1alpha1 storage:v1alpha1 auditing:v1alpha1" rm -rf ./pkg/client ./hack/generate_group.sh "client,lister,informer" kubesphere.io/kubesphere/pkg/client kubesphere.io/kubesphere/pkg/apis "$GV" --output-base=./ -h "$PWD/hack/boilerplate.go.txt" diff --git a/pkg/apis/addtoscheme_audting_v1alpha1.go b/pkg/apis/addtoscheme_audting_v1alpha1.go new file mode 100644 index 000000000..16e1d6aba --- /dev/null +++ b/pkg/apis/addtoscheme_audting_v1alpha1.go @@ -0,0 +1,25 @@ +/* +Copyright 2019 The KubeSphere 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 apis + +import ( + "kubesphere.io/kubesphere/pkg/apis/auditing/v1alpha1" +) + +func init() { + AddToSchemes = append(AddToSchemes, v1alpha1.SchemeBuilder.AddToScheme) +} diff --git a/pkg/apis/auditing/group.go b/pkg/apis/auditing/group.go new file mode 100644 index 000000000..ad9d6c9eb --- /dev/null +++ b/pkg/apis/auditing/group.go @@ -0,0 +1,18 @@ +/* +Copyright 2020 The KubeSphere 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 auditing contains auditing API versions +package auditing diff --git a/pkg/apis/auditing/v1alpha1/doc.go b/pkg/apis/auditing/v1alpha1/doc.go new file mode 100644 index 000000000..a8cd89e4e --- /dev/null +++ b/pkg/apis/auditing/v1alpha1/doc.go @@ -0,0 +1,19 @@ +/* +Copyright 2020 The KubeSphere 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 contains API Schema definitions for the audit v1alpha1 API group +// +groupName=auditing.kubesphere.io +package v1alpha1 diff --git a/pkg/apis/auditing/v1alpha1/register.go b/pkg/apis/auditing/v1alpha1/register.go new file mode 100644 index 000000000..4e31bcce5 --- /dev/null +++ b/pkg/apis/auditing/v1alpha1/register.go @@ -0,0 +1,41 @@ +/* +Copyright 2020 The KubeSphere 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. +*/ + +// NOTE: Boilerplate only. Ignore this file. + +// Package v1alpha1 contains API Schema definitions for the audit v1alpha1 API group +// +k8s:deepcopy-gen=package,register +// +groupName=auditing.kubesphere.io +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: "auditing.kubesphere.io", Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} + + AddToScheme = SchemeBuilder.AddToScheme +) + +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} diff --git a/pkg/apis/auditing/v1alpha1/rule_types.go b/pkg/apis/auditing/v1alpha1/rule_types.go new file mode 100644 index 000000000..8969ff28e --- /dev/null +++ b/pkg/apis/auditing/v1alpha1/rule_types.go @@ -0,0 +1,89 @@ +/* +Copyright 2020 The KubeSphere 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" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +type PolicyRule struct { + // Rule name + Name string `json:"name,omitempty" protobuf:"bytes,8,opt,name=name"` + // Rule type, rule, macro,list,alias + Type string `json:"type,omitempty" protobuf:"bytes,8,opt,name=type"` + // Rule describe + Desc string `json:"desc,omitempty" protobuf:"bytes,8,opt,name=desc"` + // Rule condition + // This effective When the rule type is rule + Condition string `json:"condition,omitempty" protobuf:"bytes,8,opt,name=condition"` + // This effective When the rule type is macro + Macro string `json:"macro,omitempty" protobuf:"bytes,8,opt,name=macro"` + // This effective When the rule type is alias + Alias string `json:"alias,omitempty" protobuf:"bytes,8,opt,name=alias"` + // This effective When the rule type is list + List []string `json:"list,omitempty" protobuf:"bytes,8,opt,name=list"` + // Is the rule enable + Enable bool `json:"enable" protobuf:"bytes,8,opt,name=enable"` + // The output formater of message which send to user + Output string `json:"output,omitempty" protobuf:"bytes,8,opt,name=output"` + // Rule priority, DEBUG, INFO, WARNING + Priority string `json:"priority,omitempty" protobuf:"bytes,8,opt,name=priority"` +} + +// AuditRuleSpec defines the desired state of Rule +type RuleSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + PolicyRules []PolicyRule `json:"rules,omitempty" protobuf:"bytes,8,opt,name=rules"` +} + +// AuditRuleStatus defines the observed state of Rule +type RuleStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + +// +genclient +// +genclient:noStatus +// +genclient:nonNamespaced +// +kubebuilder:object:root=true + +// Rule is the Schema for the rules API +type Rule struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec RuleSpec `json:"spec,omitempty"` + Status RuleStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// AuditRuleList contains a list of Rule +type RuleList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Rule `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Rule{}, &RuleList{}) +} diff --git a/pkg/apis/auditing/v1alpha1/v1alpha1_suite_test.go b/pkg/apis/auditing/v1alpha1/v1alpha1_suite_test.go new file mode 100644 index 000000000..70d9a172a --- /dev/null +++ b/pkg/apis/auditing/v1alpha1/v1alpha1_suite_test.go @@ -0,0 +1,55 @@ +/* +Copyright 2019 The KubeSphere 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 ( + "log" + "os" + "path/filepath" + "testing" + + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" +) + +var cfg *rest.Config +var c client.Client + +func TestMain(m *testing.M) { + t := &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "config", "crds")}, + } + + err := SchemeBuilder.AddToScheme(scheme.Scheme) + if err != nil { + log.Fatal(err) + } + + if cfg, err = t.Start(); err != nil { + log.Fatal(err) + } + + if c, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}); err != nil { + log.Fatal(err) + } + + code := m.Run() + t.Stop() + os.Exit(code) +} diff --git a/pkg/apis/auditing/v1alpha1/webhook_types.go b/pkg/apis/auditing/v1alpha1/webhook_types.go new file mode 100644 index 000000000..a4ac92fb7 --- /dev/null +++ b/pkg/apis/auditing/v1alpha1/webhook_types.go @@ -0,0 +1,155 @@ +/* +Copyright 2020 The KubeSphere 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 ( + "k8s.io/api/auditregistration/v1alpha1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// Receiver config which received the audit alert +type Receiver struct { + // Receiver name + // +optional + ReceicerName string `json:"name,omitempty" protobuf:"bytes,8,opt,name=name"` + // Receiver type, alertmanager or webhook + // +optional + ReceiverType string `json:"type,omitempty" protobuf:"bytes,8,opt,name=type"` + // ClientConfig holds the connection parameters for the webhook + // +optional + ReceiverConfig v1alpha1.WebhookClientConfig `json:"config,omitempty" protobuf:"bytes,8,opt,name=config"` +} + +type AuditSinkPolicy struct { + ArchivingRuleSelector *metav1.LabelSelector `json:"archivingRuleSelector,omitempty" protobuf:"bytes,8,opt,name=archivingRuleSelector"` + AlertingRuleSelector *metav1.LabelSelector `json:"alertingRuleSelector,omitempty" protobuf:"bytes,8,opt,name=alertingRuleSelector"` +} + +type DynamicAuditConfig struct { + // Throttle holds the options for throttling the webhook + // +optional + Throttle *v1alpha1.WebhookThrottleConfig `json:"throttle,omitempty" protobuf:"bytes,18,opt,name=throttle"` + // Policy defines the policy for selecting which events should be sent to the webhook + // +optional + Policy *v1alpha1.Policy `json:"policy,omitempty" protobuf:"bytes,18,opt,name=policy"` +} + +// WebhookSpec defines the desired state of Webhook +type WebhookSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Number of desired pods. This is a pointer to distinguish between explicit + // zero and not specified. Defaults to 1. + // +optional + Replicas *int32 `json:"replicas,omitempty" protobuf:"varint,1,opt,name=replicas"` + // The webhook docker image name. + // +optional + Image string `json:"image,omitempty" protobuf:"bytes,2,opt,name=image"` + // Image pull policy. + // One of Always, Never, IfNotPresent. + // Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + // Cannot be updated. + // More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + // +optional + ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty" protobuf:"bytes,14,opt,name=imagePullPolicy,casttype=PullPolicy"` + // ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. + // If specified, these secrets will be passed to individual puller implementations for them to use. For example, + // in the case of docker, only DockerConfig type secrets are honored. + // More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod + // +optional + // +patchMergeKey=name + // +patchStrategy=merge + ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,15,rep,name=imagePullSecrets"` + // Arguments to the entrypoint.. + // It will be appended to the args and replace the default value. + // +optional + Args []string `json:"args,omitempty" protobuf:"bytes,3,rep,name=args"` + // NodeSelector is a selector which must be true for the pod to fit on a node. + // Selector which must match a node's labels for the pod to be scheduled on that node. + // More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + // +optional + NodeSelector map[string]string `json:"nodeSelector,omitempty" protobuf:"bytes,7,rep,name=nodeSelector"` + // If specified, the pod's scheduling constraints + // +optional + Affinity *corev1.Affinity `json:"affinity,omitempty" protobuf:"bytes,18,opt,name=affinity"` + // If specified, the pod's tolerations. + // +optional + Tolerations []corev1.Toleration `json:"tolerations,omitempty" protobuf:"bytes,22,opt,name=tolerations"` + // Compute Resources required by this container. + // Cannot be updated. + // More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ + // +optional + Resources *corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,8,opt,name=resources"` + // Receiver contains the information to make a connection with the alertmanager + // +optional + Receivers []Receiver `json:"receivers,omitempty" protobuf:"bytes,8,opt,name=receivers"` + + // AuditSinkPolicy is a rule selector, only the rule matched this selector will be taked effect. + // +optional + *AuditSinkPolicy `json:"auditSinkPolicy,omitempty" protobuf:"bytes,8,opt,name=auditSinkPolicy"` + // Rule priority, DEBUG < INFO < WARNING + //Audit events will be stored only when the priority of the audit rule + // matching the audit event is greater than this. + Priority string `json:"priority,omitempty" protobuf:"bytes,8,opt,name=priority"` + // Audit type, static or dynamic. + AuditType string `json:"auditType,omitempty" protobuf:"bytes,8,opt,name=auditType"` + // The Level that all requests are recorded at. + // available options: None, Metadata, Request, RequestResponse + // default: Metadata + // +optional + AuditLevel v1alpha1.Level `json:"auditLevel" protobuf:"bytes,1,opt,name=auditLevel"` + // K8s auditing is enabled or not. + K8sAuditingEnable bool `json:"k8sAuditingEnable,omitempty" protobuf:"bytes,8,opt,name=priority"` +} + +// WebhookStatus defines the observed state of Webhook +type WebhookStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + +// +genclient +// +genclient:noStatus +// +genclient:nonNamespaced +// +kubebuilder:object:root=true + +// Webhook is the Schema for the webhooks API +type Webhook struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec WebhookSpec `json:"spec,omitempty"` + Status WebhookStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// WebhookList contains a list of Webhook +type WebhookList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Webhook `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Webhook{}, &WebhookList{}) +} diff --git a/pkg/apis/auditing/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/auditing/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 000000000..2737ab625 --- /dev/null +++ b/pkg/apis/auditing/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,202 @@ +// +build !ignore_autogenerated + +/* + +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. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Rule) DeepCopyInto(out *Rule) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rule. +func (in *Rule) DeepCopy() *Rule { + if in == nil { + return nil + } + out := new(Rule) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Rule) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RuleList) DeepCopyInto(out *RuleList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Rule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuleList. +func (in *RuleList) DeepCopy() *RuleList { + if in == nil { + return nil + } + out := new(RuleList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RuleList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RuleSpec) DeepCopyInto(out *RuleSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuleSpec. +func (in *RuleSpec) DeepCopy() *RuleSpec { + if in == nil { + return nil + } + out := new(RuleSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RuleStatus) DeepCopyInto(out *RuleStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuleStatus. +func (in *RuleStatus) DeepCopy() *RuleStatus { + if in == nil { + return nil + } + out := new(RuleStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Webhook) DeepCopyInto(out *Webhook) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Webhook. +func (in *Webhook) DeepCopy() *Webhook { + if in == nil { + return nil + } + out := new(Webhook) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Webhook) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebhookList) DeepCopyInto(out *WebhookList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Webhook, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookList. +func (in *WebhookList) DeepCopy() *WebhookList { + if in == nil { + return nil + } + out := new(WebhookList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *WebhookList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebhookSpec) DeepCopyInto(out *WebhookSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookSpec. +func (in *WebhookSpec) DeepCopy() *WebhookSpec { + if in == nil { + return nil + } + out := new(WebhookSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebhookStatus) DeepCopyInto(out *WebhookStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookStatus. +func (in *WebhookStatus) DeepCopy() *WebhookStatus { + if in == nil { + return nil + } + out := new(WebhookStatus) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index c569c769b..0afd5ec74 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -27,6 +27,7 @@ import ( unionauth "k8s.io/apiserver/pkg/authentication/request/union" "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" "k8s.io/klog" + audit "kubesphere.io/kubesphere/pkg/apiserver/auditing" "kubesphere.io/kubesphere/pkg/apiserver/authentication/authenticators/basic" "kubesphere.io/kubesphere/pkg/apiserver/authentication/authenticators/jwttoken" "kubesphere.io/kubesphere/pkg/apiserver/authentication/request/anonymous" @@ -243,6 +244,10 @@ func (s *APIServer) buildHandlerChain() { handler := s.Server.Handler handler = filters.WithKubeAPIServer(handler, s.KubernetesClient.Config(), &errorResponder{}) + if s.Config.AuditingOptions.Enabled { + handler = filters.WithAuditing(handler, audit.NewAuditing(s.InformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister())) + } + if s.Config.MultiClusterOptions.Enable { clusterDispatcher := dispatch.NewClusterDispatch(s.InformerFactory.KubeSphereSharedInformerFactory().Cluster().V1alpha1().Clusters(), s.InformerFactory.KubeSphereSharedInformerFactory().Cluster().V1alpha1().Clusters().Lister()) diff --git a/pkg/apiserver/auditing/backend.go b/pkg/apiserver/auditing/backend.go new file mode 100644 index 000000000..558f6f395 --- /dev/null +++ b/pkg/apiserver/auditing/backend.go @@ -0,0 +1,118 @@ +package auditing + +import ( + "bytes" + "context" + "crypto/tls" + "encoding/json" + "k8s.io/klog" + "net/http" + "os" + "os/signal" + "time" +) + +const ( + WaitTimeout = time.Second + WebhookURL = "https://kube-auditing-webhook-svc.kubesphere-logging-system.svc:443" +) + +type Backend struct { + channelCapacity int + semCh chan interface{} + cache chan *EventList + client http.Client + sendTimeout time.Duration + waitTimeout time.Duration +} + +func NewBackend(channelCapacity int, cache chan *EventList, sendTimeout time.Duration) *Backend { + + b := Backend{ + semCh: make(chan interface{}, channelCapacity), + channelCapacity: channelCapacity, + waitTimeout: WaitTimeout, + cache: cache, + sendTimeout: sendTimeout, + } + + b.client = http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + }, + Timeout: b.sendTimeout, + } + + go b.worker() + + return &b +} + +func (b *Backend) worker() { + + // Stop when receiver signal Interrupt. + stopCh := b.SetupSignalHandler() + + for { + + var event *EventList + select { + case event = <-b.cache: + if event == nil { + break + } + case <-stopCh: + break + } + + send := func(event *EventList) { + ctx, cancel := context.WithTimeout(context.Background(), b.waitTimeout) + defer cancel() + + select { + case <-ctx.Done(): + klog.Errorf("get goroutine for audit(%s) timeout", event.Items[0].AuditID) + return + case b.semCh <- struct{}{}: + } + + defer func() { + <-b.semCh + }() + + bs, err := json.Marshal(event) + if err != nil { + klog.Errorf("json marshal error, %s", err) + return + } + + response, err := b.client.Post(WebhookURL, "application/json", bytes.NewBuffer(bs)) + if err != nil { + klog.Errorf("send audit event[%s] error, %s", event.Items[0].AuditID, err) + return + } + + if response.StatusCode != http.StatusOK { + klog.Errorf("send audit event[%s] error[%d]", event.Items[0].AuditID, response.StatusCode) + return + } + } + + go send(event) + } +} + +func (b *Backend) SetupSignalHandler() (stopCh <-chan struct{}) { + + stop := make(chan struct{}) + c := make(chan os.Signal, 2) + signal.Notify(c, []os.Signal{os.Interrupt}...) + go func() { + <-c + close(stop) + }() + + return stop +} diff --git a/pkg/apiserver/auditing/types.go b/pkg/apiserver/auditing/types.go new file mode 100644 index 000000000..374f8277c --- /dev/null +++ b/pkg/apiserver/auditing/types.go @@ -0,0 +1,235 @@ +package auditing + +import ( + "bytes" + "github.com/google/uuid" + "io/ioutil" + "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apiserver/pkg/apis/audit" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/apiserver/request" + "kubesphere.io/kubesphere/pkg/client/listers/auditing/v1alpha1" + "kubesphere.io/kubesphere/pkg/utils/iputil" + "net/http" + "time" +) + +const ( + DefaultWebhook = "kube-auditing-webhook" + DefaultCacheCapacity = 10000 + CacheTimeout = time.Second + SendTimeout = time.Second * 3 + ChannelCapacity = 10 +) + +type Auditing interface { + Enable() bool + K8sAuditingEnable() bool + LogRequestObject(req *http.Request) *Event + LogResponseObject(e *Event, resp *ResponseCapture, info *request.RequestInfo) +} + +type Event struct { + //The workspace which this audit event happened + Workspace string + //The devops project which this audit event happened + Cluster string + + audit.Event +} + +type EventList struct { + Items []Event +} + +type auditing struct { + lister v1alpha1.WebhookLister + cache chan *EventList + backend *Backend +} + +func NewAuditing(lister v1alpha1.WebhookLister) Auditing { + + a := &auditing{ + lister: lister, + cache: make(chan *EventList, DefaultCacheCapacity), + } + + a.backend = NewBackend(ChannelCapacity, a.cache, SendTimeout) + return a +} + +func (a *auditing) getAuditLevel() audit.Level { + wh, err := a.lister.Get(DefaultWebhook) + if err != nil { + klog.Error(err) + return audit.LevelNone + } + + return (audit.Level)(wh.Spec.AuditLevel) +} + +func (a *auditing) Enable() bool { + + level := a.getAuditLevel() + if level.Less(audit.LevelMetadata) { + return false + } + return true +} + +func (a *auditing) K8sAuditingEnable() bool { + wh, err := a.lister.Get(DefaultWebhook) + if err != nil { + klog.Error(err) + return false + } + + return wh.Spec.K8sAuditingEnable +} + +func (a *auditing) LogRequestObject(req *http.Request) *Event { + e := &Event{ + Event: audit.Event{ + Level: a.getAuditLevel(), + AuditID: types.UID(uuid.New().String()), + Stage: audit.StageResponseComplete, + ImpersonatedUser: nil, + UserAgent: req.UserAgent(), + RequestReceivedTimestamp: v1.NewMicroTime(time.Now()), + Annotations: nil, + }, + } + + ips := make([]string, 1) + ips[0] = iputil.RemoteIp(req) + e.SourceIPs = ips + + user, ok := request.UserFrom(req.Context()) + if ok { + e.User.Username = user.GetName() + e.User.UID = user.GetUID() + e.User.Groups = user.GetGroups() + + for k, v := range user.GetExtra() { + e.User.Extra[k] = v + } + } + + if e.Level.GreaterOrEqual(audit.LevelRequest) && req.ContentLength > 0 { + body, err := ioutil.ReadAll(req.Body) + if err != nil { + klog.Error(err) + return e + } + _ = req.Body.Close() + req.Body = ioutil.NopCloser(bytes.NewBuffer(body)) + e.RequestObject = &runtime.Unknown{Raw: body} + } + + return e +} + +func (a *auditing) LogResponseObject(e *Event, resp *ResponseCapture, info *request.RequestInfo) { + + // Auditing should igonre k8s request when k8s auditing is enabled. + if info.IsKubernetesRequest && a.K8sAuditingEnable() { + return + } + + e.StageTimestamp = v1.NewMicroTime(time.Now()) + e.ResponseStatus = &v1.Status{Code: int32(resp.StatusCode())} + if e.Level.GreaterOrEqual(audit.LevelRequestResponse) { + e.ResponseObject = &runtime.Unknown{Raw: resp.Bytes()} + } + + // If the request is not a standard request, or a resource request, + // or part of the audit information cannot be obtained through url, + // the function that handles the request can obtain RequestInfo from + // the context of the request, assign value to audit information, + // including name, verb, resource, subresource, etc like this. + // + // info, ok := request.RequestInfoFrom(request.Request.Context()) + // if ok { + // info.Verb = "post" + // info.Name = created.Name + // } + // + e.Workspace = info.Workspace + e.Cluster = info.Cluster + e.RequestURI = info.Path + e.Verb = info.Verb + e.ObjectRef = &audit.ObjectReference{ + Resource: info.Resource, + Namespace: info.Namespace, + Name: info.Name, + UID: "", + APIGroup: info.APIGroup, + APIVersion: info.APIVersion, + ResourceVersion: info.ResourceScope, + Subresource: info.Subresource, + } + + a.cacheEvent(*e) +} + +func (a *auditing) cacheEvent(e Event) { + eventList := &EventList{} + eventList.Items = append(eventList.Items, e) + select { + case a.cache <- eventList: + return + case <-time.After(CacheTimeout): + klog.Errorf("cache audit event %s timeout", e.AuditID) + break + } +} + +type ResponseCapture struct { + http.ResponseWriter + wroteHeader bool + status int + body *bytes.Buffer + StopCh chan interface{} +} + +func NewResponseCapture(w http.ResponseWriter) *ResponseCapture { + return &ResponseCapture{ + ResponseWriter: w, + wroteHeader: false, + body: new(bytes.Buffer), + StopCh: make(chan interface{}, 1), + } +} + +func (c *ResponseCapture) Header() http.Header { + return c.ResponseWriter.Header() +} + +func (c *ResponseCapture) Write(data []byte) (int, error) { + + defer func() { + c.StopCh <- struct{}{} + }() + + c.WriteHeader(http.StatusOK) + c.body.Write(data) + return c.ResponseWriter.Write(data) +} + +func (c *ResponseCapture) WriteHeader(statusCode int) { + if !c.wroteHeader { + c.status = statusCode + c.wroteHeader = true + } +} + +func (c *ResponseCapture) Bytes() []byte { + return c.body.Bytes() +} + +func (c *ResponseCapture) StatusCode() int { + return c.status +} diff --git a/pkg/apiserver/auditing/types_test.go b/pkg/apiserver/auditing/types_test.go new file mode 100644 index 000000000..e9d7ad888 --- /dev/null +++ b/pkg/apiserver/auditing/types_test.go @@ -0,0 +1,273 @@ +package auditing + +import ( + "encoding/json" + "github.com/stretchr/testify/assert" + "k8s.io/api/auditregistration/v1alpha1" + v1 "k8s.io/api/authentication/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apiserver/pkg/apis/audit" + "k8s.io/apiserver/pkg/authentication/user" + k8srequest "k8s.io/apiserver/pkg/endpoints/request" + auditingv1alpha1 "kubesphere.io/kubesphere/pkg/apis/auditing/v1alpha1" + "kubesphere.io/kubesphere/pkg/apiserver/request" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/utils/iputil" + "net/http" + "net/url" + "testing" + "time" +) + +var noResyncPeriodFunc = func() time.Duration { return 0 } + +func TestGetAuditLevel(t *testing.T) { + webhook := &auditingv1alpha1.Webhook{ + TypeMeta: metav1.TypeMeta{ + APIVersion: auditingv1alpha1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "kube-auditing-webhook", + }, + Spec: auditingv1alpha1.WebhookSpec{ + AuditLevel: v1alpha1.LevelRequestResponse, + }, + } + + informer := ksinformers.NewSharedInformerFactory(fake.NewSimpleClientset(), noResyncPeriodFunc()) + + a := auditing{ + lister: informer.Auditing().V1alpha1().Webhooks().Lister(), + } + + err := informer.Auditing().V1alpha1().Webhooks().Informer().GetIndexer().Add(webhook) + if err != nil { + panic(err) + } + + assert.Equal(t, string(webhook.Spec.AuditLevel), string(a.getAuditLevel())) +} + +func TestAuditing_Enable(t *testing.T) { + webhook := &auditingv1alpha1.Webhook{ + TypeMeta: metav1.TypeMeta{ + APIVersion: auditingv1alpha1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "kube-auditing-webhook", + }, + Spec: auditingv1alpha1.WebhookSpec{ + AuditLevel: v1alpha1.LevelNone, + }, + } + + informer := ksinformers.NewSharedInformerFactory(fake.NewSimpleClientset(), noResyncPeriodFunc()) + + a := auditing{ + lister: informer.Auditing().V1alpha1().Webhooks().Lister(), + } + + err := informer.Auditing().V1alpha1().Webhooks().Informer().GetIndexer().Add(webhook) + if err != nil { + panic(err) + } + + assert.Equal(t, false, a.Enable()) +} + +func TestAuditing_K8sAuditingEnable(t *testing.T) { + webhook := &auditingv1alpha1.Webhook{ + TypeMeta: metav1.TypeMeta{ + APIVersion: auditingv1alpha1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "kube-auditing-webhook", + }, + Spec: auditingv1alpha1.WebhookSpec{ + AuditLevel: v1alpha1.LevelNone, + K8sAuditingEnable: true, + }, + } + + informer := ksinformers.NewSharedInformerFactory(fake.NewSimpleClientset(), noResyncPeriodFunc()) + + a := auditing{ + lister: informer.Auditing().V1alpha1().Webhooks().Lister(), + } + + err := informer.Auditing().V1alpha1().Webhooks().Informer().GetIndexer().Add(webhook) + if err != nil { + panic(err) + } + + assert.Equal(t, true, a.K8sAuditingEnable()) +} + +func TestAuditing_LogRequestObject(t *testing.T) { + webhook := &auditingv1alpha1.Webhook{ + TypeMeta: metav1.TypeMeta{ + APIVersion: auditingv1alpha1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "kube-auditing-webhook", + }, + Spec: auditingv1alpha1.WebhookSpec{ + AuditLevel: v1alpha1.LevelRequestResponse, + K8sAuditingEnable: true, + }, + } + + informer := ksinformers.NewSharedInformerFactory(fake.NewSimpleClientset(), noResyncPeriodFunc()) + + a := auditing{ + lister: informer.Auditing().V1alpha1().Webhooks().Lister(), + } + + err := informer.Auditing().V1alpha1().Webhooks().Informer().GetIndexer().Add(webhook) + if err != nil { + panic(err) + } + + req := &http.Request{} + u, err := url.Parse("http://139.198.121.143:32306//kapis/tenant.kubesphere.io/v1alpha2/workspaces") + if err != nil { + panic(err) + } + + req.URL = u + req.Header = http.Header{} + req.Header.Add(iputil.XClientIP, "192.168.0.2") + req = req.WithContext(request.WithUser(req.Context(), &user.DefaultInfo{ + Name: "admin", + Groups: []string{ + "system", + }, + })) + + e := a.LogRequestObject(req) + + expectedEvent := &Event{ + Event: audit.Event{ + AuditID: e.AuditID, + Level: "RequestResponse", + Stage: "ResponseComplete", + User: v1.UserInfo{ + Username: "admin", + Groups: []string{ + "system", + }, + }, + SourceIPs: []string{ + "192.168.0.2", + }, + + RequestReceivedTimestamp: e.RequestReceivedTimestamp, + }, + } + + assert.Equal(t, expectedEvent, e) +} + +func TestAuditing_LogResponseObject(t *testing.T) { + webhook := &auditingv1alpha1.Webhook{ + TypeMeta: metav1.TypeMeta{ + APIVersion: auditingv1alpha1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "kube-auditing-webhook", + }, + Spec: auditingv1alpha1.WebhookSpec{ + AuditLevel: v1alpha1.LevelMetadata, + K8sAuditingEnable: true, + }, + } + + informer := ksinformers.NewSharedInformerFactory(fake.NewSimpleClientset(), noResyncPeriodFunc()) + + a := auditing{ + lister: informer.Auditing().V1alpha1().Webhooks().Lister(), + } + + err := informer.Auditing().V1alpha1().Webhooks().Informer().GetIndexer().Add(webhook) + if err != nil { + panic(err) + } + + req := &http.Request{} + u, err := url.Parse("http://139.198.121.143:32306//kapis/tenant.kubesphere.io/v1alpha2/workspaces") + if err != nil { + panic(err) + } + + req.URL = u + req.Header = http.Header{} + req.Header.Add(iputil.XClientIP, "192.168.0.2") + req = req.WithContext(request.WithUser(req.Context(), &user.DefaultInfo{ + Name: "admin", + Groups: []string{ + "system", + }, + })) + + e := a.LogRequestObject(req) + + info := &request.RequestInfo{ + RequestInfo: &k8srequest.RequestInfo{ + IsResourceRequest: false, + Path: "/kapis/tenant.kubesphere.io/v1alpha2/workspaces", + Verb: "create", + APIGroup: "tenant.kubesphere.io", + APIVersion: "v1alpha2", + Resource: "workspaces", + Name: "test", + }, + } + + resp := &ResponseCapture{} + resp.WriteHeader(200) + + a.LogResponseObject(e, resp, info) + + expectedEvent := &Event{ + Event: audit.Event{ + Verb: "create", + AuditID: e.AuditID, + Level: "Metadata", + Stage: "ResponseComplete", + User: v1.UserInfo{ + Username: "admin", + Groups: []string{ + "system", + }, + }, + SourceIPs: []string{ + "192.168.0.2", + }, + ObjectRef: &audit.ObjectReference{ + Resource: "workspaces", + Name: "test", + APIGroup: "tenant.kubesphere.io", + APIVersion: "v1alpha2", + }, + + RequestReceivedTimestamp: e.RequestReceivedTimestamp, + StageTimestamp: e.StageTimestamp, + RequestURI: "/kapis/tenant.kubesphere.io/v1alpha2/workspaces", + ResponseStatus: &metav1.Status{ + Code: 200, + }, + }, + } + + expectedBs, err := json.Marshal(expectedEvent) + if err != nil { + panic(err) + } + bs, err := json.Marshal(e) + if err != nil { + panic(err) + } + + assert.EqualValues(t, string(expectedBs), string(bs)) +} diff --git a/pkg/apiserver/filters/auditing.go b/pkg/apiserver/filters/auditing.go new file mode 100644 index 000000000..e57bd5f09 --- /dev/null +++ b/pkg/apiserver/filters/auditing.go @@ -0,0 +1,42 @@ +package filters + +import ( + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/apiserver/auditing" + "kubesphere.io/kubesphere/pkg/apiserver/request" + "net/http" +) + +func WithAuditing(handler http.Handler, a auditing.Auditing) http.Handler { + + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + + // When auditing level is LevelNone, request should not be auditing. + if !a.Enable() { + handler.ServeHTTP(w, req) + return + } + + e := a.LogRequestObject(req) + resp := auditing.NewResponseCapture(w) + + // Create a new goroutine to finish the request, and wait for the response body. + // The advantage of using goroutine is that recording the return value of the + // request will not affect the processing of the request, even if the auditing fails. + go handler.ServeHTTP(resp, req) + + select { + case <-req.Context().Done(): + klog.Error("Server timeout") + return + case <-resp.StopCh: + info, ok := request.RequestInfoFrom(req.Context()) + if !ok { + klog.Error("Unable to retrieve request info from request") + return + } + a.LogResponseObject(e, resp, info) + return + } + }) +} diff --git a/pkg/client/clientset/versioned/clientset.go b/pkg/client/clientset/versioned/clientset.go index 7e84ccd33..58337e535 100644 --- a/pkg/client/clientset/versioned/clientset.go +++ b/pkg/client/clientset/versioned/clientset.go @@ -24,6 +24,7 @@ import ( discovery "k8s.io/client-go/discovery" rest "k8s.io/client-go/rest" flowcontrol "k8s.io/client-go/util/flowcontrol" + auditingv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/auditing/v1alpha1" clusterv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/cluster/v1alpha1" devopsv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha1" devopsv1alpha3 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha3" @@ -37,6 +38,7 @@ import ( type Interface interface { Discovery() discovery.DiscoveryInterface + AuditingV1alpha1() auditingv1alpha1.AuditingV1alpha1Interface ClusterV1alpha1() clusterv1alpha1.ClusterV1alpha1Interface DevopsV1alpha1() devopsv1alpha1.DevopsV1alpha1Interface DevopsV1alpha3() devopsv1alpha3.DevopsV1alpha3Interface @@ -52,6 +54,7 @@ type Interface interface { // version included in a Clientset. type Clientset struct { *discovery.DiscoveryClient + auditingV1alpha1 *auditingv1alpha1.AuditingV1alpha1Client clusterV1alpha1 *clusterv1alpha1.ClusterV1alpha1Client devopsV1alpha1 *devopsv1alpha1.DevopsV1alpha1Client devopsV1alpha3 *devopsv1alpha3.DevopsV1alpha3Client @@ -63,6 +66,11 @@ type Clientset struct { tenantV1alpha2 *tenantv1alpha2.TenantV1alpha2Client } +// AuditingV1alpha1 retrieves the AuditingV1alpha1Client +func (c *Clientset) AuditingV1alpha1() auditingv1alpha1.AuditingV1alpha1Interface { + return c.auditingV1alpha1 +} + // ClusterV1alpha1 retrieves the ClusterV1alpha1Client func (c *Clientset) ClusterV1alpha1() clusterv1alpha1.ClusterV1alpha1Interface { return c.clusterV1alpha1 @@ -129,6 +137,10 @@ func NewForConfig(c *rest.Config) (*Clientset, error) { } var cs Clientset var err error + cs.auditingV1alpha1, err = auditingv1alpha1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } cs.clusterV1alpha1, err = clusterv1alpha1.NewForConfig(&configShallowCopy) if err != nil { return nil, err @@ -177,6 +189,7 @@ func NewForConfig(c *rest.Config) (*Clientset, error) { // panics if there is an error in the config. func NewForConfigOrDie(c *rest.Config) *Clientset { var cs Clientset + cs.auditingV1alpha1 = auditingv1alpha1.NewForConfigOrDie(c) cs.clusterV1alpha1 = clusterv1alpha1.NewForConfigOrDie(c) cs.devopsV1alpha1 = devopsv1alpha1.NewForConfigOrDie(c) cs.devopsV1alpha3 = devopsv1alpha3.NewForConfigOrDie(c) @@ -194,6 +207,7 @@ func NewForConfigOrDie(c *rest.Config) *Clientset { // New creates a new Clientset for the given RESTClient. func New(c rest.Interface) *Clientset { var cs Clientset + cs.auditingV1alpha1 = auditingv1alpha1.New(c) cs.clusterV1alpha1 = clusterv1alpha1.New(c) cs.devopsV1alpha1 = devopsv1alpha1.New(c) cs.devopsV1alpha3 = devopsv1alpha3.New(c) diff --git a/pkg/client/clientset/versioned/fake/clientset_generated.go b/pkg/client/clientset/versioned/fake/clientset_generated.go index 097e6231c..0331db2c5 100644 --- a/pkg/client/clientset/versioned/fake/clientset_generated.go +++ b/pkg/client/clientset/versioned/fake/clientset_generated.go @@ -25,6 +25,8 @@ import ( fakediscovery "k8s.io/client-go/discovery/fake" "k8s.io/client-go/testing" clientset "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + auditingv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/auditing/v1alpha1" + fakeauditingv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/auditing/v1alpha1/fake" clusterv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/cluster/v1alpha1" fakeclusterv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/cluster/v1alpha1/fake" devopsv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha1" @@ -92,6 +94,11 @@ func (c *Clientset) Tracker() testing.ObjectTracker { var _ clientset.Interface = &Clientset{} +// AuditingV1alpha1 retrieves the AuditingV1alpha1Client +func (c *Clientset) AuditingV1alpha1() auditingv1alpha1.AuditingV1alpha1Interface { + return &fakeauditingv1alpha1.FakeAuditingV1alpha1{Fake: &c.Fake} +} + // ClusterV1alpha1 retrieves the ClusterV1alpha1Client func (c *Clientset) ClusterV1alpha1() clusterv1alpha1.ClusterV1alpha1Interface { return &fakeclusterv1alpha1.FakeClusterV1alpha1{Fake: &c.Fake} diff --git a/pkg/client/clientset/versioned/fake/register.go b/pkg/client/clientset/versioned/fake/register.go index a13e057be..fff9dcf05 100644 --- a/pkg/client/clientset/versioned/fake/register.go +++ b/pkg/client/clientset/versioned/fake/register.go @@ -24,6 +24,7 @@ import ( schema "k8s.io/apimachinery/pkg/runtime/schema" serializer "k8s.io/apimachinery/pkg/runtime/serializer" utilruntime "k8s.io/apimachinery/pkg/util/runtime" + auditingv1alpha1 "kubesphere.io/kubesphere/pkg/apis/auditing/v1alpha1" clusterv1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1" devopsv1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" @@ -39,6 +40,7 @@ var scheme = runtime.NewScheme() var codecs = serializer.NewCodecFactory(scheme) var parameterCodec = runtime.NewParameterCodec(scheme) var localSchemeBuilder = runtime.SchemeBuilder{ + auditingv1alpha1.AddToScheme, clusterv1alpha1.AddToScheme, devopsv1alpha1.AddToScheme, devopsv1alpha3.AddToScheme, diff --git a/pkg/client/clientset/versioned/scheme/register.go b/pkg/client/clientset/versioned/scheme/register.go index 3df91bba9..2328c7a5e 100644 --- a/pkg/client/clientset/versioned/scheme/register.go +++ b/pkg/client/clientset/versioned/scheme/register.go @@ -24,6 +24,7 @@ import ( schema "k8s.io/apimachinery/pkg/runtime/schema" serializer "k8s.io/apimachinery/pkg/runtime/serializer" utilruntime "k8s.io/apimachinery/pkg/util/runtime" + auditingv1alpha1 "kubesphere.io/kubesphere/pkg/apis/auditing/v1alpha1" clusterv1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1" devopsv1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" @@ -39,6 +40,7 @@ var Scheme = runtime.NewScheme() var Codecs = serializer.NewCodecFactory(Scheme) var ParameterCodec = runtime.NewParameterCodec(Scheme) var localSchemeBuilder = runtime.SchemeBuilder{ + auditingv1alpha1.AddToScheme, clusterv1alpha1.AddToScheme, devopsv1alpha1.AddToScheme, devopsv1alpha3.AddToScheme, diff --git a/pkg/client/clientset/versioned/typed/auditing/v1alpha1/auditing_client.go b/pkg/client/clientset/versioned/typed/auditing/v1alpha1/auditing_client.go new file mode 100644 index 000000000..aaf7b7d07 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/auditing/v1alpha1/auditing_client.go @@ -0,0 +1,94 @@ +/* +Copyright 2020 The KubeSphere 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + rest "k8s.io/client-go/rest" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/auditing/v1alpha1" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +type AuditingV1alpha1Interface interface { + RESTClient() rest.Interface + RulesGetter + WebhooksGetter +} + +// AuditingV1alpha1Client is used to interact with features provided by the auditing.kubesphere.io group. +type AuditingV1alpha1Client struct { + restClient rest.Interface +} + +func (c *AuditingV1alpha1Client) Rules() RuleInterface { + return newRules(c) +} + +func (c *AuditingV1alpha1Client) Webhooks() WebhookInterface { + return newWebhooks(c) +} + +// NewForConfig creates a new AuditingV1alpha1Client for the given config. +func NewForConfig(c *rest.Config) (*AuditingV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &AuditingV1alpha1Client{client}, nil +} + +// NewForConfigOrDie creates a new AuditingV1alpha1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *AuditingV1alpha1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new AuditingV1alpha1Client for the given RESTClient. +func New(c rest.Interface) *AuditingV1alpha1Client { + return &AuditingV1alpha1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *AuditingV1alpha1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/pkg/client/clientset/versioned/typed/auditing/v1alpha1/doc.go b/pkg/client/clientset/versioned/typed/auditing/v1alpha1/doc.go new file mode 100644 index 000000000..769278743 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/auditing/v1alpha1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 The KubeSphere 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1alpha1 diff --git a/pkg/client/clientset/versioned/typed/auditing/v1alpha1/fake/doc.go b/pkg/client/clientset/versioned/typed/auditing/v1alpha1/fake/doc.go new file mode 100644 index 000000000..7e36dbca8 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/auditing/v1alpha1/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 The KubeSphere 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/pkg/client/clientset/versioned/typed/auditing/v1alpha1/fake/fake_auditing_client.go b/pkg/client/clientset/versioned/typed/auditing/v1alpha1/fake/fake_auditing_client.go new file mode 100644 index 000000000..bd239b674 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/auditing/v1alpha1/fake/fake_auditing_client.go @@ -0,0 +1,44 @@ +/* +Copyright 2020 The KubeSphere 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" + v1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/auditing/v1alpha1" +) + +type FakeAuditingV1alpha1 struct { + *testing.Fake +} + +func (c *FakeAuditingV1alpha1) Rules() v1alpha1.RuleInterface { + return &FakeRules{c} +} + +func (c *FakeAuditingV1alpha1) Webhooks() v1alpha1.WebhookInterface { + return &FakeWebhooks{c} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeAuditingV1alpha1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/pkg/client/clientset/versioned/typed/auditing/v1alpha1/fake/fake_rule.go b/pkg/client/clientset/versioned/typed/auditing/v1alpha1/fake/fake_rule.go new file mode 100644 index 000000000..2f2bfd393 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/auditing/v1alpha1/fake/fake_rule.go @@ -0,0 +1,120 @@ +/* +Copyright 2020 The KubeSphere 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/auditing/v1alpha1" +) + +// FakeRules implements RuleInterface +type FakeRules struct { + Fake *FakeAuditingV1alpha1 +} + +var rulesResource = schema.GroupVersionResource{Group: "auditing.kubesphere.io", Version: "v1alpha1", Resource: "rules"} + +var rulesKind = schema.GroupVersionKind{Group: "auditing.kubesphere.io", Version: "v1alpha1", Kind: "Rule"} + +// Get takes name of the rule, and returns the corresponding rule object, and an error if there is any. +func (c *FakeRules) Get(name string, options v1.GetOptions) (result *v1alpha1.Rule, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(rulesResource, name), &v1alpha1.Rule{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Rule), err +} + +// List takes label and field selectors, and returns the list of Rules that match those selectors. +func (c *FakeRules) List(opts v1.ListOptions) (result *v1alpha1.RuleList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(rulesResource, rulesKind, opts), &v1alpha1.RuleList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.RuleList{ListMeta: obj.(*v1alpha1.RuleList).ListMeta} + for _, item := range obj.(*v1alpha1.RuleList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested rules. +func (c *FakeRules) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(rulesResource, opts)) +} + +// Create takes the representation of a rule and creates it. Returns the server's representation of the rule, and an error, if there is any. +func (c *FakeRules) Create(rule *v1alpha1.Rule) (result *v1alpha1.Rule, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(rulesResource, rule), &v1alpha1.Rule{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Rule), err +} + +// Update takes the representation of a rule and updates it. Returns the server's representation of the rule, and an error, if there is any. +func (c *FakeRules) Update(rule *v1alpha1.Rule) (result *v1alpha1.Rule, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(rulesResource, rule), &v1alpha1.Rule{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Rule), err +} + +// Delete takes name of the rule and deletes it. Returns an error if one occurs. +func (c *FakeRules) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(rulesResource, name), &v1alpha1.Rule{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeRules) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(rulesResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.RuleList{}) + return err +} + +// Patch applies the patch and returns the patched rule. +func (c *FakeRules) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Rule, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(rulesResource, name, pt, data, subresources...), &v1alpha1.Rule{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Rule), err +} diff --git a/pkg/client/clientset/versioned/typed/auditing/v1alpha1/fake/fake_webhook.go b/pkg/client/clientset/versioned/typed/auditing/v1alpha1/fake/fake_webhook.go new file mode 100644 index 000000000..26fe8095a --- /dev/null +++ b/pkg/client/clientset/versioned/typed/auditing/v1alpha1/fake/fake_webhook.go @@ -0,0 +1,120 @@ +/* +Copyright 2020 The KubeSphere 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/auditing/v1alpha1" +) + +// FakeWebhooks implements WebhookInterface +type FakeWebhooks struct { + Fake *FakeAuditingV1alpha1 +} + +var webhooksResource = schema.GroupVersionResource{Group: "auditing.kubesphere.io", Version: "v1alpha1", Resource: "webhooks"} + +var webhooksKind = schema.GroupVersionKind{Group: "auditing.kubesphere.io", Version: "v1alpha1", Kind: "Webhook"} + +// Get takes name of the webhook, and returns the corresponding webhook object, and an error if there is any. +func (c *FakeWebhooks) Get(name string, options v1.GetOptions) (result *v1alpha1.Webhook, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(webhooksResource, name), &v1alpha1.Webhook{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Webhook), err +} + +// List takes label and field selectors, and returns the list of Webhooks that match those selectors. +func (c *FakeWebhooks) List(opts v1.ListOptions) (result *v1alpha1.WebhookList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(webhooksResource, webhooksKind, opts), &v1alpha1.WebhookList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.WebhookList{ListMeta: obj.(*v1alpha1.WebhookList).ListMeta} + for _, item := range obj.(*v1alpha1.WebhookList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested webhooks. +func (c *FakeWebhooks) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(webhooksResource, opts)) +} + +// Create takes the representation of a webhook and creates it. Returns the server's representation of the webhook, and an error, if there is any. +func (c *FakeWebhooks) Create(webhook *v1alpha1.Webhook) (result *v1alpha1.Webhook, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(webhooksResource, webhook), &v1alpha1.Webhook{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Webhook), err +} + +// Update takes the representation of a webhook and updates it. Returns the server's representation of the webhook, and an error, if there is any. +func (c *FakeWebhooks) Update(webhook *v1alpha1.Webhook) (result *v1alpha1.Webhook, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(webhooksResource, webhook), &v1alpha1.Webhook{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Webhook), err +} + +// Delete takes name of the webhook and deletes it. Returns an error if one occurs. +func (c *FakeWebhooks) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(webhooksResource, name), &v1alpha1.Webhook{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeWebhooks) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(webhooksResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.WebhookList{}) + return err +} + +// Patch applies the patch and returns the patched webhook. +func (c *FakeWebhooks) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Webhook, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(webhooksResource, name, pt, data, subresources...), &v1alpha1.Webhook{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Webhook), err +} diff --git a/pkg/client/clientset/versioned/typed/auditing/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/auditing/v1alpha1/generated_expansion.go new file mode 100644 index 000000000..d7ee8c185 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/auditing/v1alpha1/generated_expansion.go @@ -0,0 +1,23 @@ +/* +Copyright 2020 The KubeSphere 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +type RuleExpansion interface{} + +type WebhookExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/auditing/v1alpha1/rule.go b/pkg/client/clientset/versioned/typed/auditing/v1alpha1/rule.go new file mode 100644 index 000000000..e94ce50a1 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/auditing/v1alpha1/rule.go @@ -0,0 +1,164 @@ +/* +Copyright 2020 The KubeSphere 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/auditing/v1alpha1" + scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +// RulesGetter has a method to return a RuleInterface. +// A group's client should implement this interface. +type RulesGetter interface { + Rules() RuleInterface +} + +// RuleInterface has methods to work with Rule resources. +type RuleInterface interface { + Create(*v1alpha1.Rule) (*v1alpha1.Rule, error) + Update(*v1alpha1.Rule) (*v1alpha1.Rule, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.Rule, error) + List(opts v1.ListOptions) (*v1alpha1.RuleList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Rule, err error) + RuleExpansion +} + +// rules implements RuleInterface +type rules struct { + client rest.Interface +} + +// newRules returns a Rules +func newRules(c *AuditingV1alpha1Client) *rules { + return &rules{ + client: c.RESTClient(), + } +} + +// Get takes name of the rule, and returns the corresponding rule object, and an error if there is any. +func (c *rules) Get(name string, options v1.GetOptions) (result *v1alpha1.Rule, err error) { + result = &v1alpha1.Rule{} + err = c.client.Get(). + Resource("rules"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Rules that match those selectors. +func (c *rules) List(opts v1.ListOptions) (result *v1alpha1.RuleList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.RuleList{} + err = c.client.Get(). + Resource("rules"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested rules. +func (c *rules) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("rules"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a rule and creates it. Returns the server's representation of the rule, and an error, if there is any. +func (c *rules) Create(rule *v1alpha1.Rule) (result *v1alpha1.Rule, err error) { + result = &v1alpha1.Rule{} + err = c.client.Post(). + Resource("rules"). + Body(rule). + Do(). + Into(result) + return +} + +// Update takes the representation of a rule and updates it. Returns the server's representation of the rule, and an error, if there is any. +func (c *rules) Update(rule *v1alpha1.Rule) (result *v1alpha1.Rule, err error) { + result = &v1alpha1.Rule{} + err = c.client.Put(). + Resource("rules"). + Name(rule.Name). + Body(rule). + Do(). + Into(result) + return +} + +// Delete takes name of the rule and deletes it. Returns an error if one occurs. +func (c *rules) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("rules"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *rules) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("rules"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched rule. +func (c *rules) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Rule, err error) { + result = &v1alpha1.Rule{} + err = c.client.Patch(pt). + Resource("rules"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/auditing/v1alpha1/webhook.go b/pkg/client/clientset/versioned/typed/auditing/v1alpha1/webhook.go new file mode 100644 index 000000000..9fae7b692 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/auditing/v1alpha1/webhook.go @@ -0,0 +1,164 @@ +/* +Copyright 2020 The KubeSphere 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/auditing/v1alpha1" + scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +// WebhooksGetter has a method to return a WebhookInterface. +// A group's client should implement this interface. +type WebhooksGetter interface { + Webhooks() WebhookInterface +} + +// WebhookInterface has methods to work with Webhook resources. +type WebhookInterface interface { + Create(*v1alpha1.Webhook) (*v1alpha1.Webhook, error) + Update(*v1alpha1.Webhook) (*v1alpha1.Webhook, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.Webhook, error) + List(opts v1.ListOptions) (*v1alpha1.WebhookList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Webhook, err error) + WebhookExpansion +} + +// webhooks implements WebhookInterface +type webhooks struct { + client rest.Interface +} + +// newWebhooks returns a Webhooks +func newWebhooks(c *AuditingV1alpha1Client) *webhooks { + return &webhooks{ + client: c.RESTClient(), + } +} + +// Get takes name of the webhook, and returns the corresponding webhook object, and an error if there is any. +func (c *webhooks) Get(name string, options v1.GetOptions) (result *v1alpha1.Webhook, err error) { + result = &v1alpha1.Webhook{} + err = c.client.Get(). + Resource("webhooks"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Webhooks that match those selectors. +func (c *webhooks) List(opts v1.ListOptions) (result *v1alpha1.WebhookList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.WebhookList{} + err = c.client.Get(). + Resource("webhooks"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested webhooks. +func (c *webhooks) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("webhooks"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a webhook and creates it. Returns the server's representation of the webhook, and an error, if there is any. +func (c *webhooks) Create(webhook *v1alpha1.Webhook) (result *v1alpha1.Webhook, err error) { + result = &v1alpha1.Webhook{} + err = c.client.Post(). + Resource("webhooks"). + Body(webhook). + Do(). + Into(result) + return +} + +// Update takes the representation of a webhook and updates it. Returns the server's representation of the webhook, and an error, if there is any. +func (c *webhooks) Update(webhook *v1alpha1.Webhook) (result *v1alpha1.Webhook, err error) { + result = &v1alpha1.Webhook{} + err = c.client.Put(). + Resource("webhooks"). + Name(webhook.Name). + Body(webhook). + Do(). + Into(result) + return +} + +// Delete takes name of the webhook and deletes it. Returns an error if one occurs. +func (c *webhooks) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("webhooks"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *webhooks) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("webhooks"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched webhook. +func (c *webhooks) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Webhook, err error) { + result = &v1alpha1.Webhook{} + err = c.client.Patch(pt). + Resource("webhooks"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/informers/externalversions/auditing/interface.go b/pkg/client/informers/externalversions/auditing/interface.go new file mode 100644 index 000000000..79d34680f --- /dev/null +++ b/pkg/client/informers/externalversions/auditing/interface.go @@ -0,0 +1,46 @@ +/* +Copyright 2020 The KubeSphere 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package auditing + +import ( + v1alpha1 "kubesphere.io/kubesphere/pkg/client/informers/externalversions/auditing/v1alpha1" + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to each of this group's versions. +type Interface interface { + // V1alpha1 provides access to shared informers for resources in V1alpha1. + V1alpha1() v1alpha1.Interface +} + +type group struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// V1alpha1 returns a new v1alpha1.Interface. +func (g *group) V1alpha1() v1alpha1.Interface { + return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) +} diff --git a/pkg/client/informers/externalversions/auditing/v1alpha1/interface.go b/pkg/client/informers/externalversions/auditing/v1alpha1/interface.go new file mode 100644 index 000000000..b1a5f2fe9 --- /dev/null +++ b/pkg/client/informers/externalversions/auditing/v1alpha1/interface.go @@ -0,0 +1,52 @@ +/* +Copyright 2020 The KubeSphere 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // Rules returns a RuleInformer. + Rules() RuleInformer + // Webhooks returns a WebhookInformer. + Webhooks() WebhookInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// Rules returns a RuleInformer. +func (v *version) Rules() RuleInformer { + return &ruleInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} + +// Webhooks returns a WebhookInformer. +func (v *version) Webhooks() WebhookInformer { + return &webhookInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/client/informers/externalversions/auditing/v1alpha1/rule.go b/pkg/client/informers/externalversions/auditing/v1alpha1/rule.go new file mode 100644 index 000000000..487ac3361 --- /dev/null +++ b/pkg/client/informers/externalversions/auditing/v1alpha1/rule.go @@ -0,0 +1,88 @@ +/* +Copyright 2020 The KubeSphere 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" + auditingv1alpha1 "kubesphere.io/kubesphere/pkg/apis/auditing/v1alpha1" + versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "kubesphere.io/kubesphere/pkg/client/listers/auditing/v1alpha1" +) + +// RuleInformer provides access to a shared informer and lister for +// Rules. +type RuleInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.RuleLister +} + +type ruleInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewRuleInformer constructs a new informer for Rule type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewRuleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredRuleInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredRuleInformer constructs a new informer for Rule type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredRuleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AuditingV1alpha1().Rules().List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AuditingV1alpha1().Rules().Watch(options) + }, + }, + &auditingv1alpha1.Rule{}, + resyncPeriod, + indexers, + ) +} + +func (f *ruleInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredRuleInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *ruleInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&auditingv1alpha1.Rule{}, f.defaultInformer) +} + +func (f *ruleInformer) Lister() v1alpha1.RuleLister { + return v1alpha1.NewRuleLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/auditing/v1alpha1/webhook.go b/pkg/client/informers/externalversions/auditing/v1alpha1/webhook.go new file mode 100644 index 000000000..a5a5b0b2b --- /dev/null +++ b/pkg/client/informers/externalversions/auditing/v1alpha1/webhook.go @@ -0,0 +1,88 @@ +/* +Copyright 2020 The KubeSphere 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" + auditingv1alpha1 "kubesphere.io/kubesphere/pkg/apis/auditing/v1alpha1" + versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "kubesphere.io/kubesphere/pkg/client/listers/auditing/v1alpha1" +) + +// WebhookInformer provides access to a shared informer and lister for +// Webhooks. +type WebhookInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.WebhookLister +} + +type webhookInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewWebhookInformer constructs a new informer for Webhook type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewWebhookInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredWebhookInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredWebhookInformer constructs a new informer for Webhook type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredWebhookInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AuditingV1alpha1().Webhooks().List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AuditingV1alpha1().Webhooks().Watch(options) + }, + }, + &auditingv1alpha1.Webhook{}, + resyncPeriod, + indexers, + ) +} + +func (f *webhookInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredWebhookInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *webhookInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&auditingv1alpha1.Webhook{}, f.defaultInformer) +} + +func (f *webhookInformer) Lister() v1alpha1.WebhookLister { + return v1alpha1.NewWebhookLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/factory.go b/pkg/client/informers/externalversions/factory.go index 866649404..704195cb6 100644 --- a/pkg/client/informers/externalversions/factory.go +++ b/pkg/client/informers/externalversions/factory.go @@ -28,6 +28,7 @@ import ( schema "k8s.io/apimachinery/pkg/runtime/schema" cache "k8s.io/client-go/tools/cache" versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + auditing "kubesphere.io/kubesphere/pkg/client/informers/externalversions/auditing" cluster "kubesphere.io/kubesphere/pkg/client/informers/externalversions/cluster" devops "kubesphere.io/kubesphere/pkg/client/informers/externalversions/devops" iam "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam" @@ -178,6 +179,7 @@ type SharedInformerFactory interface { ForResource(resource schema.GroupVersionResource) (GenericInformer, error) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool + Auditing() auditing.Interface Cluster() cluster.Interface Devops() devops.Interface Iam() iam.Interface @@ -187,6 +189,10 @@ type SharedInformerFactory interface { Tenant() tenant.Interface } +func (f *sharedInformerFactory) Auditing() auditing.Interface { + return auditing.New(f, f.namespace, f.tweakListOptions) +} + func (f *sharedInformerFactory) Cluster() cluster.Interface { return cluster.New(f, f.namespace, f.tweakListOptions) } diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index 71d7a2028..46e13a473 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -23,7 +23,8 @@ import ( schema "k8s.io/apimachinery/pkg/runtime/schema" cache "k8s.io/client-go/tools/cache" - v1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/auditing/v1alpha1" + clusterv1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1" devopsv1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" v1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" @@ -60,8 +61,14 @@ func (f *genericInformer) Lister() cache.GenericLister { // TODO extend this to unknown resources with a client pool func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { switch resource { - // Group=cluster.kubesphere.io, Version=v1alpha1 - case v1alpha1.SchemeGroupVersion.WithResource("clusters"): + // Group=auditing.kubesphere.io, Version=v1alpha1 + case v1alpha1.SchemeGroupVersion.WithResource("rules"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Auditing().V1alpha1().Rules().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("webhooks"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Auditing().V1alpha1().Webhooks().Informer()}, nil + + // Group=cluster.kubesphere.io, Version=v1alpha1 + case clusterv1alpha1.SchemeGroupVersion.WithResource("clusters"): return &genericInformer{resource: resource.GroupResource(), informer: f.Cluster().V1alpha1().Clusters().Informer()}, nil // Group=devops.kubesphere.io, Version=v1alpha1 diff --git a/pkg/client/listers/auditing/v1alpha1/expansion_generated.go b/pkg/client/listers/auditing/v1alpha1/expansion_generated.go new file mode 100644 index 000000000..5b4b62ddf --- /dev/null +++ b/pkg/client/listers/auditing/v1alpha1/expansion_generated.go @@ -0,0 +1,27 @@ +/* +Copyright 2020 The KubeSphere 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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +// RuleListerExpansion allows custom methods to be added to +// RuleLister. +type RuleListerExpansion interface{} + +// WebhookListerExpansion allows custom methods to be added to +// WebhookLister. +type WebhookListerExpansion interface{} diff --git a/pkg/client/listers/auditing/v1alpha1/rule.go b/pkg/client/listers/auditing/v1alpha1/rule.go new file mode 100644 index 000000000..9f7dc8896 --- /dev/null +++ b/pkg/client/listers/auditing/v1alpha1/rule.go @@ -0,0 +1,65 @@ +/* +Copyright 2020 The KubeSphere 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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/auditing/v1alpha1" +) + +// RuleLister helps list Rules. +type RuleLister interface { + // List lists all Rules in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.Rule, err error) + // Get retrieves the Rule from the index for a given name. + Get(name string) (*v1alpha1.Rule, error) + RuleListerExpansion +} + +// ruleLister implements the RuleLister interface. +type ruleLister struct { + indexer cache.Indexer +} + +// NewRuleLister returns a new RuleLister. +func NewRuleLister(indexer cache.Indexer) RuleLister { + return &ruleLister{indexer: indexer} +} + +// List lists all Rules in the indexer. +func (s *ruleLister) List(selector labels.Selector) (ret []*v1alpha1.Rule, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Rule)) + }) + return ret, err +} + +// Get retrieves the Rule from the index for a given name. +func (s *ruleLister) Get(name string) (*v1alpha1.Rule, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("rule"), name) + } + return obj.(*v1alpha1.Rule), nil +} diff --git a/pkg/client/listers/auditing/v1alpha1/webhook.go b/pkg/client/listers/auditing/v1alpha1/webhook.go new file mode 100644 index 000000000..6e484586d --- /dev/null +++ b/pkg/client/listers/auditing/v1alpha1/webhook.go @@ -0,0 +1,65 @@ +/* +Copyright 2020 The KubeSphere 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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/auditing/v1alpha1" +) + +// WebhookLister helps list Webhooks. +type WebhookLister interface { + // List lists all Webhooks in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.Webhook, err error) + // Get retrieves the Webhook from the index for a given name. + Get(name string) (*v1alpha1.Webhook, error) + WebhookListerExpansion +} + +// webhookLister implements the WebhookLister interface. +type webhookLister struct { + indexer cache.Indexer +} + +// NewWebhookLister returns a new WebhookLister. +func NewWebhookLister(indexer cache.Indexer) WebhookLister { + return &webhookLister{indexer: indexer} +} + +// List lists all Webhooks in the indexer. +func (s *webhookLister) List(selector labels.Selector) (ret []*v1alpha1.Webhook, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Webhook)) + }) + return ret, err +} + +// Get retrieves the Webhook from the index for a given name. +func (s *webhookLister) Get(name string) (*v1alpha1.Webhook, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("webhook"), name) + } + return obj.(*v1alpha1.Webhook), nil +} diff --git a/pkg/controller/auditing/auditing_controller.go b/pkg/controller/auditing/auditing_controller.go new file mode 100644 index 000000000..aa0a0c1f4 --- /dev/null +++ b/pkg/controller/auditing/auditing_controller.go @@ -0,0 +1,144 @@ +/* + + Copyright 2020 The KubeSphere 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 auditing + +import ( + "fmt" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/util/workqueue" + "k8s.io/klog" + kubespherescheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" + auditinginformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/auditing/v1alpha1" + auditinglister "kubesphere.io/kubesphere/pkg/client/listers/auditing/v1alpha1" + "time" +) + +type Controller struct { + auditingLister auditinglister.WebhookLister + auditingSynced cache.InformerSynced + workQueue workqueue.RateLimitingInterface +} + +func NewController(informer auditinginformer.WebhookInformer) *Controller { + // Create auditing webhook informer + + utilruntime.Must(kubespherescheme.AddToScheme(scheme.Scheme)) + + return &Controller{ + auditingLister: informer.Lister(), + auditingSynced: informer.Informer().HasSynced, + workQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "auditing"), + } +} + +func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error { + defer utilruntime.HandleCrash() + defer c.workQueue.ShutDown() + + //init client + + // Start the informer factories to begin populating the informer caches + klog.Info("Starting User controller") + + // Wait for the caches to be synced before starting workers + klog.Info("Waiting for informer caches to sync") + if ok := cache.WaitForCacheSync(stopCh, c.auditingSynced); !ok { + return fmt.Errorf("failed to wait for caches to sync") + } + + klog.Info("Starting workers") + // Launch two workers to process Foo resources + for i := 0; i < threadiness; i++ { + go wait.Until(c.runWorker, time.Second, stopCh) + } + + klog.Info("Started workers") + <-stopCh + klog.Info("Shutting down workers") + return nil +} + +func (c *Controller) enqueueUser(obj interface{}) { + var key string + var err error + if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { + utilruntime.HandleError(err) + return + } + c.workQueue.Add(key) +} + +func (c *Controller) runWorker() { + for c.processNextWorkItem() { + } +} + +func (c *Controller) processNextWorkItem() bool { + obj, shutdown := c.workQueue.Get() + + if shutdown { + return false + } + + // We wrap this block in a func so we can defer c.workqueue.Done. + err := func(obj interface{}) error { + // We call Done here so the workqueue knows we have finished + // processing this item. We also must remember to call Forget if we + // do not want this work item being re-queued. For example, we do + // not call Forget if a transient error occurs, instead the item is + // put back on the workqueue and attempted again after a back-off + // period. + defer c.workQueue.Done(obj) + var key string + var ok bool + // We expect strings to come off the workqueue. These are of the + // form namespace/name. We do this as the delayed nature of the + // workqueue means the items in the informer cache may actually be + // more up to date that when the item was initially put onto the + // workqueue. + if key, ok = obj.(string); !ok { + // As the item in the workqueue is actually invalid, we call + // Forget here else we'd go into a loop of attempting to + // process a work item that is invalid. + c.workQueue.Forget(obj) + utilruntime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj)) + return nil + } + + // Finally, if no error occurs we Forget this item so it does not + // get queued again until another change happens. + c.workQueue.Forget(obj) + klog.Infof("Successfully synced %s:%s", "key", key) + return nil + }(obj) + + if err != nil { + utilruntime.HandleError(err) + return true + } + + return true +} + +func (c *Controller) Start(stopCh <-chan struct{}) error { + return c.Run(4, stopCh) +} diff --git a/pkg/simple/client/auditing/elasticsearch/options.go b/pkg/simple/client/auditing/elasticsearch/options.go index 7809c25a5..b1be3a714 100644 --- a/pkg/simple/client/auditing/elasticsearch/options.go +++ b/pkg/simple/client/auditing/elasticsearch/options.go @@ -22,6 +22,7 @@ import ( ) type Options struct { + Enabled bool `json:"enabled" yaml:"enabled"` Host string `json:"host" yaml:"host"` IndexPrefix string `json:"indexPrefix,omitempty" yaml:"indexPrefix"` Version string `json:"version" yaml:"version"` @@ -47,6 +48,7 @@ func (s *Options) Validate() []error { } func (s *Options) AddFlags(fs *pflag.FlagSet, c *Options) { + fs.BoolVar(&s.Enabled, "auditing-enabled", c.Enabled, "Enable auditing component or not. ") fs.StringVar(&s.Host, "auditing-elasticsearch-host", c.Host, ""+ "Elasticsearch service host. KubeSphere is using elastic as auditing store, "+ "if this filed left blank, KubeSphere will use kubernetes builtin event API instead, and"+ From 356560ac749297efc570b49352845413318dd3b4 Mon Sep 17 00:00:00 2001 From: wanjunlei Date: Thu, 11 Jun 2020 17:44:18 +0800 Subject: [PATCH 2/6] resolve conversation formater Signed-off-by: wanjunlei debug debug Signed-off-by: wanjunlei --- cmd/ks-apiserver/app/server.go | 2 +- pkg/apis/auditing/v1alpha1/webhook_types.go | 2 +- pkg/apiserver/apiserver.go | 12 ++++--- pkg/apiserver/auditing/backend.go | 34 +++++++------------ pkg/apiserver/auditing/types.go | 24 ++++++++----- pkg/apiserver/auditing/types_test.go | 20 +++++------ pkg/apiserver/filters/auditing.go | 26 +++++--------- .../client/auditing/elasticsearch/options.go | 8 +++-- 8 files changed, 61 insertions(+), 67 deletions(-) diff --git a/cmd/ks-apiserver/app/server.go b/cmd/ks-apiserver/app/server.go index 09c4fa7db..0cbc44671 100644 --- a/cmd/ks-apiserver/app/server.go +++ b/cmd/ks-apiserver/app/server.go @@ -80,7 +80,7 @@ func Run(s *options.ServerRunOptions, stopCh <-chan struct{}) error { return err } - err = apiserver.PrepareRun() + err = apiserver.PrepareRun(stopCh) if err != nil { return nil } diff --git a/pkg/apis/auditing/v1alpha1/webhook_types.go b/pkg/apis/auditing/v1alpha1/webhook_types.go index a4ac92fb7..b579b5fe9 100644 --- a/pkg/apis/auditing/v1alpha1/webhook_types.go +++ b/pkg/apis/auditing/v1alpha1/webhook_types.go @@ -118,7 +118,7 @@ type WebhookSpec struct { // +optional AuditLevel v1alpha1.Level `json:"auditLevel" protobuf:"bytes,1,opt,name=auditLevel"` // K8s auditing is enabled or not. - K8sAuditingEnable bool `json:"k8sAuditingEnable,omitempty" protobuf:"bytes,8,opt,name=priority"` + K8sAuditingEnabled bool `json:"k8sAuditingEnabled,omitempty" protobuf:"bytes,8,opt,name=priority"` } // WebhookStatus defines the observed state of Webhook diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 0afd5ec74..d3cd54b86 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -141,7 +141,7 @@ type APIServer struct { AuditingClient auditing.Client } -func (s *APIServer) PrepareRun() error { +func (s *APIServer) PrepareRun(stopCh <-chan struct{}) error { s.container = restful.NewContainer() s.container.Filter(logRequestAndResponse) @@ -158,7 +158,7 @@ func (s *APIServer) PrepareRun() error { s.Server.Handler = s.container - s.buildHandlerChain() + s.buildHandlerChain(stopCh) return nil } @@ -235,7 +235,7 @@ func (s *APIServer) Run(stopCh <-chan struct{}) (err error) { return err } -func (s *APIServer) buildHandlerChain() { +func (s *APIServer) buildHandlerChain(stopCh <-chan struct{}) { requestInfoResolver := &request.RequestInfoFactory{ APIPrefixes: sets.NewString("api", "apis", "kapis", "kapi"), GrouplessAPIPrefixes: sets.NewString("api", "kapi"), @@ -244,8 +244,10 @@ func (s *APIServer) buildHandlerChain() { handler := s.Server.Handler handler = filters.WithKubeAPIServer(handler, s.KubernetesClient.Config(), &errorResponder{}) - if s.Config.AuditingOptions.Enabled { - handler = filters.WithAuditing(handler, audit.NewAuditing(s.InformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister())) + if s.Config.AuditingOptions.Enable { + handler = filters.WithAuditing(handler, + audit.NewAuditing(s.InformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister(), + s.Config.AuditingOptions.WebhookUrl, stopCh)) } if s.Config.MultiClusterOptions.Enable { diff --git a/pkg/apiserver/auditing/backend.go b/pkg/apiserver/auditing/backend.go index 558f6f395..87287070f 100644 --- a/pkg/apiserver/auditing/backend.go +++ b/pkg/apiserver/auditing/backend.go @@ -7,33 +7,39 @@ import ( "encoding/json" "k8s.io/klog" "net/http" - "os" - "os/signal" "time" ) const ( WaitTimeout = time.Second - WebhookURL = "https://kube-auditing-webhook-svc.kubesphere-logging-system.svc:443" + WebhookURL = "https://kube-auditing-webhook-svc.kubesphere-logging-system.svc:443/audit/webhook/event" ) type Backend struct { + url string channelCapacity int semCh chan interface{} cache chan *EventList client http.Client sendTimeout time.Duration waitTimeout time.Duration + stopCh <-chan struct{} } -func NewBackend(channelCapacity int, cache chan *EventList, sendTimeout time.Duration) *Backend { +func NewBackend(url string, channelCapacity int, cache chan *EventList, sendTimeout time.Duration, stopCh <-chan struct{}) *Backend { b := Backend{ + url: url, semCh: make(chan interface{}, channelCapacity), channelCapacity: channelCapacity, waitTimeout: WaitTimeout, cache: cache, sendTimeout: sendTimeout, + stopCh: stopCh, + } + + if len(b.url) == 0 { + b.url = WebhookURL } b.client = http.Client{ @@ -52,9 +58,6 @@ func NewBackend(channelCapacity int, cache chan *EventList, sendTimeout time.Dur func (b *Backend) worker() { - // Stop when receiver signal Interrupt. - stopCh := b.SetupSignalHandler() - for { var event *EventList @@ -63,7 +66,7 @@ func (b *Backend) worker() { if event == nil { break } - case <-stopCh: + case <-b.stopCh: break } @@ -88,7 +91,7 @@ func (b *Backend) worker() { return } - response, err := b.client.Post(WebhookURL, "application/json", bytes.NewBuffer(bs)) + response, err := b.client.Post(b.url, "application/json", bytes.NewBuffer(bs)) if err != nil { klog.Errorf("send audit event[%s] error, %s", event.Items[0].AuditID, err) return @@ -103,16 +106,3 @@ func (b *Backend) worker() { go send(event) } } - -func (b *Backend) SetupSignalHandler() (stopCh <-chan struct{}) { - - stop := make(chan struct{}) - c := make(chan os.Signal, 2) - signal.Notify(c, []os.Signal{os.Interrupt}...) - go func() { - <-c - close(stop) - }() - - return stop -} diff --git a/pkg/apiserver/auditing/types.go b/pkg/apiserver/auditing/types.go index 374f8277c..7c5cecd1e 100644 --- a/pkg/apiserver/auditing/types.go +++ b/pkg/apiserver/auditing/types.go @@ -2,6 +2,7 @@ package auditing import ( "bytes" + "encoding/json" "github.com/google/uuid" "io/ioutil" "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -25,8 +26,8 @@ const ( ) type Auditing interface { - Enable() bool - K8sAuditingEnable() bool + Enabled() bool + K8sAuditingEnabled() bool LogRequestObject(req *http.Request) *Event LogResponseObject(e *Event, resp *ResponseCapture, info *request.RequestInfo) } @@ -34,7 +35,7 @@ type Auditing interface { type Event struct { //The workspace which this audit event happened Workspace string - //The devops project which this audit event happened + //The cluster which this audit event happened Cluster string audit.Event @@ -50,14 +51,14 @@ type auditing struct { backend *Backend } -func NewAuditing(lister v1alpha1.WebhookLister) Auditing { +func NewAuditing(lister v1alpha1.WebhookLister, url string, stopCh <-chan struct{}) Auditing { a := &auditing{ lister: lister, cache: make(chan *EventList, DefaultCacheCapacity), } - a.backend = NewBackend(ChannelCapacity, a.cache, SendTimeout) + a.backend = NewBackend(url, ChannelCapacity, a.cache, SendTimeout, stopCh) return a } @@ -71,7 +72,7 @@ func (a *auditing) getAuditLevel() audit.Level { return (audit.Level)(wh.Spec.AuditLevel) } -func (a *auditing) Enable() bool { +func (a *auditing) Enabled() bool { level := a.getAuditLevel() if level.Less(audit.LevelMetadata) { @@ -80,14 +81,14 @@ func (a *auditing) Enable() bool { return true } -func (a *auditing) K8sAuditingEnable() bool { +func (a *auditing) K8sAuditingEnabled() bool { wh, err := a.lister.Get(DefaultWebhook) if err != nil { klog.Error(err) return false } - return wh.Spec.K8sAuditingEnable + return wh.Spec.K8sAuditingEnabled } func (a *auditing) LogRequestObject(req *http.Request) *Event { @@ -135,7 +136,7 @@ func (a *auditing) LogRequestObject(req *http.Request) *Event { func (a *auditing) LogResponseObject(e *Event, resp *ResponseCapture, info *request.RequestInfo) { // Auditing should igonre k8s request when k8s auditing is enabled. - if info.IsKubernetesRequest && a.K8sAuditingEnable() { + if info.IsKubernetesRequest && a.K8sAuditingEnabled() { return } @@ -176,6 +177,11 @@ func (a *auditing) LogResponseObject(e *Event, resp *ResponseCapture, info *requ } func (a *auditing) cacheEvent(e Event) { + if klog.V(8) { + bs, _ := json.Marshal(e) + klog.Infof("%s", string(bs)) + } + eventList := &EventList{} eventList.Items = append(eventList.Items, e) select { diff --git a/pkg/apiserver/auditing/types_test.go b/pkg/apiserver/auditing/types_test.go index e9d7ad888..df9d02c17 100644 --- a/pkg/apiserver/auditing/types_test.go +++ b/pkg/apiserver/auditing/types_test.go @@ -49,7 +49,7 @@ func TestGetAuditLevel(t *testing.T) { assert.Equal(t, string(webhook.Spec.AuditLevel), string(a.getAuditLevel())) } -func TestAuditing_Enable(t *testing.T) { +func TestAuditing_Enabled(t *testing.T) { webhook := &auditingv1alpha1.Webhook{ TypeMeta: metav1.TypeMeta{ APIVersion: auditingv1alpha1.SchemeGroupVersion.String(), @@ -73,10 +73,10 @@ func TestAuditing_Enable(t *testing.T) { panic(err) } - assert.Equal(t, false, a.Enable()) + assert.Equal(t, false, a.Enabled()) } -func TestAuditing_K8sAuditingEnable(t *testing.T) { +func TestAuditing_K8sAuditingEnabled(t *testing.T) { webhook := &auditingv1alpha1.Webhook{ TypeMeta: metav1.TypeMeta{ APIVersion: auditingv1alpha1.SchemeGroupVersion.String(), @@ -85,8 +85,8 @@ func TestAuditing_K8sAuditingEnable(t *testing.T) { Name: "kube-auditing-webhook", }, Spec: auditingv1alpha1.WebhookSpec{ - AuditLevel: v1alpha1.LevelNone, - K8sAuditingEnable: true, + AuditLevel: v1alpha1.LevelNone, + K8sAuditingEnabled: true, }, } @@ -101,7 +101,7 @@ func TestAuditing_K8sAuditingEnable(t *testing.T) { panic(err) } - assert.Equal(t, true, a.K8sAuditingEnable()) + assert.Equal(t, true, a.K8sAuditingEnabled()) } func TestAuditing_LogRequestObject(t *testing.T) { @@ -113,8 +113,8 @@ func TestAuditing_LogRequestObject(t *testing.T) { Name: "kube-auditing-webhook", }, Spec: auditingv1alpha1.WebhookSpec{ - AuditLevel: v1alpha1.LevelRequestResponse, - K8sAuditingEnable: true, + AuditLevel: v1alpha1.LevelRequestResponse, + K8sAuditingEnabled: true, }, } @@ -178,8 +178,8 @@ func TestAuditing_LogResponseObject(t *testing.T) { Name: "kube-auditing-webhook", }, Spec: auditingv1alpha1.WebhookSpec{ - AuditLevel: v1alpha1.LevelMetadata, - K8sAuditingEnable: true, + AuditLevel: v1alpha1.LevelMetadata, + K8sAuditingEnabled: true, }, } diff --git a/pkg/apiserver/filters/auditing.go b/pkg/apiserver/filters/auditing.go index e57bd5f09..28b46ef81 100644 --- a/pkg/apiserver/filters/auditing.go +++ b/pkg/apiserver/filters/auditing.go @@ -12,31 +12,23 @@ func WithAuditing(handler http.Handler, a auditing.Auditing) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { // When auditing level is LevelNone, request should not be auditing. - if !a.Enable() { + // Auditing level can be modified with cr kube-auditing-webhook, + // so it need to judge every time. + if !a.Enabled() { handler.ServeHTTP(w, req) return } e := a.LogRequestObject(req) resp := auditing.NewResponseCapture(w) + handler.ServeHTTP(resp, req) - // Create a new goroutine to finish the request, and wait for the response body. - // The advantage of using goroutine is that recording the return value of the - // request will not affect the processing of the request, even if the auditing fails. - go handler.ServeHTTP(resp, req) - - select { - case <-req.Context().Done(): - klog.Error("Server timeout") - return - case <-resp.StopCh: - info, ok := request.RequestInfoFrom(req.Context()) - if !ok { - klog.Error("Unable to retrieve request info from request") - return - } - a.LogResponseObject(e, resp, info) + info, ok := request.RequestInfoFrom(req.Context()) + if !ok { + klog.Error("Unable to retrieve request info from request") return } + + go a.LogResponseObject(e, resp, info) }) } diff --git a/pkg/simple/client/auditing/elasticsearch/options.go b/pkg/simple/client/auditing/elasticsearch/options.go index b1be3a714..9da5ac996 100644 --- a/pkg/simple/client/auditing/elasticsearch/options.go +++ b/pkg/simple/client/auditing/elasticsearch/options.go @@ -22,7 +22,8 @@ import ( ) type Options struct { - Enabled bool `json:"enabled" yaml:"enabled"` + Enable bool `json:"enable" yaml:"enable"` + WebhookUrl string `json:"webhookUrl" yaml:"webhookUrl"` Host string `json:"host" yaml:"host"` IndexPrefix string `json:"indexPrefix,omitempty" yaml:"indexPrefix"` Version string `json:"version" yaml:"version"` @@ -48,7 +49,10 @@ func (s *Options) Validate() []error { } func (s *Options) AddFlags(fs *pflag.FlagSet, c *Options) { - fs.BoolVar(&s.Enabled, "auditing-enabled", c.Enabled, "Enable auditing component or not. ") + fs.BoolVar(&s.Enable, "auditing-enabled", c.Enable, "Enable auditing component or not. ") + + fs.StringVar(&s.WebhookUrl, "auditing-webhook-url", c.WebhookUrl, "Auditing wehook url") + fs.StringVar(&s.Host, "auditing-elasticsearch-host", c.Host, ""+ "Elasticsearch service host. KubeSphere is using elastic as auditing store, "+ "if this filed left blank, KubeSphere will use kubernetes builtin event API instead, and"+ From dd03cab01cc999c751c2604e2e495e9c97436fca Mon Sep 17 00:00:00 2001 From: wanjunlei Date: Fri, 12 Jun 2020 13:20:18 +0800 Subject: [PATCH 3/6] change log level of get crd webhook error --- pkg/apiserver/auditing/types.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/apiserver/auditing/types.go b/pkg/apiserver/auditing/types.go index 7c5cecd1e..2700c7531 100644 --- a/pkg/apiserver/auditing/types.go +++ b/pkg/apiserver/auditing/types.go @@ -65,7 +65,7 @@ func NewAuditing(lister v1alpha1.WebhookLister, url string, stopCh <-chan struct func (a *auditing) getAuditLevel() audit.Level { wh, err := a.lister.Get(DefaultWebhook) if err != nil { - klog.Error(err) + klog.V(8).Info(err) return audit.LevelNone } @@ -84,7 +84,7 @@ func (a *auditing) Enabled() bool { func (a *auditing) K8sAuditingEnabled() bool { wh, err := a.lister.Get(DefaultWebhook) if err != nil { - klog.Error(err) + klog.V(8).Info(err) return false } From 584bdda50e9c247932d1bc2dc399dfdf83ee40af Mon Sep 17 00:00:00 2001 From: wanjunlei Date: Sat, 13 Jun 2020 17:13:55 +0800 Subject: [PATCH 4/6] Add two elements Message and Devops into Event struct. Pass the event object instead of RequestInfo by request context to request handler. --- pkg/apiserver/auditing/backend.go | 9 +-- pkg/apiserver/auditing/types.go | 86 ++++++++++-------------- pkg/apiserver/auditing/types_test.go | 36 ++++++++-- pkg/apiserver/auditing/v1alpha1/event.go | 20 ++++++ pkg/apiserver/filters/auditing.go | 10 +-- pkg/apiserver/request/context.go | 8 +-- 6 files changed, 102 insertions(+), 67 deletions(-) create mode 100644 pkg/apiserver/auditing/v1alpha1/event.go diff --git a/pkg/apiserver/auditing/backend.go b/pkg/apiserver/auditing/backend.go index 87287070f..fd6a5cdb5 100644 --- a/pkg/apiserver/auditing/backend.go +++ b/pkg/apiserver/auditing/backend.go @@ -6,6 +6,7 @@ import ( "crypto/tls" "encoding/json" "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/apiserver/auditing/v1alpha1" "net/http" "time" ) @@ -19,14 +20,14 @@ type Backend struct { url string channelCapacity int semCh chan interface{} - cache chan *EventList + cache chan *v1alpha1.EventList client http.Client sendTimeout time.Duration waitTimeout time.Duration stopCh <-chan struct{} } -func NewBackend(url string, channelCapacity int, cache chan *EventList, sendTimeout time.Duration, stopCh <-chan struct{}) *Backend { +func NewBackend(url string, channelCapacity int, cache chan *v1alpha1.EventList, sendTimeout time.Duration, stopCh <-chan struct{}) *Backend { b := Backend{ url: url, @@ -60,7 +61,7 @@ func (b *Backend) worker() { for { - var event *EventList + var event *v1alpha1.EventList select { case event = <-b.cache: if event == nil { @@ -70,7 +71,7 @@ func (b *Backend) worker() { break } - send := func(event *EventList) { + send := func(event *v1alpha1.EventList) { ctx, cancel := context.WithTimeout(context.Background(), b.waitTimeout) defer cancel() diff --git a/pkg/apiserver/auditing/types.go b/pkg/apiserver/auditing/types.go index 2700c7531..775c84c82 100644 --- a/pkg/apiserver/auditing/types.go +++ b/pkg/apiserver/auditing/types.go @@ -10,6 +10,7 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apiserver/pkg/apis/audit" "k8s.io/klog" + auditv1alpha1 "kubesphere.io/kubesphere/pkg/apiserver/auditing/v1alpha1" "kubesphere.io/kubesphere/pkg/apiserver/request" "kubesphere.io/kubesphere/pkg/client/listers/auditing/v1alpha1" "kubesphere.io/kubesphere/pkg/utils/iputil" @@ -28,26 +29,13 @@ const ( type Auditing interface { Enabled() bool K8sAuditingEnabled() bool - LogRequestObject(req *http.Request) *Event - LogResponseObject(e *Event, resp *ResponseCapture, info *request.RequestInfo) -} - -type Event struct { - //The workspace which this audit event happened - Workspace string - //The cluster which this audit event happened - Cluster string - - audit.Event -} - -type EventList struct { - Items []Event + LogRequestObject(req *http.Request, info *request.RequestInfo) *auditv1alpha1.Event + LogResponseObject(e *auditv1alpha1.Event, resp *ResponseCapture, info *request.RequestInfo) } type auditing struct { lister v1alpha1.WebhookLister - cache chan *EventList + cache chan *auditv1alpha1.EventList backend *Backend } @@ -55,7 +43,7 @@ func NewAuditing(lister v1alpha1.WebhookLister, url string, stopCh <-chan struct a := &auditing{ lister: lister, - cache: make(chan *EventList, DefaultCacheCapacity), + cache: make(chan *auditv1alpha1.EventList, DefaultCacheCapacity), } a.backend = NewBackend(url, ChannelCapacity, a.cache, SendTimeout, stopCh) @@ -91,9 +79,26 @@ func (a *auditing) K8sAuditingEnabled() bool { return wh.Spec.K8sAuditingEnabled } -func (a *auditing) LogRequestObject(req *http.Request) *Event { - e := &Event{ +// If the request is not a standard request, or a resource request, +// or part of the audit information cannot be obtained through url, +// the function that handles the request can obtain Event from +// the context of the request, assign value to audit information, +// including name, verb, resource, subresource, message etc like this. +// +// info, ok := request.AuditEventFrom(request.Request.Context()) +// if ok { +// info.Verb = "post" +// info.Name = created.Name +// } +// +func (a *auditing) LogRequestObject(req *http.Request, info *request.RequestInfo) *auditv1alpha1.Event { + + e := &auditv1alpha1.Event{ + Workspace: info.Workspace, + Cluster: info.Cluster, Event: audit.Event{ + RequestURI: info.Path, + Verb: info.Verb, Level: a.getAuditLevel(), AuditID: types.UID(uuid.New().String()), Stage: audit.StageResponseComplete, @@ -101,6 +106,16 @@ func (a *auditing) LogRequestObject(req *http.Request) *Event { UserAgent: req.UserAgent(), RequestReceivedTimestamp: v1.NewMicroTime(time.Now()), Annotations: nil, + ObjectRef: &audit.ObjectReference{ + Resource: info.Resource, + Namespace: info.Namespace, + Name: info.Name, + UID: "", + APIGroup: info.APIGroup, + APIVersion: info.APIVersion, + ResourceVersion: info.ResourceScope, + Subresource: info.Subresource, + }, }, } @@ -133,7 +148,7 @@ func (a *auditing) LogRequestObject(req *http.Request) *Event { return e } -func (a *auditing) LogResponseObject(e *Event, resp *ResponseCapture, info *request.RequestInfo) { +func (a *auditing) LogResponseObject(e *auditv1alpha1.Event, resp *ResponseCapture, info *request.RequestInfo) { // Auditing should igonre k8s request when k8s auditing is enabled. if info.IsKubernetesRequest && a.K8sAuditingEnabled() { @@ -146,43 +161,16 @@ func (a *auditing) LogResponseObject(e *Event, resp *ResponseCapture, info *requ e.ResponseObject = &runtime.Unknown{Raw: resp.Bytes()} } - // If the request is not a standard request, or a resource request, - // or part of the audit information cannot be obtained through url, - // the function that handles the request can obtain RequestInfo from - // the context of the request, assign value to audit information, - // including name, verb, resource, subresource, etc like this. - // - // info, ok := request.RequestInfoFrom(request.Request.Context()) - // if ok { - // info.Verb = "post" - // info.Name = created.Name - // } - // - e.Workspace = info.Workspace - e.Cluster = info.Cluster - e.RequestURI = info.Path - e.Verb = info.Verb - e.ObjectRef = &audit.ObjectReference{ - Resource: info.Resource, - Namespace: info.Namespace, - Name: info.Name, - UID: "", - APIGroup: info.APIGroup, - APIVersion: info.APIVersion, - ResourceVersion: info.ResourceScope, - Subresource: info.Subresource, - } - a.cacheEvent(*e) } -func (a *auditing) cacheEvent(e Event) { +func (a *auditing) cacheEvent(e auditv1alpha1.Event) { if klog.V(8) { bs, _ := json.Marshal(e) klog.Infof("%s", string(bs)) } - eventList := &EventList{} + eventList := &auditv1alpha1.EventList{} eventList.Items = append(eventList.Items, e) select { case a.cache <- eventList: diff --git a/pkg/apiserver/auditing/types_test.go b/pkg/apiserver/auditing/types_test.go index df9d02c17..2e5367bbe 100644 --- a/pkg/apiserver/auditing/types_test.go +++ b/pkg/apiserver/auditing/types_test.go @@ -10,6 +10,7 @@ import ( "k8s.io/apiserver/pkg/authentication/user" k8srequest "k8s.io/apiserver/pkg/endpoints/request" auditingv1alpha1 "kubesphere.io/kubesphere/pkg/apis/auditing/v1alpha1" + v1alpha12 "kubesphere.io/kubesphere/pkg/apiserver/auditing/v1alpha1" "kubesphere.io/kubesphere/pkg/apiserver/request" "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" @@ -145,12 +146,25 @@ func TestAuditing_LogRequestObject(t *testing.T) { }, })) - e := a.LogRequestObject(req) + info := &request.RequestInfo{ + RequestInfo: &k8srequest.RequestInfo{ + IsResourceRequest: false, + Path: "/kapis/tenant.kubesphere.io/v1alpha2/workspaces", + Verb: "create", + APIGroup: "tenant.kubesphere.io", + APIVersion: "v1alpha2", + Resource: "workspaces", + Name: "test", + }, + } - expectedEvent := &Event{ + e := a.LogRequestObject(req, info) + + expectedEvent := &v1alpha12.Event{ Event: audit.Event{ AuditID: e.AuditID, Level: "RequestResponse", + Verb: "create", Stage: "ResponseComplete", User: v1.UserInfo{ Username: "admin", @@ -161,8 +175,18 @@ func TestAuditing_LogRequestObject(t *testing.T) { SourceIPs: []string{ "192.168.0.2", }, - + RequestURI: "/kapis/tenant.kubesphere.io/v1alpha2/workspaces", RequestReceivedTimestamp: e.RequestReceivedTimestamp, + ObjectRef: &audit.ObjectReference{ + Resource: "workspaces", + Namespace: "", + Name: "test", + UID: "", + APIGroup: "tenant.kubesphere.io", + APIVersion: "v1alpha2", + ResourceVersion: "", + Subresource: "", + }, }, } @@ -210,8 +234,6 @@ func TestAuditing_LogResponseObject(t *testing.T) { }, })) - e := a.LogRequestObject(req) - info := &request.RequestInfo{ RequestInfo: &k8srequest.RequestInfo{ IsResourceRequest: false, @@ -224,12 +246,14 @@ func TestAuditing_LogResponseObject(t *testing.T) { }, } + e := a.LogRequestObject(req, info) + resp := &ResponseCapture{} resp.WriteHeader(200) a.LogResponseObject(e, resp, info) - expectedEvent := &Event{ + expectedEvent := &v1alpha12.Event{ Event: audit.Event{ Verb: "create", AuditID: e.AuditID, diff --git a/pkg/apiserver/auditing/v1alpha1/event.go b/pkg/apiserver/auditing/v1alpha1/event.go new file mode 100644 index 000000000..f71fb565b --- /dev/null +++ b/pkg/apiserver/auditing/v1alpha1/event.go @@ -0,0 +1,20 @@ +package v1alpha1 + +import "k8s.io/apiserver/pkg/apis/audit" + +type Event struct { + // Devops project + Devops string + // The workspace which this audit event happened + Workspace string + // The cluster which this audit event happened + Cluster string + // Message send to user.s + Message string + + audit.Event +} + +type EventList struct { + Items []Event +} diff --git a/pkg/apiserver/filters/auditing.go b/pkg/apiserver/filters/auditing.go index 28b46ef81..17a734275 100644 --- a/pkg/apiserver/filters/auditing.go +++ b/pkg/apiserver/filters/auditing.go @@ -19,16 +19,18 @@ func WithAuditing(handler http.Handler, a auditing.Auditing) http.Handler { return } - e := a.LogRequestObject(req) - resp := auditing.NewResponseCapture(w) - handler.ServeHTTP(resp, req) - info, ok := request.RequestInfoFrom(req.Context()) if !ok { klog.Error("Unable to retrieve request info from request") + handler.ServeHTTP(w, req) return } + e := a.LogRequestObject(req, info) + req = req.WithContext(request.WithAuditEvent(req.Context(), e)) + resp := auditing.NewResponseCapture(w) + handler.ServeHTTP(resp, req) + go a.LogResponseObject(e, resp, info) }) } diff --git a/pkg/apiserver/request/context.go b/pkg/apiserver/request/context.go index d10bf1075..41154176c 100644 --- a/pkg/apiserver/request/context.go +++ b/pkg/apiserver/request/context.go @@ -18,9 +18,9 @@ package request import ( "context" + "kubesphere.io/kubesphere/pkg/apiserver/auditing/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apiserver/pkg/apis/audit" "k8s.io/apiserver/pkg/authentication/user" ) @@ -87,12 +87,12 @@ func UserFrom(ctx context.Context) (user.Info, bool) { } // WithAuditEvent returns set audit event struct. -func WithAuditEvent(parent context.Context, ev *audit.Event) context.Context { +func WithAuditEvent(parent context.Context, ev *v1alpha1.Event) context.Context { return WithValue(parent, auditKey, ev) } // AuditEventFrom returns the audit event struct on the ctx -func AuditEventFrom(ctx context.Context) *audit.Event { - ev, _ := ctx.Value(auditKey).(*audit.Event) +func AuditEventFrom(ctx context.Context) *v1alpha1.Event { + ev, _ := ctx.Value(auditKey).(*v1alpha1.Event) return ev } From b96537b27f439e0d434407ff83947497b34c70de Mon Sep 17 00:00:00 2001 From: wanjunlei Date: Sat, 13 Jun 2020 17:42:27 +0800 Subject: [PATCH 5/6] delete auditing controller --- cmd/controller-manager/app/controllers.go | 4 - .../auditing/auditing_controller.go | 144 ------------------ 2 files changed, 148 deletions(-) delete mode 100644 pkg/controller/auditing/auditing_controller.go diff --git a/cmd/controller-manager/app/controllers.go b/cmd/controller-manager/app/controllers.go index 94064994c..5b45cd98d 100644 --- a/cmd/controller-manager/app/controllers.go +++ b/cmd/controller-manager/app/controllers.go @@ -20,7 +20,6 @@ import ( "fmt" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/controller/application" - "kubesphere.io/kubesphere/pkg/controller/auditing" "kubesphere.io/kubesphere/pkg/controller/certificatesigningrequest" "kubesphere.io/kubesphere/pkg/controller/cluster" "kubesphere.io/kubesphere/pkg/controller/clusterrolebinding" @@ -162,8 +161,6 @@ func AddControllers( kubesphereInformer.Tenant().V1alpha1().Workspaces(), kubernetesInformer.Core().V1().Namespaces(), nsnpProvider) - auditingController := auditing.NewController(kubesphereInformer.Auditing().V1alpha1().Webhooks()) - controllers := map[string]manager.Runnable{ "virtualservice-controller": vsController, "destinationrule-controller": drController, @@ -181,7 +178,6 @@ func AddControllers( "csr-controller": csrController, "clusterrolebinding-controller": clusterRoleBindingController, "globalrolebinding-controller": globalRoleBindingController, - "auditing-controller": auditingController, } if storageCapabilityController.IsValidKubernetesVersion() { diff --git a/pkg/controller/auditing/auditing_controller.go b/pkg/controller/auditing/auditing_controller.go deleted file mode 100644 index aa0a0c1f4..000000000 --- a/pkg/controller/auditing/auditing_controller.go +++ /dev/null @@ -1,144 +0,0 @@ -/* - - Copyright 2020 The KubeSphere 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 auditing - -import ( - "fmt" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/util/workqueue" - "k8s.io/klog" - kubespherescheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" - auditinginformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/auditing/v1alpha1" - auditinglister "kubesphere.io/kubesphere/pkg/client/listers/auditing/v1alpha1" - "time" -) - -type Controller struct { - auditingLister auditinglister.WebhookLister - auditingSynced cache.InformerSynced - workQueue workqueue.RateLimitingInterface -} - -func NewController(informer auditinginformer.WebhookInformer) *Controller { - // Create auditing webhook informer - - utilruntime.Must(kubespherescheme.AddToScheme(scheme.Scheme)) - - return &Controller{ - auditingLister: informer.Lister(), - auditingSynced: informer.Informer().HasSynced, - workQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "auditing"), - } -} - -func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error { - defer utilruntime.HandleCrash() - defer c.workQueue.ShutDown() - - //init client - - // Start the informer factories to begin populating the informer caches - klog.Info("Starting User controller") - - // Wait for the caches to be synced before starting workers - klog.Info("Waiting for informer caches to sync") - if ok := cache.WaitForCacheSync(stopCh, c.auditingSynced); !ok { - return fmt.Errorf("failed to wait for caches to sync") - } - - klog.Info("Starting workers") - // Launch two workers to process Foo resources - for i := 0; i < threadiness; i++ { - go wait.Until(c.runWorker, time.Second, stopCh) - } - - klog.Info("Started workers") - <-stopCh - klog.Info("Shutting down workers") - return nil -} - -func (c *Controller) enqueueUser(obj interface{}) { - var key string - var err error - if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { - utilruntime.HandleError(err) - return - } - c.workQueue.Add(key) -} - -func (c *Controller) runWorker() { - for c.processNextWorkItem() { - } -} - -func (c *Controller) processNextWorkItem() bool { - obj, shutdown := c.workQueue.Get() - - if shutdown { - return false - } - - // We wrap this block in a func so we can defer c.workqueue.Done. - err := func(obj interface{}) error { - // We call Done here so the workqueue knows we have finished - // processing this item. We also must remember to call Forget if we - // do not want this work item being re-queued. For example, we do - // not call Forget if a transient error occurs, instead the item is - // put back on the workqueue and attempted again after a back-off - // period. - defer c.workQueue.Done(obj) - var key string - var ok bool - // We expect strings to come off the workqueue. These are of the - // form namespace/name. We do this as the delayed nature of the - // workqueue means the items in the informer cache may actually be - // more up to date that when the item was initially put onto the - // workqueue. - if key, ok = obj.(string); !ok { - // As the item in the workqueue is actually invalid, we call - // Forget here else we'd go into a loop of attempting to - // process a work item that is invalid. - c.workQueue.Forget(obj) - utilruntime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj)) - return nil - } - - // Finally, if no error occurs we Forget this item so it does not - // get queued again until another change happens. - c.workQueue.Forget(obj) - klog.Infof("Successfully synced %s:%s", "key", key) - return nil - }(obj) - - if err != nil { - utilruntime.HandleError(err) - return true - } - - return true -} - -func (c *Controller) Start(stopCh <-chan struct{}) error { - return c.Run(4, stopCh) -} From a3561550bbf5b7f0c6e8d0a9cdf28b1204e0d77a Mon Sep 17 00:00:00 2001 From: wanjunlei Date: Sun, 14 Jun 2020 11:22:19 +0800 Subject: [PATCH 6/6] modify annotation of Event --- pkg/apiserver/auditing/v1alpha1/event.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/apiserver/auditing/v1alpha1/event.go b/pkg/apiserver/auditing/v1alpha1/event.go index f71fb565b..ae1294ae6 100644 --- a/pkg/apiserver/auditing/v1alpha1/event.go +++ b/pkg/apiserver/auditing/v1alpha1/event.go @@ -9,7 +9,7 @@ type Event struct { Workspace string // The cluster which this audit event happened Cluster string - // Message send to user.s + // Message send to user. Message string audit.Event