Skip to content

Commit 1d0c71b

Browse files
committed
✨ Add operator-managed annotations for asset routing in inventory functions
✨ Implement asset routing test with MondooAuditConfigs and related scripts ✨ Refactor asset routing annotations to use centralized AuditConfigAnnotations function ✨ Reorder asset annotation application to prioritize user-defined annotations over operator-managed ones
1 parent 75d8535 commit 1d0c71b

18 files changed

Lines changed: 571 additions & 13 deletions

controllers/container_image/resources.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,12 +296,16 @@ func Inventory(integrationMRN, clusterUID string, m v1alpha2.MondooAuditConfig,
296296
}
297297
}
298298

299-
// Add user-defined annotations to all assets
299+
// Add user-defined annotations first, then operator-managed annotations.
300+
// Operator annotations go last so they cannot be overwritten by user values.
300301
if len(m.Spec.Annotations) > 0 {
301302
for i := range inv.Spec.Assets {
302303
inv.Spec.Assets[i].AddAnnotations(m.Spec.Annotations)
303304
}
304305
}
306+
for i := range inv.Spec.Assets {
307+
inv.Spec.Assets[i].AddAnnotations(constants.AuditConfigAnnotations(m.Name, m.Namespace))
308+
}
305309

306310
invBytes, err := yaml.Marshal(inv)
307311
if err != nil {

controllers/k8s_scan/resources.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -995,12 +995,16 @@ func Inventory(integrationMRN, clusterUID string, m v1alpha2.MondooAuditConfig,
995995
}
996996
}
997997

998-
// Add user-defined annotations to all assets
998+
// Add user-defined annotations first, then operator-managed annotations.
999+
// Operator annotations go last so they cannot be overwritten by user values.
9991000
if len(m.Spec.Annotations) > 0 {
10001001
for i := range inv.Spec.Assets {
10011002
inv.Spec.Assets[i].AddAnnotations(m.Spec.Annotations)
10021003
}
10031004
}
1005+
for i := range inv.Spec.Assets {
1006+
inv.Spec.Assets[i].AddAnnotations(constants.AuditConfigAnnotations(m.Name, m.Namespace))
1007+
}
10041008

10051009
invBytes, err := yaml.Marshal(inv)
10061010
if err != nil {
@@ -1065,12 +1069,18 @@ func ExternalClusterInventory(integrationMRN, operatorClusterUID string, cluster
10651069
}
10661070
}
10671071

1068-
// Add user-defined annotations to all assets
1072+
// Add user-defined annotations first, then operator-managed annotations.
1073+
// Operator annotations go last so they cannot be overwritten by user values.
10691074
if len(m.Spec.Annotations) > 0 {
10701075
for i := range inv.Spec.Assets {
10711076
inv.Spec.Assets[i].AddAnnotations(m.Spec.Annotations)
10721077
}
10731078
}
1079+
operatorAnnotations := constants.AuditConfigAnnotations(m.Name, m.Namespace)
1080+
operatorAnnotations[constants.MondooClusterNameAnnotation] = cluster.Name
1081+
for i := range inv.Spec.Assets {
1082+
inv.Spec.Assets[i].AddAnnotations(operatorAnnotations)
1083+
}
10741084

10751085
invBytes, err := yaml.Marshal(inv)
10761086
if err != nil {

controllers/nodes/resources.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,12 +401,16 @@ func Inventory(integrationMRN, clusterUID string, m v1alpha2.MondooAuditConfig)
401401
}
402402
}
403403

404-
// Add user-defined annotations to all assets
404+
// Add user-defined annotations first, then operator-managed annotations.
405+
// Operator annotations go last so they cannot be overwritten by user values.
405406
if len(m.Spec.Annotations) > 0 {
406407
for i := range inv.Spec.Assets {
407408
inv.Spec.Assets[i].AddAnnotations(m.Spec.Annotations)
408409
}
409410
}
411+
for i := range inv.Spec.Assets {
412+
inv.Spec.Assets[i].AddAnnotations(constants.AuditConfigAnnotations(m.Name, m.Namespace))
413+
}
410414

