From 713c646bfdd7c3ae0d1c4db07730a3eb1425d567 Mon Sep 17 00:00:00 2001 From: wanjunlei Date: Mon, 27 Jul 2020 22:53:53 +0800 Subject: [PATCH 1/5] make ws admin aware of anything happened in its workspace. Signed-off-by: wanjunlei --- pkg/apiserver/apiserver.go | 3 +- pkg/apiserver/auditing/types.go | 41 +++++++++--- pkg/apiserver/auditing/types_test.go | 43 +++++++----- pkg/models/auditing/events.go | 6 +- pkg/models/tenant/tenant.go | 67 +++++++++++++++++++ .../auditing/elasticsearch/elasticsearch.go | 19 +++++- pkg/simple/client/auditing/interface.go | 1 + 7 files changed, 150 insertions(+), 30 deletions(-) diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index dc928afc1..64c129daf 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -260,8 +260,7 @@ func (s *APIServer) buildHandlerChain(stopCh <-chan struct{}) { if s.Config.AuditingOptions.Enable { handler = filters.WithAuditing(handler, - audit.NewAuditing(s.InformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister(), - s.Config.AuditingOptions.WebhookUrl, stopCh)) + audit.NewAuditing(s.InformerFactory, s.Config.AuditingOptions.WebhookUrl, stopCh)) } var authorizers authorizer.Authorizer diff --git a/pkg/apiserver/auditing/types.go b/pkg/apiserver/auditing/types.go index d03d09800..13112f5c7 100644 --- a/pkg/apiserver/auditing/types.go +++ b/pkg/apiserver/auditing/types.go @@ -12,9 +12,13 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apiserver/pkg/apis/audit" "k8s.io/klog" + devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" auditv1alpha1 "kubesphere.io/kubesphere/pkg/apiserver/auditing/v1alpha1" + "kubesphere.io/kubesphere/pkg/apiserver/query" "kubesphere.io/kubesphere/pkg/apiserver/request" "kubesphere.io/kubesphere/pkg/client/listers/auditing/v1alpha1" + "kubesphere.io/kubesphere/pkg/informers" + resourcesv1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" "kubesphere.io/kubesphere/pkg/utils/iputil" "net" "net/http" @@ -37,16 +41,18 @@ type Auditing interface { } type auditing struct { - lister v1alpha1.WebhookLister - cache chan *auditv1alpha1.EventList - backend *Backend + lister v1alpha1.WebhookLister + resourceGetter *resourcesv1alpha3.ResourceGetter + cache chan *auditv1alpha1.EventList + backend *Backend } -func NewAuditing(lister v1alpha1.WebhookLister, url string, stopCh <-chan struct{}) Auditing { +func NewAuditing(informers informers.InformerFactory, url string, stopCh <-chan struct{}) Auditing { a := &auditing{ - lister: lister, - cache: make(chan *auditv1alpha1.EventList, DefaultCacheCapacity), + lister: informers.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister(), + resourceGetter: resourcesv1alpha3.NewResourceGetter(informers), + cache: make(chan *auditv1alpha1.EventList, DefaultCacheCapacity), } a.backend = NewBackend(url, ChannelCapacity, a.cache, SendTimeout, stopCh) @@ -115,7 +121,7 @@ func (a *auditing) LogRequestObject(req *http.Request, info *request.RequestInfo Stage: audit.StageResponseComplete, ImpersonatedUser: nil, UserAgent: req.UserAgent(), - RequestReceivedTimestamp: v1.NewMicroTime(time.Now()), + RequestReceivedTimestamp: v1.NowMicro(), Annotations: nil, ObjectRef: &audit.ObjectReference{ Resource: info.Resource, @@ -141,6 +147,25 @@ func (a *auditing) LogRequestObject(req *http.Request, info *request.RequestInfo if len(info.Parts) >= 4 { e.ObjectRef.Name = info.Parts[3] } + + // Get the workspace which the devops project be in. + if len(e.Workspace) == 0 { + res, err := a.resourceGetter.List(devopsv1alpha3.ResourcePluralDevOpsProject, "", query.New()) + if err != nil { + klog.Error(err) + } + + for _, obj := range res.Items { + d := obj.(*devopsv1alpha3.DevOpsProject) + + if d.Name == e.Devops { + e.Workspace = d.Labels["kubesphere.io/workspace"] + } else if d.Status.AdminNamespace == e.Devops { + e.Workspace = d.Labels["kubesphere.io/workspace"] + e.Devops = d.Name + } + } + } } ips := make([]string, 1) @@ -185,7 +210,7 @@ func (a *auditing) LogRequestObject(req *http.Request, info *request.RequestInfo func (a *auditing) LogResponseObject(e *auditv1alpha1.Event, resp *ResponseCapture) { - e.StageTimestamp = v1.NewMicroTime(time.Now()) + e.StageTimestamp = v1.NowMicro() e.ResponseStatus = &v1.Status{Code: int32(resp.StatusCode())} if e.Level.GreaterOrEqual(audit.LevelRequestResponse) { e.ResponseObject = &runtime.Unknown{Raw: resp.Bytes()} diff --git a/pkg/apiserver/auditing/types_test.go b/pkg/apiserver/auditing/types_test.go index ffd87f27d..9c947be7f 100644 --- a/pkg/apiserver/auditing/types_test.go +++ b/pkg/apiserver/auditing/types_test.go @@ -9,11 +9,12 @@ import ( "k8s.io/apiserver/pkg/apis/audit" "k8s.io/apiserver/pkg/authentication/user" k8srequest "k8s.io/apiserver/pkg/endpoints/request" + fakek8s "k8s.io/client-go/kubernetes/fake" 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" + "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/utils/iputil" "net/http" "net/http/httptest" @@ -37,13 +38,15 @@ func TestGetAuditLevel(t *testing.T) { }, } - informer := ksinformers.NewSharedInformerFactory(fake.NewSimpleClientset(), noResyncPeriodFunc()) + ksClient := fake.NewSimpleClientset() + k8sClient := fakek8s.NewSimpleClientset() + fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, nil, nil, nil, nil) a := auditing{ - lister: informer.Auditing().V1alpha1().Webhooks().Lister(), + lister: fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister(), } - err := informer.Auditing().V1alpha1().Webhooks().Informer().GetIndexer().Add(webhook) + err := fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Informer().GetIndexer().Add(webhook) if err != nil { panic(err) } @@ -64,13 +67,15 @@ func TestAuditing_Enabled(t *testing.T) { }, } - informer := ksinformers.NewSharedInformerFactory(fake.NewSimpleClientset(), noResyncPeriodFunc()) + ksClient := fake.NewSimpleClientset() + k8sClient := fakek8s.NewSimpleClientset() + fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, nil, nil, nil, nil) a := auditing{ - lister: informer.Auditing().V1alpha1().Webhooks().Lister(), + lister: fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister(), } - err := informer.Auditing().V1alpha1().Webhooks().Informer().GetIndexer().Add(webhook) + err := fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Informer().GetIndexer().Add(webhook) if err != nil { panic(err) } @@ -92,13 +97,15 @@ func TestAuditing_K8sAuditingEnabled(t *testing.T) { }, } - informer := ksinformers.NewSharedInformerFactory(fake.NewSimpleClientset(), noResyncPeriodFunc()) + ksClient := fake.NewSimpleClientset() + k8sClient := fakek8s.NewSimpleClientset() + fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, nil, nil, nil, nil) a := auditing{ - lister: informer.Auditing().V1alpha1().Webhooks().Lister(), + lister: fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister(), } - err := informer.Auditing().V1alpha1().Webhooks().Informer().GetIndexer().Add(webhook) + err := fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Informer().GetIndexer().Add(webhook) if err != nil { panic(err) } @@ -120,13 +127,15 @@ func TestAuditing_LogRequestObject(t *testing.T) { }, } - informer := ksinformers.NewSharedInformerFactory(fake.NewSimpleClientset(), noResyncPeriodFunc()) + ksClient := fake.NewSimpleClientset() + k8sClient := fakek8s.NewSimpleClientset() + fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, nil, nil, nil, nil) a := auditing{ - lister: informer.Auditing().V1alpha1().Webhooks().Lister(), + lister: fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister(), } - err := informer.Auditing().V1alpha1().Webhooks().Informer().GetIndexer().Add(webhook) + err := fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Informer().GetIndexer().Add(webhook) if err != nil { panic(err) } @@ -208,13 +217,15 @@ func TestAuditing_LogResponseObject(t *testing.T) { }, } - informer := ksinformers.NewSharedInformerFactory(fake.NewSimpleClientset(), noResyncPeriodFunc()) + ksClient := fake.NewSimpleClientset() + k8sClient := fakek8s.NewSimpleClientset() + fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, nil, nil, nil, nil) a := auditing{ - lister: informer.Auditing().V1alpha1().Webhooks().Lister(), + lister: fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister(), } - err := informer.Auditing().V1alpha1().Webhooks().Informer().GetIndexer().Add(webhook) + err := fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Informer().GetIndexer().Add(webhook) if err != nil { panic(err) } diff --git a/pkg/models/auditing/events.go b/pkg/models/auditing/events.go index 9e7a2ac70..36530e763 100644 --- a/pkg/models/auditing/events.go +++ b/pkg/models/auditing/events.go @@ -70,19 +70,19 @@ func (eo *eventsOperator) Events(queryParam *v1alpha1.Query, var err error switch queryParam.Operation { case "histogram": - if len(filter.ObjectRefNamespaceMap) == 0 { + if len(filter.ObjectRefNamespaceMap) == 0 && len(filter.ObjectRefWorkspaceMap) == 0 { ar.Histogram = &auditing.Histogram{} } else { ar.Histogram, err = eo.client.CountOverTime(filter, queryParam.Interval) } case "statistics": - if len(filter.ObjectRefNamespaceMap) == 0 { + if len(filter.ObjectRefNamespaceMap) == 0 && len(filter.ObjectRefWorkspaceMap) == 0 { ar.Statistics = &auditing.Statistics{} } else { ar.Statistics, err = eo.client.StatisticsOnResources(filter) } default: - if len(filter.ObjectRefNamespaceMap) == 0 { + if len(filter.ObjectRefNamespaceMap) == 0 && len(filter.ObjectRefWorkspaceMap) == 0 { ar.Events = &auditing.Events{} } else { ar.Events, err = eo.client.SearchAuditingEvent(filter, queryParam.From, queryParam.Size, queryParam.Sort) diff --git a/pkg/models/tenant/tenant.go b/pkg/models/tenant/tenant.go index b8bbb27a8..b8780dc40 100644 --- a/pkg/models/tenant/tenant.go +++ b/pkg/models/tenant/tenant.go @@ -608,6 +608,40 @@ func (t *tenantOperator) listIntersectedNamespaces(workspaces, workspaceSubstrs, return iNamespaces, nil } +// listIntersectedWorkspaces returns a list of workspaces that MUST meet ALL the following filters: +// 1. If `workspaces` is not empty, the workspace SHOULD be one of the specified workpsaces. +// 2. Else f `workspaceSubstrs` is not empty, the workspace SHOULD be contains one of the specified substrings. +// 3. Else, return all workspace in the cluster. +func (t *tenantOperator) listIntersectedWorkspaces(workspaces, workspaceSubstrs []string) ([]*tenantv1alpha1.Workspace, error) { + var ( + workspaceSet = stringSet(workspaces) + iWorkspaces []*tenantv1alpha1.Workspace + ) + + result, err := t.resourceGetter.List("workspaces", "", query.New()) + if err != nil { + return nil, err + } + for _, obj := range result.Items { + ws, ok := obj.(*tenantv1alpha1.Workspace) + if !ok { + continue + } + + if len(workspaceSet) > 0 { + if _, ok := workspaceSet[ws.Name]; !ok { + continue + } + } + if len(workspaceSubstrs) > 0 && !stringContains(ws.Name, workspaceSubstrs) { + continue + } + + iWorkspaces = append(iWorkspaces, ws) + } + return iWorkspaces, nil +} + func (t *tenantOperator) Events(user user.Info, queryParam *eventsv1alpha1.Query) (*eventsv1alpha1.APIResponse, error) { iNamespaces, err := t.listIntersectedNamespaces( stringutils.Split(queryParam.WorkspaceFilter, ","), @@ -805,7 +839,16 @@ func (t *tenantOperator) Auditing(user user.Info, queryParam *auditingv1alpha1.Q return nil, err } + iWorkspaces, err := t.listIntersectedWorkspaces( + stringutils.Split(queryParam.WorkspaceFilter, ","), + stringutils.Split(queryParam.WorkspaceSearch, ",")) + if err != nil { + klog.Error(err) + return nil, err + } + namespaceCreateTimeMap := make(map[string]time.Time) + workspaceCreateTimeMap := make(map[string]time.Time) // Now auditing and event have the same authorization mechanism, so we can determine whether the user // has permission to view the auditing log in ns by judging whether the user has the permission to view the event in ns. @@ -829,6 +872,28 @@ func (t *tenantOperator) Auditing(user user.Info, queryParam *auditingv1alpha1.Q namespaceCreateTimeMap[ns.Name] = ns.CreationTimestamp.Time } } + + for _, ws := range iWorkspaces { + listEvts := authorizer.AttributesRecord{ + User: user, + Verb: "list", + APIGroup: "", + APIVersion: "v1", + Workspace: ws.Name, + Resource: "events", + ResourceRequest: true, + ResourceScope: request.WorkspaceScope, + } + decision, _, err := t.authorizer.Authorize(listEvts) + if err != nil { + klog.Error(err) + return nil, err + } + if decision == authorizer.DecisionAllow { + workspaceCreateTimeMap[ws.Name] = ws.CreationTimestamp.Time + } + } + // If there are no ns and ws query conditions, // those events with empty `objectRef.namespace` will also be listed when user can list all events if len(queryParam.WorkspaceFilter) == 0 && len(queryParam.ObjectRefNamespaceFilter) == 0 && @@ -849,11 +914,13 @@ func (t *tenantOperator) Auditing(user user.Info, queryParam *auditingv1alpha1.Q } if decision == authorizer.DecisionAllow { namespaceCreateTimeMap[""] = time.Time{} + workspaceCreateTimeMap[""] = time.Time{} } } return t.auditing.Events(queryParam, func(filter *auditingclient.Filter) { filter.ObjectRefNamespaceMap = namespaceCreateTimeMap + filter.ObjectRefWorkspaceMap = workspaceCreateTimeMap }) } diff --git a/pkg/simple/client/auditing/elasticsearch/elasticsearch.go b/pkg/simple/client/auditing/elasticsearch/elasticsearch.go index 967d9a5e1..82a6c45dc 100644 --- a/pkg/simple/client/auditing/elasticsearch/elasticsearch.go +++ b/pkg/simple/client/auditing/elasticsearch/elasticsearch.go @@ -305,7 +305,7 @@ func parseToQueryPart(f *auditing.Filter) interface{} { "bool": &b, } - if len(f.ObjectRefNamespaceMap) > 0 { + if len(f.ObjectRefNamespaceMap) > 0 || len(f.ObjectRefWorkspaceMap) > 0 { bi := BoolBody{MinimumShouldMatch: &mini} for k, v := range f.ObjectRefNamespaceMap { bi.Should = append(bi.Should, map[string]interface{}{ @@ -322,6 +322,23 @@ func parseToQueryPart(f *auditing.Filter) interface{} { }, }) } + + for k, v := range f.ObjectRefWorkspaceMap { + bi.Should = append(bi.Should, map[string]interface{}{ + "bool": &BoolBody{ + Filter: []map[string]interface{}{{ + "match_phrase": map[string]string{"Workspace.keyword": k}, + }, { + "range": map[string]interface{}{ + "RequestReceivedTimestamp": map[string]interface{}{ + "gte": v, + }, + }, + }}, + }, + }) + } + if len(bi.Should) > 0 { b.Filter = append(b.Filter, map[string]interface{}{"bool": &bi}) } diff --git a/pkg/simple/client/auditing/interface.go b/pkg/simple/client/auditing/interface.go index 4e8ded20d..1a55a6991 100644 --- a/pkg/simple/client/auditing/interface.go +++ b/pkg/simple/client/auditing/interface.go @@ -28,6 +28,7 @@ type Client interface { type Filter struct { ObjectRefNamespaceMap map[string]time.Time + ObjectRefWorkspaceMap map[string]time.Time ObjectRefNames []string ObjectRefNameFuzzy []string Levels []string From b51a5c22f2a09366abb6cd3fe18b843e561a5538 Mon Sep 17 00:00:00 2001 From: wanjunlei Date: Tue, 28 Jul 2020 10:57:49 +0800 Subject: [PATCH 2/5] resolve conversation Signed-off-by: wanjunlei --- pkg/models/tenant/tenant.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/models/tenant/tenant.go b/pkg/models/tenant/tenant.go index b8780dc40..9608de1b1 100644 --- a/pkg/models/tenant/tenant.go +++ b/pkg/models/tenant/tenant.go @@ -610,7 +610,7 @@ func (t *tenantOperator) listIntersectedNamespaces(workspaces, workspaceSubstrs, // listIntersectedWorkspaces returns a list of workspaces that MUST meet ALL the following filters: // 1. If `workspaces` is not empty, the workspace SHOULD be one of the specified workpsaces. -// 2. Else f `workspaceSubstrs` is not empty, the workspace SHOULD be contains one of the specified substrings. +// 2. Else if `workspaceSubstrs` is not empty, the workspace SHOULD be contains one of the specified substrings. // 3. Else, return all workspace in the cluster. func (t *tenantOperator) listIntersectedWorkspaces(workspaces, workspaceSubstrs []string) ([]*tenantv1alpha1.Workspace, error) { var ( @@ -873,6 +873,8 @@ func (t *tenantOperator) Auditing(user user.Info, queryParam *auditingv1alpha1.Q } } + // Now auditing and event have the same authorization mechanism, so we can determine whether the user + // has permission to view the auditing log in ws by judging whether the user has the permission to view the event in ws. for _, ws := range iWorkspaces { listEvts := authorizer.AttributesRecord{ User: user, From b5392b97c0bcf2356ef3ec4126c1eb47d6c42111 Mon Sep 17 00:00:00 2001 From: wanjunlei Date: Tue, 28 Jul 2020 12:08:49 +0800 Subject: [PATCH 3/5] add ns and ws query parameter to es query body Signed-off-by: wanjunlei --- pkg/models/auditing/events.go | 36 ++++++++++--------- pkg/models/tenant/tenant.go | 2 +- .../auditing/elasticsearch/elasticsearch.go | 34 ++++++++++++++++-- pkg/simple/client/auditing/interface.go | 36 ++++++++++--------- 4 files changed, 73 insertions(+), 35 deletions(-) diff --git a/pkg/models/auditing/events.go b/pkg/models/auditing/events.go index 36530e763..fe21ed682 100644 --- a/pkg/models/auditing/events.go +++ b/pkg/models/auditing/events.go @@ -38,19 +38,23 @@ func NewEventsOperator(client auditing.Client) Interface { func (eo *eventsOperator) Events(queryParam *v1alpha1.Query, MutateFilterFunc func(*auditing.Filter)) (*v1alpha1.APIResponse, error) { filter := &auditing.Filter{ - ObjectRefNames: stringutils.Split(queryParam.ObjectRefNameFilter, ","), - ObjectRefNameFuzzy: stringutils.Split(queryParam.ObjectRefNameSearch, ","), - Levels: stringutils.Split(queryParam.LevelFilter, ","), - Verbs: stringutils.Split(queryParam.VerbFilter, ","), - Users: stringutils.Split(queryParam.UserFilter, ","), - UserFuzzy: stringutils.Split(queryParam.UserSearch, ","), - GroupFuzzy: stringutils.Split(queryParam.GroupSearch, ","), - SourceIpFuzzy: stringutils.Split(queryParam.SourceIpSearch, ","), - ObjectRefResources: stringutils.Split(queryParam.ObjectRefResourceFilter, ","), - ObjectRefSubresources: stringutils.Split(queryParam.ObjectRefSubresourceFilter, ","), - ResponseStatus: stringutils.Split(queryParam.ResponseStatusFilter, ","), - StartTime: queryParam.StartTime, - EndTime: queryParam.EndTime, + ObjectRefNamespaces: stringutils.Split(queryParam.ObjectRefNamespaceFilter, ","), + ObjectRefNamespaceFuzzy: stringutils.Split(queryParam.ObjectRefNamespaceSearch, ","), + Workspaces: stringutils.Split(queryParam.WorkspaceFilter, ","), + WorkspaceFuzzy: stringutils.Split(queryParam.WorkspaceSearch, ","), + ObjectRefNames: stringutils.Split(queryParam.ObjectRefNameFilter, ","), + ObjectRefNameFuzzy: stringutils.Split(queryParam.ObjectRefNameSearch, ","), + Levels: stringutils.Split(queryParam.LevelFilter, ","), + Verbs: stringutils.Split(queryParam.VerbFilter, ","), + Users: stringutils.Split(queryParam.UserFilter, ","), + UserFuzzy: stringutils.Split(queryParam.UserSearch, ","), + GroupFuzzy: stringutils.Split(queryParam.GroupSearch, ","), + SourceIpFuzzy: stringutils.Split(queryParam.SourceIpSearch, ","), + ObjectRefResources: stringutils.Split(queryParam.ObjectRefResourceFilter, ","), + ObjectRefSubresources: stringutils.Split(queryParam.ObjectRefSubresourceFilter, ","), + ResponseStatus: stringutils.Split(queryParam.ResponseStatusFilter, ","), + StartTime: queryParam.StartTime, + EndTime: queryParam.EndTime, } if MutateFilterFunc != nil { MutateFilterFunc(filter) @@ -70,19 +74,19 @@ func (eo *eventsOperator) Events(queryParam *v1alpha1.Query, var err error switch queryParam.Operation { case "histogram": - if len(filter.ObjectRefNamespaceMap) == 0 && len(filter.ObjectRefWorkspaceMap) == 0 { + if len(filter.ObjectRefNamespaceMap) == 0 && len(filter.WorkspaceMap) == 0 { ar.Histogram = &auditing.Histogram{} } else { ar.Histogram, err = eo.client.CountOverTime(filter, queryParam.Interval) } case "statistics": - if len(filter.ObjectRefNamespaceMap) == 0 && len(filter.ObjectRefWorkspaceMap) == 0 { + if len(filter.ObjectRefNamespaceMap) == 0 && len(filter.WorkspaceMap) == 0 { ar.Statistics = &auditing.Statistics{} } else { ar.Statistics, err = eo.client.StatisticsOnResources(filter) } default: - if len(filter.ObjectRefNamespaceMap) == 0 && len(filter.ObjectRefWorkspaceMap) == 0 { + if len(filter.ObjectRefNamespaceMap) == 0 && len(filter.WorkspaceMap) == 0 { ar.Events = &auditing.Events{} } else { ar.Events, err = eo.client.SearchAuditingEvent(filter, queryParam.From, queryParam.Size, queryParam.Sort) diff --git a/pkg/models/tenant/tenant.go b/pkg/models/tenant/tenant.go index 9608de1b1..c7a9fd814 100644 --- a/pkg/models/tenant/tenant.go +++ b/pkg/models/tenant/tenant.go @@ -922,7 +922,7 @@ func (t *tenantOperator) Auditing(user user.Info, queryParam *auditingv1alpha1.Q return t.auditing.Events(queryParam, func(filter *auditingclient.Filter) { filter.ObjectRefNamespaceMap = namespaceCreateTimeMap - filter.ObjectRefWorkspaceMap = workspaceCreateTimeMap + filter.WorkspaceMap = workspaceCreateTimeMap }) } diff --git a/pkg/simple/client/auditing/elasticsearch/elasticsearch.go b/pkg/simple/client/auditing/elasticsearch/elasticsearch.go index 82a6c45dc..2d39fda5a 100644 --- a/pkg/simple/client/auditing/elasticsearch/elasticsearch.go +++ b/pkg/simple/client/auditing/elasticsearch/elasticsearch.go @@ -305,7 +305,7 @@ func parseToQueryPart(f *auditing.Filter) interface{} { "bool": &b, } - if len(f.ObjectRefNamespaceMap) > 0 || len(f.ObjectRefWorkspaceMap) > 0 { + if len(f.ObjectRefNamespaceMap) > 0 || len(f.WorkspaceMap) > 0 { bi := BoolBody{MinimumShouldMatch: &mini} for k, v := range f.ObjectRefNamespaceMap { bi.Should = append(bi.Should, map[string]interface{}{ @@ -323,7 +323,7 @@ func parseToQueryPart(f *auditing.Filter) interface{} { }) } - for k, v := range f.ObjectRefWorkspaceMap { + for k, v := range f.WorkspaceMap { bi.Should = append(bi.Should, map[string]interface{}{ "bool": &BoolBody{ Filter: []map[string]interface{}{{ @@ -360,6 +360,36 @@ func parseToQueryPart(f *auditing.Filter) interface{} { return &bi } + if len(f.ObjectRefNamespaces) > 0 { + if bi := shouldBoolbody("match_phrase_prefix", "ObjectRef.Namespace.keyword", + f.ObjectRefNamespaces, nil); bi != nil { + b.Filter = append(b.Filter, map[string]interface{}{"bool": bi}) + } + } + if len(f.ObjectRefNamespaceFuzzy) > 0 { + if bi := shouldBoolbody("wildcard", "ObjectRef.Namespace", + f.ObjectRefNamespaceFuzzy, func(s string) string { + return fmt.Sprintf("*" + s + "*") + }); bi != nil { + b.Filter = append(b.Filter, map[string]interface{}{"bool": bi}) + } + } + + if len(f.Workspaces) > 0 { + if bi := shouldBoolbody("match_phrase_prefix", "Workspace.keyword", + f.Workspaces, nil); bi != nil { + b.Filter = append(b.Filter, map[string]interface{}{"bool": bi}) + } + } + if len(f.WorkspaceFuzzy) > 0 { + if bi := shouldBoolbody("wildcard", "Workspace", + f.WorkspaceFuzzy, func(s string) string { + return fmt.Sprintf("*" + s + "*") + }); bi != nil { + b.Filter = append(b.Filter, map[string]interface{}{"bool": bi}) + } + } + if len(f.ObjectRefNames) > 0 { if bi := shouldBoolbody("match_phrase_prefix", "ObjectRef.Name.keyword", f.ObjectRefNames, nil); bi != nil { diff --git a/pkg/simple/client/auditing/interface.go b/pkg/simple/client/auditing/interface.go index 1a55a6991..48e1907f2 100644 --- a/pkg/simple/client/auditing/interface.go +++ b/pkg/simple/client/auditing/interface.go @@ -27,22 +27,26 @@ type Client interface { } type Filter struct { - ObjectRefNamespaceMap map[string]time.Time - ObjectRefWorkspaceMap map[string]time.Time - ObjectRefNames []string - ObjectRefNameFuzzy []string - Levels []string - Verbs []string - Users []string - UserFuzzy []string - GroupFuzzy []string - SourceIpFuzzy []string - ObjectRefResources []string - ObjectRefSubresources []string - ResponseCodes []int32 - ResponseStatus []string - StartTime *time.Time - EndTime *time.Time + ObjectRefNamespaceMap map[string]time.Time + WorkspaceMap map[string]time.Time + ObjectRefNamespaces []string + ObjectRefNamespaceFuzzy []string + Workspaces []string + WorkspaceFuzzy []string + ObjectRefNames []string + ObjectRefNameFuzzy []string + Levels []string + Verbs []string + Users []string + UserFuzzy []string + GroupFuzzy []string + SourceIpFuzzy []string + ObjectRefResources []string + ObjectRefSubresources []string + ResponseCodes []int32 + ResponseStatus []string + StartTime *time.Time + EndTime *time.Time } type Event map[string]interface{} From 9306b6abf76a886422d0883f24f72c26626e37ec Mon Sep 17 00:00:00 2001 From: wanjunlei Date: Tue, 28 Jul 2020 16:24:51 +0800 Subject: [PATCH 4/5] change match_phrase_prefix to match_phrase Signed-off-by: wanjunlei --- pkg/simple/client/auditing/elasticsearch/elasticsearch.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/simple/client/auditing/elasticsearch/elasticsearch.go b/pkg/simple/client/auditing/elasticsearch/elasticsearch.go index 2d39fda5a..a0c76af7b 100644 --- a/pkg/simple/client/auditing/elasticsearch/elasticsearch.go +++ b/pkg/simple/client/auditing/elasticsearch/elasticsearch.go @@ -361,7 +361,7 @@ func parseToQueryPart(f *auditing.Filter) interface{} { } if len(f.ObjectRefNamespaces) > 0 { - if bi := shouldBoolbody("match_phrase_prefix", "ObjectRef.Namespace.keyword", + if bi := shouldBoolbody("match_phrase", "ObjectRef.Namespace.keyword", f.ObjectRefNamespaces, nil); bi != nil { b.Filter = append(b.Filter, map[string]interface{}{"bool": bi}) } @@ -376,7 +376,7 @@ func parseToQueryPart(f *auditing.Filter) interface{} { } if len(f.Workspaces) > 0 { - if bi := shouldBoolbody("match_phrase_prefix", "Workspace.keyword", + if bi := shouldBoolbody("match_phrase", "Workspace.keyword", f.Workspaces, nil); bi != nil { b.Filter = append(b.Filter, map[string]interface{}{"bool": bi}) } From 6f90e24b83a3ba1010759a327fbf14044e14e7df Mon Sep 17 00:00:00 2001 From: wanjunlei Date: Wed, 29 Jul 2020 19:31:20 +0800 Subject: [PATCH 5/5] change resourceGetter to devopsGetter Signed-off-by: wanjunlei --- pkg/apiserver/auditing/types.go | 59 +++++++++++----------------- pkg/apiserver/auditing/types_test.go | 10 ++--- 2 files changed, 29 insertions(+), 40 deletions(-) diff --git a/pkg/apiserver/auditing/types.go b/pkg/apiserver/auditing/types.go index 13112f5c7..f69d519e6 100644 --- a/pkg/apiserver/auditing/types.go +++ b/pkg/apiserver/auditing/types.go @@ -18,7 +18,8 @@ import ( "kubesphere.io/kubesphere/pkg/apiserver/request" "kubesphere.io/kubesphere/pkg/client/listers/auditing/v1alpha1" "kubesphere.io/kubesphere/pkg/informers" - resourcesv1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/devops" "kubesphere.io/kubesphere/pkg/utils/iputil" "net" "net/http" @@ -41,18 +42,18 @@ type Auditing interface { } type auditing struct { - lister v1alpha1.WebhookLister - resourceGetter *resourcesv1alpha3.ResourceGetter - cache chan *auditv1alpha1.EventList - backend *Backend + webhookLister v1alpha1.WebhookLister + devopsGetter v1alpha3.Interface + cache chan *auditv1alpha1.EventList + backend *Backend } func NewAuditing(informers informers.InformerFactory, url string, stopCh <-chan struct{}) Auditing { a := &auditing{ - lister: informers.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister(), - resourceGetter: resourcesv1alpha3.NewResourceGetter(informers), - cache: make(chan *auditv1alpha1.EventList, DefaultCacheCapacity), + webhookLister: informers.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister(), + devopsGetter: devops.New(informers.KubeSphereSharedInformerFactory()), + cache: make(chan *auditv1alpha1.EventList, DefaultCacheCapacity), } a.backend = NewBackend(url, ChannelCapacity, a.cache, SendTimeout, stopCh) @@ -60,7 +61,7 @@ func NewAuditing(informers informers.InformerFactory, url string, stopCh <-chan } func (a *auditing) getAuditLevel() audit.Level { - wh, err := a.lister.Get(DefaultWebhook) + wh, err := a.webhookLister.Get(DefaultWebhook) if err != nil { klog.V(8).Info(err) return audit.LevelNone @@ -79,7 +80,7 @@ func (a *auditing) Enabled() bool { } func (a *auditing) K8sAuditingEnabled() bool { - wh, err := a.lister.Get(DefaultWebhook) + wh, err := a.webhookLister.Get(DefaultWebhook) if err != nil { klog.V(8).Info(err) return false @@ -111,6 +112,7 @@ func (a *auditing) LogRequestObject(req *http.Request, info *request.RequestInfo } e := &auditv1alpha1.Event{ + Devops: info.DevOps, Workspace: info.Workspace, Cluster: info.Cluster, Event: audit.Event{ @@ -136,34 +138,21 @@ func (a *auditing) LogRequestObject(req *http.Request, info *request.RequestInfo }, } - // Handle the devops request which request url matched /devops/{devops}/kind. - if len(info.Parts) >= 3 && info.Parts[0] == "devops" { - e.ObjectRef.Subresource = "" - e.Devops = info.Parts[1] - // set resource as kind - e.ObjectRef.Resource = info.Parts[2] - - // If the request url matched /devops/{devops}/kind/{kind}, set resource name as {kind} - if len(info.Parts) >= 4 { - e.ObjectRef.Name = info.Parts[3] + // Get the workspace which the devops project be in. + if len(e.Devops) > 0 && len(e.Workspace) == 0 { + res, err := a.devopsGetter.List("", query.New()) + if err != nil { + klog.Error(err) } - // Get the workspace which the devops project be in. - if len(e.Workspace) == 0 { - res, err := a.resourceGetter.List(devopsv1alpha3.ResourcePluralDevOpsProject, "", query.New()) - if err != nil { - klog.Error(err) - } + for _, obj := range res.Items { + d := obj.(*devopsv1alpha3.DevOpsProject) - for _, obj := range res.Items { - d := obj.(*devopsv1alpha3.DevOpsProject) - - if d.Name == e.Devops { - e.Workspace = d.Labels["kubesphere.io/workspace"] - } else if d.Status.AdminNamespace == e.Devops { - e.Workspace = d.Labels["kubesphere.io/workspace"] - e.Devops = d.Name - } + if d.Name == e.Devops { + e.Workspace = d.Labels["kubesphere.io/workspace"] + } else if d.Status.AdminNamespace == e.Devops { + e.Workspace = d.Labels["kubesphere.io/workspace"] + e.Devops = d.Name } } } diff --git a/pkg/apiserver/auditing/types_test.go b/pkg/apiserver/auditing/types_test.go index 9c947be7f..477d8e415 100644 --- a/pkg/apiserver/auditing/types_test.go +++ b/pkg/apiserver/auditing/types_test.go @@ -43,7 +43,7 @@ func TestGetAuditLevel(t *testing.T) { fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, nil, nil, nil, nil) a := auditing{ - lister: fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister(), + webhookLister: fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister(), } err := fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Informer().GetIndexer().Add(webhook) @@ -72,7 +72,7 @@ func TestAuditing_Enabled(t *testing.T) { fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, nil, nil, nil, nil) a := auditing{ - lister: fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister(), + webhookLister: fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister(), } err := fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Informer().GetIndexer().Add(webhook) @@ -102,7 +102,7 @@ func TestAuditing_K8sAuditingEnabled(t *testing.T) { fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, nil, nil, nil, nil) a := auditing{ - lister: fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister(), + webhookLister: fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister(), } err := fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Informer().GetIndexer().Add(webhook) @@ -132,7 +132,7 @@ func TestAuditing_LogRequestObject(t *testing.T) { fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, nil, nil, nil, nil) a := auditing{ - lister: fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister(), + webhookLister: fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister(), } err := fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Informer().GetIndexer().Add(webhook) @@ -222,7 +222,7 @@ func TestAuditing_LogResponseObject(t *testing.T) { fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, nil, nil, nil, nil) a := auditing{ - lister: fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister(), + webhookLister: fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Lister(), } err := fakeInformerFactory.KubeSphereSharedInformerFactory().Auditing().V1alpha1().Webhooks().Informer().GetIndexer().Add(webhook)