-
Notifications
You must be signed in to change notification settings - Fork 500
/
trails_fetch.go
executable file
·174 lines (151 loc) · 5.01 KB
/
trails_fetch.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package cloudtrail
import (
"context"
"fmt"
"regexp"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/aws/arn"
"github.com/aws/aws-sdk-go-v2/service/cloudtrail"
"github.com/aws/aws-sdk-go-v2/service/cloudtrail/types"
"github.com/cloudquery/cloudquery/plugins/source/aws/client"
"github.com/cloudquery/plugin-sdk/schema"
)
type CloudTrailWrapper struct {
types.Trail
Tags map[string]string
}
// groupNameRegex extracts log group name from the ARN
var groupNameRegex = regexp.MustCompile("arn:[a-zA-Z0-9-]+:logs:[a-z0-9-]+:[0-9]+:log-group:([a-zA-Z0-9-/]+):")
func fetchCloudtrailTrails(ctx context.Context, meta schema.ClientMeta, parent *schema.Resource, res chan<- interface{}) error {
c := meta.(*client.Client)
svc := c.Services().Cloudtrail
log := meta.(*client.Client).Logger()
response, err := svc.DescribeTrails(ctx, nil)
if err != nil {
return err
}
getBundledTrailsWithTags := func(trails []types.Trail, region string) ([]CloudTrailWrapper, error) {
processed := make([]CloudTrailWrapper, len(trails))
input := cloudtrail.ListTagsInput{
ResourceIdList: make([]string, 0, len(trails)),
}
for i, h := range trails {
processed[i] = CloudTrailWrapper{
Trail: h,
Tags: make(map[string]string),
}
// Before fetching trail tags we have to check if the trail is organization trail
// If the trail is organization trail and the account id is not matched with current account id
// We skip, and not fetch the trail tags
arnParts, err := arn.Parse(*h.TrailARN)
if err != nil {
log.Warn().Str("arn", *h.TrailARN).Msg("could not parse cloudtrail ARN")
continue
}
if aws.ToBool(h.IsOrganizationTrail) && c.AccountID != arnParts.AccountID {
log.Warn().Str("arn", *h.TrailARN).Msg("the trail is an organization-level trail, could not fetch tags")
continue
}
input.ResourceIdList = append(input.ResourceIdList, *h.TrailARN)
}
if len(input.ResourceIdList) == 0 {
return processed, nil
}
for {
response, err := svc.ListTags(ctx, &input, func(options *cloudtrail.Options) {
options.Region = region
})
if err != nil {
return nil, err
}
for i, tr := range processed {
client.TagsIntoMap(getCloudTrailTagsByResourceID(*tr.TrailARN, response.ResourceTagList), processed[i].Tags)
}
if aws.ToString(response.NextToken) == "" {
break
}
input.NextToken = response.NextToken
}
return processed, nil
}
// since api returns all the cloudtrails despite region we aggregate trails by region to get tags.
aggregatedTrails, err := aggregateCloudTrails(response.TrailList)
if err != nil {
return err
}
for region, trails := range aggregatedTrails {
for i := 0; i < len(trails); i += 20 {
end := i + 20
if end > len(trails) {
end = len(trails)
}
t := trails[i:end]
processed, err := getBundledTrailsWithTags(t, region)
if err != nil {
return err
}
res <- processed
}
}
return nil
}
func resolveCloudTrailStatus(ctx context.Context, meta schema.ClientMeta, resource *schema.Resource, col schema.Column) error {
c := meta.(*client.Client)
svc := c.Services().Cloudtrail
r := resource.Item.(CloudTrailWrapper)
response, err := svc.GetTrailStatus(ctx,
&cloudtrail.GetTrailStatusInput{Name: r.TrailARN}, func(o *cloudtrail.Options) {
o.Region = *r.HomeRegion
})
if err != nil {
return err
}
return resource.Set("status", response)
}
func resolveCloudtrailTrailCloudwatchLogsLogGroupName(ctx context.Context, meta schema.ClientMeta, resource *schema.Resource, c schema.Column) error {
groupName := ""
log := meta.(*client.Client).Logger()
r := resource.Item.(CloudTrailWrapper)
if r.CloudWatchLogsLogGroupArn != nil {
matches := groupNameRegex.FindStringSubmatch(*r.CloudWatchLogsLogGroupArn)
if len(matches) < 2 {
log.Warn().Str("arn", *r.CloudWatchLogsLogGroupArn).Msg("CloudWatchLogsLogGroupARN doesn't fit standard regex")
} else {
groupName = matches[1]
}
} else {
log.Info().Msg("CloudWatchLogsLogGroupARN is empty")
}
return resource.Set("cloudwatch_logs_log_group_name", groupName)
}
func fetchCloudtrailTrailEventSelectors(ctx context.Context, meta schema.ClientMeta, parent *schema.Resource, res chan<- interface{}) error {
r := parent.Item.(CloudTrailWrapper)
c := meta.(*client.Client)
svc := c.Services().Cloudtrail
response, err := svc.GetEventSelectors(ctx, &cloudtrail.GetEventSelectorsInput{TrailName: r.TrailARN}, func(options *cloudtrail.Options) {
options.Region = *r.HomeRegion
})
if err != nil {
return err
}
res <- response.EventSelectors
return nil
}
func getCloudTrailTagsByResourceID(id string, set []types.ResourceTag) []types.Tag {
for _, s := range set {
if *s.ResourceId == id {
return s.TagsList
}
}
return nil
}
func aggregateCloudTrails(trails []types.Trail) (map[string][]types.Trail, error) {
resp := make(map[string][]types.Trail)
for _, t := range trails {
if t.HomeRegion == nil {
return nil, fmt.Errorf("got cloudtrail with HomeRegion == nil")
}
resp[*t.HomeRegion] = append(resp[*t.HomeRegion], t)
}
return resp, nil
}