Skip to content

Commit bf22d31

Browse files
committed
Add additional fields to the GCP pub/sub resources
New security checks enabled: - Ensure Pub/Sub topics are not publicly accessible (via iamPolicy) - Ensure Pub/Sub subscriptions are not publicly accessible (via iamPolicy) New fields on topic: - iamPolicy: IAM policy bindings for the topic - config.state: topic state (ACTIVE, INGESTION_RESOURCE_ERROR) New fields on subscription: - iamPolicy: IAM policy bindings for the subscription - config.enableMessageOrdering: whether ordered key delivery is enabled - config.enableExactlyOnceDelivery: whether exactly-once delivery is enabled - config.filter: message filter expression - config.detached: whether the subscription is detached from its topic - config.state: subscription state (ACTIVE, RESOURCE_ERROR) Signed-off-by: Tim Smith <tsmith84@gmail.com>
1 parent ab7c48b commit bf22d31

File tree

4 files changed

+203
-9
lines changed

4 files changed

+203
-9
lines changed

providers/gcp/resources/gcp.lr

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2216,6 +2216,8 @@ private gcp.project.pubsubService.topic @defaults("name") {
22162216
name string
22172217
// Topic configuration
22182218
config() gcp.project.pubsubService.topic.config
2219+
// IAM policy for this topic
2220+
iamPolicy() []gcp.resourcemanager.binding
22192221
}
22202222

22212223
// Google Cloud (GCP) Pub/Sub topic configuration
@@ -2230,6 +2232,8 @@ private gcp.project.pubsubService.topic.config @defaults("kmsKeyName messageStor
22302232
kmsKeyName string
22312233
// Message storage policy
22322234
messageStoragePolicy gcp.project.pubsubService.topic.config.messagestoragepolicy
2235+
// State of the topic (ACTIVE, INGESTION_RESOURCE_ERROR)
2236+
state string
22332237
}
22342238

22352239
// Google Cloud (GCP) Pub/Sub topic message storage policy
@@ -2248,6 +2252,8 @@ private gcp.project.pubsubService.subscription @defaults("name") {
22482252
name string
22492253
// Subscription configuration
22502254
config() gcp.project.pubsubService.subscription.config
2255+
// IAM policy for this subscription
2256+
iamPolicy() []gcp.resourcemanager.binding
22512257
}
22522258

22532259
// Google Cloud (GCP) Pub/Sub subscription configuration
@@ -2270,6 +2276,16 @@ private gcp.project.pubsubService.subscription.config @defaults("topic.name ackD
22702276
expirationPolicy time
22712277
// The labels associated with this subscription
22722278
labels map[string]string
2279+
// Whether message ordering is enabled
2280+
enableMessageOrdering bool
2281+
// Whether exactly-once delivery is enabled
2282+
enableExactlyOnceDelivery bool
2283+
// Filter expression for the subscription
2284+
filter string
2285+
// Whether the subscription is detached from its topic
2286+
detached bool
2287+
// State of the subscription (ACTIVE, RESOURCE_ERROR)
2288+
state string
22732289
}
22742290

22752291
// GCP Pub/Sub configuration for subscriptions that operate in push mode

providers/gcp/resources/gcp.lr.go

Lines changed: 48 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

providers/gcp/resources/gcp.lr.manifest.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2042,6 +2042,7 @@ resources:
20422042
gcp.project.pubsubService.subscription:
20432043
fields:
20442044
config: {}
2045+
iamPolicy: {}
20452046
name: {}
20462047
projectId: {}
20472048
is_private: true
@@ -2055,12 +2056,17 @@ resources:
20552056
gcp.project.pubsubService.subscription.config:
20562057
fields:
20572058
ackDeadline: {}
2059+
detached: {}
2060+
enableExactlyOnceDelivery: {}
2061+
enableMessageOrdering: {}
20582062
expirationPolicy: {}
2063+
filter: {}
20592064
labels: {}
20602065
projectId: {}
20612066
pushConfig: {}
20622067
retainAckedMessages: {}
20632068
retentionDuration: {}
2069+
state: {}
20642070
subscriptionName: {}
20652071
topic: {}
20662072
is_private: true
@@ -2087,6 +2093,7 @@ resources:
20872093
gcp.project.pubsubService.topic:
20882094
fields:
20892095
config: {}
2096+
iamPolicy: {}
20902097
name: {}
20912098
projectId: {}
20922099
is_private: true
@@ -2103,6 +2110,7 @@ resources:
21032110
labels: {}
21042111
messageStoragePolicy: {}
21052112
projectId: {}
2113+
state: {}
21062114
topicName: {}
21072115
is_private: true
21082116
min_mondoo_version: latest

