Skip to content

Commit af03b29

Browse files
alexmtjannfis
andauthored
Merge pull request from GHSA-2f5v-8r3f-8pww
* fix: application resource APIs must enforce project restrictions Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com> * Fix unit tests Signed-off-by: jannfis <jann@mistrust.net> Co-authored-by: jannfis <jann@mistrust.net>
1 parent a6c664b commit af03b29

File tree

10 files changed

+197
-76
lines changed

10 files changed

+197
-76
lines changed

controller/appcontroller.go

+18-4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2929
"k8s.io/apimachinery/pkg/labels"
3030
apiruntime "k8s.io/apimachinery/pkg/runtime"
31+
"k8s.io/apimachinery/pkg/runtime/schema"
3132
"k8s.io/apimachinery/pkg/types"
3233
"k8s.io/apimachinery/pkg/util/runtime"
3334
"k8s.io/apimachinery/pkg/util/wait"
@@ -421,8 +422,12 @@ func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managed
421422
},
422423
})
423424
} else {
424-
err := ctrl.stateCache.IterateHierarchy(a.Spec.Destination.Server, kube.GetResourceKey(live), func(child appv1.ResourceNode, appName string) {
425+
err := ctrl.stateCache.IterateHierarchy(a.Spec.Destination.Server, kube.GetResourceKey(live), func(child appv1.ResourceNode, appName string) bool {
426+
if !proj.IsResourcePermitted(schema.GroupKind{Group: child.ResourceRef.Group, Kind: child.ResourceRef.Kind}, child.Namespace, a.Spec.Destination) {
427+
return false
428+
}
425429
nodes = append(nodes, child)
430+
return true
426431
})
427432
if err != nil {
428433
return nil, err
@@ -432,16 +437,18 @@ func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managed
432437
orphanedNodes := make([]appv1.ResourceNode, 0)
433438
for k := range orphanedNodesMap {
434439
if k.Namespace != "" && proj.IsGroupKindPermitted(k.GroupKind(), true) && !isKnownOrphanedResourceExclusion(k, proj) {
435-
err := ctrl.stateCache.IterateHierarchy(a.Spec.Destination.Server, k, func(child appv1.ResourceNode, appName string) {
440+
err := ctrl.stateCache.IterateHierarchy(a.Spec.Destination.Server, k, func(child appv1.ResourceNode, appName string) bool {
436441
belongToAnotherApp := false
437442
if appName != "" {
438443
if _, exists, err := ctrl.appInformer.GetIndexer().GetByKey(ctrl.namespace + "/" + appName); exists && err == nil {
439444
belongToAnotherApp = true
440445
}
441446
}
442-
if !belongToAnotherApp {
443-
orphanedNodes = append(orphanedNodes, child)
447+
if belongToAnotherApp || !proj.IsResourcePermitted(schema.GroupKind{Group: child.ResourceRef.Group, Kind: child.ResourceRef.Kind}, child.Namespace, a.Spec.Destination) {
448+
return false
444449
}
450+
orphanedNodes = append(orphanedNodes, child)
451+
return true
445452
})
446453
if err != nil {
447454
return nil, err
@@ -1291,6 +1298,13 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
12911298
app.Status.Sync.Status = appv1.SyncStatusCodeUnknown
12921299
app.Status.Health.Status = health.HealthStatusUnknown
12931300
ctrl.persistAppStatus(origApp, &app.Status)
1301+
1302+
if err := ctrl.cache.SetAppResourcesTree(app.Name, &appv1.ApplicationTree{}); err != nil {
1303+
log.Warnf("failed to set app resource tree: %v", err)
1304+
}
1305+
if err := ctrl.cache.SetAppManagedResources(app.Name, nil); err != nil {
1306+
log.Warnf("failed to set app managed resources tree: %v", err)
1307+
}
12941308
return
12951309
}
12961310

controller/appcontroller_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,12 @@ func newFakeController(data *fakeData) *ApplicationController {
136136
mockStateCache.On("GetClusterCache", mock.Anything).Return(&clusterCacheMock, nil)
137137
mockStateCache.On("IterateHierarchy", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
138138
key := args[1].(kube.ResourceKey)
139-
action := args[2].(func(child argoappv1.ResourceNode, appName string))
139+
action := args[2].(func(child argoappv1.ResourceNode, appName string) bool)
140140
appName := ""
141141
if res, ok := data.namespacedResources[key]; ok {
142142
appName = res.AppName
143143
}
144-
action(argoappv1.ResourceNode{ResourceRef: argoappv1.ResourceRef{Kind: key.Kind, Group: key.Group, Namespace: key.Namespace, Name: key.Name}}, appName)
144+
_ = action(argoappv1.ResourceNode{ResourceRef: argoappv1.ResourceRef{Kind: key.Kind, Group: key.Group, Namespace: key.Namespace, Name: key.Name}}, appName)
145145
}).Return(nil)
146146
return ctrl
147147
}

controller/cache/cache.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ type LiveStateCache interface {
108108
// Returns synced cluster cache
109109
GetClusterCache(server string) (clustercache.ClusterCache, error)
110110
// Executes give callback against resource specified by the key and all its children
111-
IterateHierarchy(server string, key kube.ResourceKey, action func(child appv1.ResourceNode, appName string)) error
111+
IterateHierarchy(server string, key kube.ResourceKey, action func(child appv1.ResourceNode, appName string) bool) error
112112
// Returns state of live nodes which correspond for target nodes of specified application.
113113
GetManagedLiveObjs(a *appv1.Application, targetObjs []*unstructured.Unstructured) (map[kube.ResourceKey]*unstructured.Unstructured, error)
114114
// IterateResources iterates all resource stored in cache
@@ -481,13 +481,13 @@ func (c *liveStateCache) IsNamespaced(server string, gk schema.GroupKind) (bool,
481481
return clusterInfo.IsNamespaced(gk)
482482
}
483483

484-
func (c *liveStateCache) IterateHierarchy(server string, key kube.ResourceKey, action func(child appv1.ResourceNode, appName string)) error {
484+
func (c *liveStateCache) IterateHierarchy(server string, key kube.ResourceKey, action func(child appv1.ResourceNode, appName string) bool) error {
485485
clusterInfo, err := c.getSyncedCluster(server)
486486
if err != nil {
487487
return err
488488
}
489-
clusterInfo.IterateHierarchy(key, func(resource *clustercache.Resource, namespaceResources map[kube.ResourceKey]*clustercache.Resource) {
490-
action(asResourceNode(resource), getApp(resource, namespaceResources))
489+
clusterInfo.IterateHierarchy(key, func(resource *clustercache.Resource, namespaceResources map[kube.ResourceKey]*clustercache.Resource) bool {
490+
return action(asResourceNode(resource), getApp(resource, namespaceResources))
491491
})
492492
return nil
493493
}

controller/cache/mocks/LiveStateCache.go

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ require (
88
github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d
99
github.com/alicebob/miniredis v2.5.0+incompatible
1010
github.com/alicebob/miniredis/v2 v2.14.2
11-
github.com/argoproj/gitops-engine v0.6.0
11+
github.com/argoproj/gitops-engine v0.6.1-0.20220316000647-723667dff7d5
1212
github.com/argoproj/notifications-engine v0.3.1-0.20220127183449-91deed20b998
1313
github.com/argoproj/pkg v0.11.1-0.20211203175135-36c59d8fafe0
1414
github.com/bombsimon/logrusr/v2 v2.0.1

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,8 @@ github.com/antonmedv/expr v1.8.9/go.mod h1:5qsM3oLGDND7sDmQGDXHkYfkjYMUX14qsgqmH
130130
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
131131
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
132132
github.com/appscode/go v0.0.0-20190808133642-1d4ef1f1c1e0/go.mod h1:iy07dV61Z7QQdCKJCIvUoDL21u6AIceRhZzyleh2ymc=
133-
github.com/argoproj/gitops-engine v0.6.0 h1:Tnh6kUUVuBV0m3gueYIymAeErWl9XNN9O9JcOoNM0vU=
134-
github.com/argoproj/gitops-engine v0.6.0/go.mod h1:pRgVpLW7pZqf7n3COJ7UcDepk4cI61LAcJd64Q3Jq/c=
133+
github.com/argoproj/gitops-engine v0.6.1-0.20220316000647-723667dff7d5 h1:QZvSvXHKx/hKTvbw3EK+u9JDVlo6UvC4+IVqNIGbcUo=
134+
github.com/argoproj/gitops-engine v0.6.1-0.20220316000647-723667dff7d5/go.mod h1:pRgVpLW7pZqf7n3COJ7UcDepk4cI61LAcJd64Q3Jq/c=
135135
github.com/argoproj/notifications-engine v0.3.1-0.20220127183449-91deed20b998 h1:V9RDg+IZeebnm3XjkfkbN07VM21Fu1Cy/RJNoHO++VM=
136136
github.com/argoproj/notifications-engine v0.3.1-0.20220127183449-91deed20b998/go.mod h1:5mKv7zEgI3NO0L+fsuRSwBSY9EIXSuyIsDND8O8TTIw=
137137
github.com/argoproj/pkg v0.11.1-0.20211203175135-36c59d8fafe0 h1:Cfp7rO/HpVxnwlRqJe0jHiBbZ77ZgXhB6HWlYD02Xdc=

pkg/apis/application/v1alpha1/app_project_types.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -313,11 +313,15 @@ func (proj AppProject) IsGroupKindPermitted(gk schema.GroupKind, namespaced bool
313313

314314
// IsLiveResourcePermitted returns whether a live resource found in the cluster is permitted by an AppProject
315315
func (proj AppProject) IsLiveResourcePermitted(un *unstructured.Unstructured, server string, name string) bool {
316-
if !proj.IsGroupKindPermitted(un.GroupVersionKind().GroupKind(), un.GetNamespace() != "") {
316+
return proj.IsResourcePermitted(un.GroupVersionKind().GroupKind(), un.GetNamespace(), ApplicationDestination{Server: server, Name: name})
317+
}
318+
319+
func (proj AppProject) IsResourcePermitted(groupKind schema.GroupKind, namespace string, dest ApplicationDestination) bool {
320+
if !proj.IsGroupKindPermitted(groupKind, namespace != "") {
317321
return false
318322
}
319-
if un.GetNamespace() != "" {
320-
return proj.IsDestinationPermitted(ApplicationDestination{Server: server, Namespace: un.GetNamespace(), Name: name})
323+
if namespace != "" {
324+
return proj.IsDestinationPermitted(ApplicationDestination{Server: dest.Server, Name: dest.Name, Namespace: namespace})
321325
}
322326
return true
323327
}

server/application/application.go

+23-8
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,21 @@ func (s *Server) ListResourceEvents(ctx context.Context, q *application.Applicat
488488
"involvedObject.namespace": a.Namespace,
489489
}).String()
490490
} else {
491+
tree, err := s.getAppResources(ctx, a)
492+
if err != nil {
493+
return nil, err
494+
}
495+
found := false
496+
for _, n := range append(tree.Nodes, tree.OrphanedNodes...) {
497+
if n.ResourceRef.UID == q.ResourceUID && n.ResourceRef.Name == q.ResourceName && n.ResourceRef.Namespace == q.ResourceNamespace {
498+
found = true
499+
break
500+
}
501+
}
502+
if !found {
503+
return nil, status.Errorf(codes.InvalidArgument, "%s not found as part of application %s", q.ResourceName, *q.Name)
504+
}
505+
491506
namespace = q.ResourceNamespace
492507
var config *rest.Config
493508
config, err = s.getApplicationClusterConfig(ctx, a)
@@ -937,7 +952,7 @@ func (s *Server) getAppResources(ctx context.Context, a *appv1.Application) (*ap
937952
return &tree, err
938953
}
939954

940-
func (s *Server) getAppResource(ctx context.Context, action string, q *application.ApplicationResourceRequest) (*appv1.ResourceNode, *rest.Config, *appv1.Application, error) {
955+
func (s *Server) getAppLiveResource(ctx context.Context, action string, q *application.ApplicationResourceRequest) (*appv1.ResourceNode, *rest.Config, *appv1.Application, error) {
941956
a, err := s.appLister.Get(*q.Name)
942957
if err != nil {
943958
return nil, nil, nil, err
@@ -952,7 +967,7 @@ func (s *Server) getAppResource(ctx context.Context, action string, q *applicati
952967
}
953968

954969
found := tree.FindNode(q.Group, q.Kind, q.Namespace, q.ResourceName)
955-
if found == nil {
970+
if found == nil || found.ResourceRef.UID == "" {
956971
return nil, nil, nil, status.Errorf(codes.InvalidArgument, "%s %s %s not found as part of application %s", q.Kind, q.Group, q.ResourceName, *q.Name)
957972
}
958973
config, err := s.getApplicationClusterConfig(ctx, a)
@@ -963,7 +978,7 @@ func (s *Server) getAppResource(ctx context.Context, action string, q *applicati
963978
}
964979

965980
func (s *Server) GetResource(ctx context.Context, q *application.ApplicationResourceRequest) (*application.ApplicationResourceResponse, error) {
966-
res, config, _, err := s.getAppResource(ctx, rbacpolicy.ActionGet, q)
981+
res, config, _, err := s.getAppLiveResource(ctx, rbacpolicy.ActionGet, q)
967982
if err != nil {
968983
return nil, err
969984
}
@@ -1008,7 +1023,7 @@ func (s *Server) PatchResource(ctx context.Context, q *application.ApplicationRe
10081023
Version: q.Version,
10091024
Group: q.Group,
10101025
}
1011-
res, config, a, err := s.getAppResource(ctx, rbacpolicy.ActionUpdate, resourceRequest)
1026+
res, config, a, err := s.getAppLiveResource(ctx, rbacpolicy.ActionUpdate, resourceRequest)
10121027
if err != nil {
10131028
return nil, err
10141029
}
@@ -1048,7 +1063,7 @@ func (s *Server) DeleteResource(ctx context.Context, q *application.ApplicationR
10481063
Version: q.Version,
10491064
Group: q.Group,
10501065
}
1051-
res, config, a, err := s.getAppResource(ctx, rbacpolicy.ActionDelete, resourceRequest)
1066+
res, config, a, err := s.getAppLiveResource(ctx, rbacpolicy.ActionDelete, resourceRequest)
10521067
if err != nil {
10531068
return nil, err
10541069
}
@@ -1335,7 +1350,7 @@ func getSelectedPods(treeNodes []appv1.ResourceNode, q *application.ApplicationP
13351350
var pods []appv1.ResourceNode
13361351
isTheOneMap := make(map[string]bool)
13371352
for _, treeNode := range treeNodes {
1338-
if treeNode.Kind == kube.PodKind && treeNode.Group == "" {
1353+
if treeNode.Kind == kube.PodKind && treeNode.Group == "" && treeNode.UID != "" {
13391354
if isTheSelectedOne(&treeNode, q, treeNodes, isTheOneMap) {
13401355
pods = append(pods, treeNode)
13411356
}
@@ -1625,7 +1640,7 @@ func (s *Server) logResourceEvent(res *appv1.ResourceNode, ctx context.Context,
16251640
}
16261641

16271642
func (s *Server) ListResourceActions(ctx context.Context, q *application.ApplicationResourceRequest) (*application.ResourceActionsListResponse, error) {
1628-
res, config, _, err := s.getAppResource(ctx, rbacpolicy.ActionGet, q)
1643+
res, config, _, err := s.getAppLiveResource(ctx, rbacpolicy.ActionGet, q)
16291644
if err != nil {
16301645
return nil, err
16311646
}
@@ -1676,7 +1691,7 @@ func (s *Server) RunResourceAction(ctx context.Context, q *application.ResourceA
16761691
Group: q.Group,
16771692
}
16781693
actionRequest := fmt.Sprintf("%s/%s/%s/%s", rbacpolicy.ActionAction, q.Group, q.Kind, q.Action)
1679-
res, config, a, err := s.getAppResource(ctx, actionRequest, resourceRequest)
1694+
res, config, a, err := s.getAppLiveResource(ctx, actionRequest, resourceRequest)
16801695
if err != nil {
16811696
return nil, err
16821697
}

0 commit comments

Comments
 (0)