411415
invBytes, err := yaml.Marshal(inv)
412416
if err != nil {

pkg/constants/constants.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,19 @@ const (
1616
// MondooAssetsIntegrationLabel is the label we set for any assets whenever the consoleIntegration is enabled
1717
// (for consistency with other integrations, the integration tag will not use the 'k8s' prefix)
1818
MondooAssetsIntegrationLabel = "mondoo.com/" + "integration-mrn"
19+
20+
// Operator-managed asset annotations propagated to all discovered assets for routing.
21+
MondooAuditConfigAnnotation = "mondoo.com/audit-config/name"
22+
MondooAuditConfigNamespaceAnnotation = "mondoo.com/audit-config/namespace"
23+
MondooClusterNameAnnotation = "mondoo.com/audit-config/cluster-name"
1924
)
25+
26+
// AuditConfigAnnotations returns operator-managed annotations identifying the
27+
// MondooAuditConfig that owns the scan. These are propagated to all discovered
28+
// assets so that server-side routing rules can match on them.
29+
func AuditConfigAnnotations(name, namespace string) map[string]string {
30+
return map[string]string{
31+
MondooAuditConfigAnnotation: name,
32+
MondooAuditConfigNamespaceAnnotation: namespace,
33+
}
34+
}

pkg/utils/mondoo/gc.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,14 @@ func GarbageCollectAssets(
126126
req.SpaceMrn = SpaceMrnFromServiceAccountMrn(sa.Mrn)
127127
}
128128
}
129+
130+
// Org-level service accounts without spaceId have no determinable space for GC.
131+
// This happens when asset routing is used (server-side routing, no operator-side space).
132+
if req.SpaceMrn == "" {
133+
logger.Info("Skipping garbage collection: no space MRN determinable (org-level SA without spaceId)")
134+
return nil
135+
}
136+
129137
logger.Info("Preparing GarbageCollectAssets request", "spaceMrn", req.SpaceMrn, "managedBy", req.ManagedBy)
130138