providers/gcp/resources/pubsub.go

Lines changed: 131 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ func (g *mqlGcpProjectPubsubServiceTopic) config() (*mqlGcpProjectPubsubServiceT
225225
"labels": llx.MapData(convert.MapToInterfaceMap(cfg.Labels), types.String),
226226
"kmsKeyName": llx.StringData(cfg.KMSKeyName),
227227
"messageStoragePolicy": llx.ResourceData(messageStoragePolicy, "gcp.project.pubsubService.topic.config.messagestoragepolicy"),
228+
"state": llx.StringData(topicStateToString(cfg.State)),
228229
})
229230
if err != nil {
230231
return nil, err
@@ -327,15 +328,20 @@ func (g *mqlGcpProjectPubsubServiceSubscription) config() (*mqlGcpProjectPubsubS
327328
expPolicy = llx.DurationToTime(int64(exp.Seconds()))
328329
}
329330
res, err := CreateResource(g.MqlRuntime, "gcp.project.pubsubService.subscription.config", map[string]*llx.RawData{
330-
"projectId": llx.StringData(projectId),
331-
"subscriptionName": llx.StringData(s.ID()),
332-
"topic": llx.ResourceData(topic, "gcp.project.pubsubService.topic"),
333-
"pushConfig": llx.ResourceData(pushConfig, "gcp.project.pubsubService.subscription.config.pushconfig"),
334-
"ackDeadline": llx.TimeData(llx.DurationToTime(int64(cfg.AckDeadline.Seconds()))),
335-
"retainAckedMessages": llx.BoolData(cfg.RetainAckedMessages),
336-
"retentionDuration": llx.TimeData(llx.DurationToTime(int64(cfg.RetentionDuration.Seconds()))),
337-
"expirationPolicy": llx.TimeData(expPolicy),
338-
"labels": llx.MapData(convert.MapToInterfaceMap(cfg.Labels), types.String),
331+
"projectId": llx.StringData(projectId),
332+
"subscriptionName": llx.StringData(s.ID()),
333+
"topic": llx.ResourceData(topic, "gcp.project.pubsubService.topic"),
334+
"pushConfig": llx.ResourceData(pushConfig, "gcp.project.pubsubService.subscription.config.pushconfig"),
335+
"ackDeadline": llx.TimeData(llx.DurationToTime(int64(cfg.AckDeadline.Seconds()))),
336+
"retainAckedMessages": llx.BoolData(cfg.RetainAckedMessages),
337+
"retentionDuration": llx.TimeData(llx.DurationToTime(int64(cfg.RetentionDuration.Seconds()))),
338+
"expirationPolicy": llx.TimeData(expPolicy),
339+
"labels": llx.MapData(convert.MapToInterfaceMap(cfg.Labels), types.String),
340+
"enableMessageOrdering": llx.BoolData(cfg.EnableMessageOrdering),
341+
"enableExactlyOnceDelivery": llx.BoolData(cfg.EnableExactlyOnceDelivery),
342+
"filter": llx.StringData(cfg.Filter),
343+
"detached": llx.BoolData(cfg.Detached),
344+
"state": llx.StringData(subscriptionStateToString(cfg.State)),
339345
})
340346
if err != nil {
341347
return nil, err
@@ -401,6 +407,122 @@ func (g *mqlGcpProjectPubsubService) snapshots() ([]any, error) {
401407
return subs, nil
402408
}
403409

410+
func (g *mqlGcpProjectPubsubServiceTopic) iamPolicy() ([]any, error) {
411+
if g.Name.Error != nil {
412+
return nil, g.Name.Error
413+
}
414+
name := g.Name.Data
415+
416+
if g.ProjectId.Error != nil {
417+
return nil, g.ProjectId.Error
418+
}
419+
projectId := g.ProjectId.Data
420+
421+
conn := g.MqlRuntime.Connection.(*connection.GcpConnection)
422+
423+
creds, err := conn.Credentials(pubsub.ScopePubSub)
424+
if err != nil {
425+
return nil, err
426+
}
427+
428+
ctx := context.Background()
429+
430+
pubsubSvc, err := pubsub.NewClient(ctx, projectId, option.WithCredentials(creds))
431+
if err != nil {
432+
return nil, err
433+
}
434+
defer pubsubSvc.Close()
435+
436+
policy, err := pubsubSvc.Topic(name).IAM().Policy(ctx)
437+
if err != nil {
438+
return nil, err
439+
}
440+
441+
res := []any{}
442+
for i, role := range policy.Roles() {
443+
members := policy.Members(role)
444+
mqlBinding, err := CreateResource(g.MqlRuntime, "gcp.resourcemanager.binding", map[string]*llx.RawData{
445+
"id": llx.StringData(fmt.Sprintf("projects/%s/topics/%s-%d", projectId, name, i)),
446+
"role": llx.StringData(string(role)),
447+
"members": llx.ArrayData(convert.SliceAnyToInterface(members), types.String),
448+
})
449+
if err != nil {
450+
return nil, err
451+
}
452+
res = append(res, mqlBinding)
453+
}
454+
return res, nil
455+
}
456+
457+
func (g *mqlGcpProjectPubsubServiceSubscription) iamPolicy() ([]any, error) {
458+
if g.Name.Error != nil {
459+
return nil, g.Name.Error
460+
}
461+
name := g.Name.Data
462+
463+
if g.ProjectId.Error != nil {
464+
return nil, g.ProjectId.Error
465+
}
466+
projectId := g.ProjectId.Data
467+
468+
conn := g.MqlRuntime.Connection.(*connection.GcpConnection)
469+
470+
creds, err := conn.Credentials(pubsub.ScopePubSub)
471+
if err != nil {
472+
return nil, err
473+
}
474+
475+
ctx := context.Background()
476+
477+
pubsubSvc, err := pubsub.NewClient(ctx, projectId, option.WithCredentials(creds))
478+
if err != nil {
479+
return nil, err
480+
}
481+
defer pubsubSvc.Close()
482+
483+
policy, err := pubsubSvc.Subscription(name).IAM().Policy(ctx)
484+
if err != nil {
485+
return nil, err
486+
}
487+
488+
res := []any{}
489+
for i, role := range policy.Roles() {
490+
members := policy.Members(role)
491+
mqlBinding, err := CreateResource(g.MqlRuntime, "gcp.resourcemanager.binding", map[string]*llx.RawData{
492+
"id": llx.StringData(fmt.Sprintf("projects/%s/subscriptions/%s-%d", projectId, name, i)),
493+
"role": llx.StringData(string(role)),
494+
"members": llx.ArrayData(convert.SliceAnyToInterface(members), types.String),
495+
})
496+
if err != nil {
497+
return nil, err
498+
}
499+
res = append(res, mqlBinding)
500+
}
501+
return res, nil
502+
}
503+
504+
func topicStateToString(state pubsub.TopicState) string {
505+
switch state {
506+
case pubsub.TopicStateActive:
507+
return "ACTIVE"
508+
case pubsub.TopicStateIngestionResourceError:
509+
return "INGESTION_RESOURCE_ERROR"
510+
default:
511+
return "STATE_UNSPECIFIED"
512+
}
513+
}
514+
515+
func subscriptionStateToString(state pubsub.SubscriptionState) string {
516+
switch state {
517+
case pubsub.SubscriptionStateActive:
518+
return "ACTIVE"
519+
case pubsub.SubscriptionStateResourceError:
520+
return "RESOURCE_ERROR"
521+
default:
522+
return "STATE_UNSPECIFIED"
523+
}
524+
}
525+
404526
func pubsubConfigId(projectId, parentName string) string {
405527
return fmt.Sprintf("%s/%s/config", projectId, parentName)
406528
}

0 commit comments

Comments
 (0)