diff --git a/pkg/kubehound/graph/edge/permission_discover.go b/pkg/kubehound/graph/edge/permission_discover.go index 5d209dfe7..153be9be8 100644 --- a/pkg/kubehound/graph/edge/permission_discover.go +++ b/pkg/kubehound/graph/edge/permission_discover.go @@ -122,13 +122,6 @@ func (e *PermissionDiscover) Stream(ctx context.Context, store storedb.Provider, "", }, }, - // service account so no namespace checks needed - bson.M{ - "$eq": bson.A{ - "$result.subjects.subject.kind", - "ServiceAccount", - }, - }, // clusterrolerbinding so no namespace checks needed bson.M{ "$eq": bson.A{ diff --git a/pkg/kubehound/models/converter/store.go b/pkg/kubehound/models/converter/store.go index a7127ea05..bc4212d31 100644 --- a/pkg/kubehound/models/converter/store.go +++ b/pkg/kubehound/models/converter/store.go @@ -310,6 +310,10 @@ func (c *StoreConverter) RoleBinding(ctx context.Context, input types.RoleBindin } for _, s := range subj { + // ServiceAccount are bounded to a namespace + if s.Namespace == "" && s.Kind == rbacv1.ServiceAccountKind { + s.Namespace = input.Namespace + } s, err := c.convertSubject(ctx, s) if err != nil { return nil, fmt.Errorf("role binding subject convert: %w", err) @@ -366,7 +370,7 @@ func (c *StoreConverter) ClusterRoleBinding(ctx context.Context, input types.Clu // Identity returns the store representation of a K8s identity role binding from an input store BindSubject (subfield of RoleBinding) object. // NOTE: store.Identity does not map directly to a K8s API object and instead derives from the subject of a role binding. -func (c *StoreConverter) Identity(_ context.Context, input *store.BindSubject, parent *store.RoleBinding) (*store.Identity, error) { +func (c *StoreConverter) Identity(ctx context.Context, input *store.BindSubject, parent *store.RoleBinding) (*store.Identity, error) { output := &store.Identity{ Id: input.IdentityId, Name: input.Subject.Name, @@ -376,6 +380,21 @@ func (c *StoreConverter) Identity(_ context.Context, input *store.BindSubject, p Runtime: store.Runtime(c.runtime), } + // ServiceAccount are bounded to a namespace + // In a rolebindings definition, namespace is optional for ServiceAccount + // Since we are parsing rolebindings to get the list of ServiceAccount we need to fix the ServiceAccount namespace if it is missing + if input.Subject.Kind == "ServiceAccount" && len(input.Subject.Namespace) == 0 { + // This should never happen but ¯\_(ツ)_/¯ + if len(parent.Namespace) == 0 { + log.Trace(ctx).Errorf("Namespace not found for service account (%s), using input(rolebinding) namespace (%s) for PermissionSet (%s)\n", input.Subject.Name, parent.Namespace, input.IdentityId) + } else { + output.Namespace = parent.Namespace + output.IsNamespaced = true + } + + return output, nil + } + if len(input.Subject.Namespace) != 0 { output.IsNamespaced = true output.Namespace = input.Subject.Namespace