131139
opts := mondooclient.MondooClientOptions{
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Copyright Mondoo, Inc. 2026
2+
# SPDX-License-Identifier: BUSL-1.1
3+
4+
# MondooAuditConfig for scanning the LOCAL cluster (Autopilot — no nodes).
5+
# No spaceId — server-side asset routing rules determine destination spaces.
6+
apiVersion: k8s.mondoo.com/v1alpha2
7+
kind: MondooAuditConfig
8+
metadata:
9+
name: mondoo-scanner
10+
namespace: ${NAMESPACE}
11+
spec:
12+
mondooCredsSecretRef:
13+
name: mondoo-client
14+
filtering:
15+
namespaces:
16+
exclude:
17+
- kube-system
18+
- gke-managed-system
19+
- gke-managed-cim
20+
kubernetesResources:
21+
enable: true
22+
schedule: "*/5 * * * *"
23+
containers:
24+
enable: true
25+
schedule: "*/5 * * * *"
26+
nodes:
27+
enable: false
28+
---
29+
# MondooAuditConfig for scanning the REMOTE (target) cluster.
30+
# No spaceId — server-side asset routing rules determine destination spaces.
31+
# enable: false prevents local cluster scanning; externalClusters reconciles independently.
32+
apiVersion: k8s.mondoo.com/v1alpha2
33+
kind: MondooAuditConfig
34+
metadata:
35+
name: mondoo-target
36+
namespace: ${NAMESPACE}
37+
spec:
38+
mondooCredsSecretRef:
39+
name: mondoo-client
40+
filtering:
41+
namespaces:
42+
exclude:
43+
- kube-system
44+
- gke-managed-system
45+
- gke-managed-cim
46+
kubernetesResources:
47+
enable: false
48+
schedule: "*/5 * * * *"
49+
externalClusters:
50+
- name: target-cluster
51+
kubeconfigSecretRef:
52+
name: target-kubeconfig
53+
containers:
54+
enable: false
55+
nodes:
56+
enable: false
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Copyright Mondoo, Inc. 2026
2+
# SPDX-License-Identifier: BUSL-1.1
3+
4+
# MondooAuditConfig for scanning the LOCAL cluster.
5+
# No spaceId — server-side asset routing rules determine destination spaces.
6+
apiVersion: k8s.mondoo.com/v1alpha2
7+
kind: MondooAuditConfig
8+
metadata:
9+
name: mondoo-scanner
10+
namespace: ${NAMESPACE}
11+
spec:
12+
mondooCredsSecretRef:
13+
name: mondoo-client
14+
filtering:
15+
namespaces:
16+
exclude:
17+
- kube-system
18+
- gke-managed-system
19+
- gke-managed-cim
20+
kubernetesResources:
21+
enable: true
22+
schedule: "*/5 * * * *"
23+
containers:
24+
enable: true
25+
schedule: "*/5 * * * *"
26+
nodes:
27+
enable: true
28+
style: cronjob
29+
schedule: "*/5 * * * *"
30+
---
31+
# MondooAuditConfig for scanning the REMOTE (target) cluster.
32+
# No spaceId — server-side asset routing rules determine destination spaces.
33+
# enable: false prevents local cluster scanning; externalClusters reconciles independently.
34+
apiVersion: k8s.mondoo.com/v1alpha2
35+
kind: MondooAuditConfig
36+
metadata:
37+
name: mondoo-target
38+
namespace: ${NAMESPACE}
39+
spec:
40+
mondooCredsSecretRef:
41+
name: mondoo-client
42+
filtering:
43+
namespaces:
44+
exclude:
45+
- kube-system
46+
- gke-managed-system
47+
- gke-managed-cim
48+
kubernetesResources:
49+
enable: false
50+
schedule: "*/5 * * * *"
51+
externalClusters:
52+
- name: target-cluster
53+
kubeconfigSecretRef:
54+
name: target-kubeconfig
55+
containers:
56+
enable: false
57+
nodes:
58+
enable: false

tests/e2e/gke/terraform/mondoo.tf

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,59 @@ resource "mondoo_service_account" "e2e" {
2020
################################################################################
2121

2222
resource "mondoo_space" "target" {
23-
count = var.enable_space_splitting_test ? 1 : 0
23+
count = (var.enable_space_splitting_test || var.enable_asset_routing_test) ? 1 : 0
2424
name = "e2e-target-${local.name_prefix}"
2525
org_id = var.mondoo_org_id
2626
}
2727

2828
resource "mondoo_service_account" "org" {
29-
count = var.enable_space_splitting_test ? 1 : 0
29+
count = (var.enable_space_splitting_test || var.enable_asset_routing_test) ? 1 : 0
3030
name = "e2e-org-sa"
3131
description = "Org-level service account for space splitting e2e test"
3232
roles = ["//iam.api.mondoo.app/roles/agent"]
3333
org_id = var.mondoo_org_id
3434
}
35+
36+
################################################################################
37+
# Asset Routing Test: developers space + routing table
38+
################################################################################
39+
40+
resource "mondoo_space" "developers" {
41+
count = var.enable_asset_routing_test ? 1 : 0
42+
name = "e2e-developers-${local.name_prefix}"
43+
org_id = var.mondoo_org_id
44+
}
45+
46+
resource "mondoo_asset_routing_table" "e2e" {
47+
count = var.enable_asset_routing_test ? 1 : 0
48+
org_mrn = "//captain.api.mondoo.app/organizations/${var.mondoo_org_id}"
49+
50+
# Priority 1: k8s workload label app=nginx-developers → developers space
51+
rule {
52+
target_space_mrn = mondoo_space.developers[0].mrn
53+
condition {
54+
field = "LABEL"
55+
key = "app"
56+
operator = "EQUAL"
57+
values = ["nginx-developers"]
58+
}
59+
}
60+
61+
# Priority 2: external cluster annotation (set by operator) → target space
62+
rule {
63+
target_space_mrn = mondoo_space.target[0].mrn
64+
condition {
65+
field = "LABEL"
66+
key = "mondoo.com/audit-config/cluster-name"
67+
operator = "EQUAL"
68+
values = ["target-cluster"]
69+
}
70+
}
71+
72+
# Catch-all → default e2e space
73+
rule {
74+
target_space_mrn = mondoo_space.e2e.mrn
75+
}
76+
77+
depends_on = [mondoo_space.developers, mondoo_space.target, mondoo_space.e2e]
78+
}

tests/e2e/gke/terraform/outputs.tf

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,22 @@ output "scanner_space_id" {
8888
}
8989

9090
output "target_space_id" {
91-
value = var.enable_space_splitting_test ? mondoo_space.target[0].id : ""
91+
value = (var.enable_space_splitting_test || var.enable_asset_routing_test) ? mondoo_space.target[0].id : ""
9292
}
9393

9494
output "target_space_mrn" {
95-
value = var.enable_space_splitting_test ? mondoo_space.target[0].mrn : ""
95+
value = (var.enable_space_splitting_test || var.enable_asset_routing_test) ? mondoo_space.target[0].mrn : ""
9696
}
9797

9898
output "org_credentials_b64" {
99-
value = var.enable_space_splitting_test ? mondoo_service_account.org[0].credential : ""
99+
value = (var.enable_space_splitting_test || var.enable_asset_routing_test) ? mondoo_service_account.org[0].credential : ""
100100
sensitive = true
101101
}
102+
103+
output "enable_asset_routing_test" {
104+
value = var.enable_asset_routing_test
105+
}
106+
107+
output "developers_space_id" {
108+
value = var.enable_asset_routing_test ? mondoo_space.developers[0].id : ""
109+
}

tests/e2e/gke/terraform/terraform.example.tfvars

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,22 @@
33

44
project_id = "your-gcp-project-id"
55
mondoo_org_id = "your-mondoo-organization-id"
6+
region = "europe-west3"
67
autopilot = true
78

8-
# Set to true to provision a mirror AR repo for registry mirroring/imagePullSecrets tests
9+
# Create a second GKE cluster as a scan target for external cluster testing
10+
enable_target_cluster = false
11+
12+
# Create a mirror AR repo for registry mirroring/imagePullSecrets tests
913
enable_mirror_test = false
10-
# Set to true to also provision a Squid proxy VM for proxy tests (requires enable_mirror_test)
11-
enable_proxy_test = false
14+
# Provision a Squid proxy VM for proxy tests (requires enable_mirror_test)
15+
enable_proxy_test = false
16+
17+
# Enable GKE Workload Identity Federation for external cluster scanning
18+
enable_wif_test = false
19+
20+
# Test org-level SA with spaceId routing (requires enable_target_cluster)
21+
enable_space_splitting_test = false
22+
23+
# Test server-side asset routing rules (requires enable_target_cluster)
24+
enable_asset_routing_test = false

0 commit comments

Comments
 (0)