Skip to content

Commit

Permalink
New AWS Cloudtrail rule: include global service events
Browse files Browse the repository at this point in the history
  • Loading branch information
aisha-als committed Jul 22, 2023
1 parent bbd990f commit acd0447
Show file tree
Hide file tree
Showing 13 changed files with 279 additions and 51 deletions.
15 changes: 15 additions & 0 deletions avd_docs/aws/cloudtrail/AVD-AWS-0343/CloudFormation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

Enable include global service events for Cloudtrail

```yaml---
Resources:
GoodExampleTrail:
Type: AWS::CloudTrail::Trail
Properties:
IncludeGlobalServiceEvents: true
S3BucketName: "my-bucket"
TrailName: "Cloudtrail"
```


14 changes: 14 additions & 0 deletions avd_docs/aws/cloudtrail/AVD-AWS-0343/Terraform.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

Enable include global service events for Cloudtrail

```hcl
resource "aws_cloudtrail" "good_example" {
include_global_service_events = true
s3_bucket_name = "abcdefgh"
}
```

#### Remediation Links
- https://registry.terraform.io/providers/rgeraskin/aws2/latest/docs/resources/cloudtrail#include_global_service_events

13 changes: 13 additions & 0 deletions avd_docs/aws/cloudtrail/AVD-AWS-0343/docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

Include Global Service Events is a default value for Cloudtrail and it publishes events from global services that are not region specific such as IAM, STS and CloudFront. It is feasible that a rogue actor compromising an AWS account might want to disable this field to remove trace of their actions.

### Impact
Events from global services such as IAM are not being published to the log files

<!-- DO NOT CHANGE -->
{{ remediationActions }}

### Links
- https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-concepts.html#cloudtrail-concepts-global-service-events


24 changes: 15 additions & 9 deletions internal/adapters/cloud/aws/cloudtrail/adapt.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ func (a *adapter) adaptTrail(info types.TrailInfo) (*cloudtrail.Trail, error) {
isLogging = defsecTypes.Bool(*status.IsLogging, metadata)
}

includeGlobalServiceEvents := defsecTypes.BoolDefault(false, metadata)
if response.Trail.IncludeGlobalServiceEvents != nil {
includeGlobalServiceEvents = defsecTypes.Bool(*response.Trail.IncludeGlobalServiceEvents, metadata)
}

var eventSelectors []cloudtrail.EventSelector
if response.Trail.HasCustomEventSelectors != nil && *response.Trail.HasCustomEventSelectors {
output, err := a.client.GetEventSelectors(a.Context(), &api.GetEventSelectorsInput{
Expand Down Expand Up @@ -141,14 +146,15 @@ func (a *adapter) adaptTrail(info types.TrailInfo) (*cloudtrail.Trail, error) {
}

return &cloudtrail.Trail{
Metadata: metadata,
Name: name,
EnableLogFileValidation: defsecTypes.Bool(response.Trail.LogFileValidationEnabled != nil && *response.Trail.LogFileValidationEnabled, metadata),
IsMultiRegion: defsecTypes.Bool(response.Trail.IsMultiRegionTrail != nil && *response.Trail.IsMultiRegionTrail, metadata),
CloudWatchLogsLogGroupArn: cloudWatchLogsArn,
KMSKeyID: defsecTypes.String(kmsKeyId, metadata),
IsLogging: isLogging,
BucketName: defsecTypes.String(bucketName, metadata),
EventSelectors: eventSelectors,
Metadata: metadata,
Name: name,
EnableLogFileValidation: defsecTypes.Bool(response.Trail.LogFileValidationEnabled != nil && *response.Trail.LogFileValidationEnabled, metadata),
IsMultiRegion: defsecTypes.Bool(response.Trail.IsMultiRegionTrail != nil && *response.Trail.IsMultiRegionTrail, metadata),
CloudWatchLogsLogGroupArn: cloudWatchLogsArn,
KMSKeyID: defsecTypes.String(kmsKeyId, metadata),
IsLogging: isLogging,
IncludeGlobalServiceEvents: includeGlobalServiceEvents,
BucketName: defsecTypes.String(bucketName, metadata),
EventSelectors: eventSelectors,
}, nil
}
17 changes: 9 additions & 8 deletions internal/adapters/cloudformation/aws/cloudtrail/trails.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ func getCloudTrails(ctx parser.FileContext) (trails []cloudtrail.Trail) {

for _, r := range cloudtrailResources {
ct := cloudtrail.Trail{
Metadata: r.Metadata(),
Name: r.GetStringProperty("TrailName"),
EnableLogFileValidation: r.GetBoolProperty("EnableLogFileValidation"),
IsMultiRegion: r.GetBoolProperty("IsMultiRegionTrail"),
KMSKeyID: r.GetStringProperty("KmsKeyId"),
CloudWatchLogsLogGroupArn: r.GetStringProperty("CloudWatchLogsLogGroupArn"),
IsLogging: r.GetBoolProperty("IsLogging"),
BucketName: r.GetStringProperty("S3BucketName"),
Metadata: r.Metadata(),
Name: r.GetStringProperty("TrailName"),
EnableLogFileValidation: r.GetBoolProperty("EnableLogFileValidation"),
IsMultiRegion: r.GetBoolProperty("IsMultiRegionTrail"),
KMSKeyID: r.GetStringProperty("KmsKeyId"),
CloudWatchLogsLogGroupArn: r.GetStringProperty("CloudWatchLogsLogGroupArn"),
IsLogging: r.GetBoolProperty("IsLogging"),
IncludeGlobalServiceEvents: r.GetBoolProperty("IncludeGlobalServiceEvents"),
BucketName: r.GetStringProperty("S3BucketName"),
}

trails = append(trails, ct)
Expand Down
19 changes: 10 additions & 9 deletions internal/adapters/terraform/aws/cloudtrail/adapt.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,15 @@ func adaptTrail(resource *terraform.Block) cloudtrail.Trail {
}

return cloudtrail.Trail{
Metadata: resource.GetMetadata(),
Name: nameVal,
EnableLogFileValidation: enableLogFileValidationVal,
IsMultiRegion: isMultiRegionVal,
KMSKeyID: KMSKeyIDVal,
CloudWatchLogsLogGroupArn: resource.GetAttribute("cloud_watch_logs_group_arn").AsStringValueOrDefault("", resource),
IsLogging: resource.GetAttribute("enable_logging").AsBoolValueOrDefault(true, resource),
BucketName: resource.GetAttribute("s3_bucket_name").AsStringValueOrDefault("", resource),
EventSelectors: selectors,
Metadata: resource.GetMetadata(),
Name: nameVal,
EnableLogFileValidation: enableLogFileValidationVal,
IsMultiRegion: isMultiRegionVal,
KMSKeyID: KMSKeyIDVal,
CloudWatchLogsLogGroupArn: resource.GetAttribute("cloud_watch_logs_group_arn").AsStringValueOrDefault("", resource),
IsLogging: resource.GetAttribute("enable_logging").AsBoolValueOrDefault(true, resource),
IncludeGlobalServiceEvents: resource.GetAttribute("include_global_service_events").AsBoolValueOrDefault(true, resource),
BucketName: resource.GetAttribute("s3_bucket_name").AsStringValueOrDefault("", resource),
EventSelectors: selectors,
}
}
35 changes: 19 additions & 16 deletions internal/adapters/terraform/aws/cloudtrail/adapt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,19 @@ func Test_adaptTrail(t *testing.T) {
s3_bucket_name = "abcdefgh"
cloud_watch_logs_group_arn = "abc"
enable_logging = false
include_global_service_events = false
}
`,
expected: cloudtrail.Trail{
Metadata: defsecTypes.NewTestMetadata(),
Name: defsecTypes.String("example", defsecTypes.NewTestMetadata()),
EnableLogFileValidation: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()),
IsMultiRegion: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()),
KMSKeyID: defsecTypes.String("kms-key", defsecTypes.NewTestMetadata()),
CloudWatchLogsLogGroupArn: defsecTypes.String("abc", defsecTypes.NewTestMetadata()),
IsLogging: defsecTypes.Bool(false, defsecTypes.NewTestMetadata()),
BucketName: defsecTypes.String("abcdefgh", defsecTypes.NewTestMetadata()),
Metadata: defsecTypes.NewTestMetadata(),
Name: defsecTypes.String("example", defsecTypes.NewTestMetadata()),
EnableLogFileValidation: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()),
IsMultiRegion: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()),
KMSKeyID: defsecTypes.String("kms-key", defsecTypes.NewTestMetadata()),
CloudWatchLogsLogGroupArn: defsecTypes.String("abc", defsecTypes.NewTestMetadata()),
IsLogging: defsecTypes.Bool(false, defsecTypes.NewTestMetadata()),
IncludeGlobalServiceEvents: defsecTypes.Bool(false, defsecTypes.NewTestMetadata()),
BucketName: defsecTypes.String("abcdefgh", defsecTypes.NewTestMetadata()),
},
},
{
Expand All @@ -52,14 +54,15 @@ func Test_adaptTrail(t *testing.T) {
}
`,
expected: cloudtrail.Trail{
Metadata: defsecTypes.NewTestMetadata(),
Name: defsecTypes.String("", defsecTypes.NewTestMetadata()),
EnableLogFileValidation: defsecTypes.Bool(false, defsecTypes.NewTestMetadata()),
IsMultiRegion: defsecTypes.Bool(false, defsecTypes.NewTestMetadata()),
KMSKeyID: defsecTypes.String("", defsecTypes.NewTestMetadata()),
BucketName: defsecTypes.String("", defsecTypes.NewTestMetadata()),
CloudWatchLogsLogGroupArn: defsecTypes.String("", defsecTypes.NewTestMetadata()),
IsLogging: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()),
Metadata: defsecTypes.NewTestMetadata(),
Name: defsecTypes.String("", defsecTypes.NewTestMetadata()),
EnableLogFileValidation: defsecTypes.Bool(false, defsecTypes.NewTestMetadata()),
IsMultiRegion: defsecTypes.Bool(false, defsecTypes.NewTestMetadata()),
KMSKeyID: defsecTypes.String("", defsecTypes.NewTestMetadata()),
BucketName: defsecTypes.String("", defsecTypes.NewTestMetadata()),
CloudWatchLogsLogGroupArn: defsecTypes.String("", defsecTypes.NewTestMetadata()),
IsLogging: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()),
IncludeGlobalServiceEvents: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()),
},
},
}
Expand Down
19 changes: 10 additions & 9 deletions pkg/providers/aws/cloudtrail/cloudtrail.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@ func (c CloudTrail) MultiRegionTrails() (multiRegionTrails []Trail) {
}

type Trail struct {
Metadata defsecTypes.Metadata
Name defsecTypes.StringValue
EnableLogFileValidation defsecTypes.BoolValue
IsMultiRegion defsecTypes.BoolValue
KMSKeyID defsecTypes.StringValue
CloudWatchLogsLogGroupArn defsecTypes.StringValue
IsLogging defsecTypes.BoolValue
BucketName defsecTypes.StringValue
EventSelectors []EventSelector
Metadata defsecTypes.Metadata
Name defsecTypes.StringValue
EnableLogFileValidation defsecTypes.BoolValue
IsMultiRegion defsecTypes.BoolValue
KMSKeyID defsecTypes.StringValue
CloudWatchLogsLogGroupArn defsecTypes.StringValue
IsLogging defsecTypes.BoolValue
IncludeGlobalServiceEvents defsecTypes.BoolValue
BucketName defsecTypes.StringValue
EventSelectors []EventSelector
}

type EventSelector struct {
Expand Down
4 changes: 4 additions & 0 deletions pkg/rego/schemas/cloud.json
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,10 @@
"$ref": "#/definitions/github.com.aquasecurity.defsec.pkg.providers.aws.cloudtrail.EventSelector"
}
},
"includeglobalserviceevents": {
"type": "object",
"$ref": "#/definitions/github.com.aquasecurity.defsec.pkg.types.BoolValue"
},
"islogging": {
"type": "object",
"$ref": "#/definitions/github.com.aquasecurity.defsec.pkg.types.BoolValue"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package cloudtrail

var cloudFormationIncludeGlobalServiceEventsGoodExamples = []string{
`---
Resources:
GoodExampleTrail:
Type: AWS::CloudTrail::Trail
Properties:
IncludeGlobalServiceEvents: true
S3BucketName: "my-bucket"
TrailName: "Cloudtrail"
`,
}

var cloudFormationIncludeGlobalServiceEventsBadExamples = []string{
`---
Resources:
BadExampleTrail:
Type: AWS::CloudTrail::Trail
Properties:
IncludeGlobalServiceEvents: false
S3BucketName: "my-bucket"
TrailName: "Cloudtrail"
`,
}

var cloudFormationIncludeGlobalServiceEventsLinks = []string{}

var cloudFormationIncludeGlobalServiceEventsRemediationMarkdown = ``
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package cloudtrail

import (
"github.com/aquasecurity/defsec/internal/rules"
"github.com/aquasecurity/defsec/pkg/providers"
"github.com/aquasecurity/defsec/pkg/scan"
"github.com/aquasecurity/defsec/pkg/severity"
"github.com/aquasecurity/defsec/pkg/state"
)

var checkIncludeGlobalServiceEvents = rules.Register(
scan.Rule{
AVDID: "AVD-AWS-0343",
Provider: providers.AWSProvider,
Service: "cloudtrail",
ShortCode: "include-global-service-events",
Summary: "Specifies whether Cloudtrail is publishing events from global services such as IAM to the log files. ",
Impact: "Events from global services such as IAM are not being published to the log files",
Resolution: "Enable include global service events for Cloudtrail",
Explanation: `Include Global Service Events is a default value for Cloudtrail and it publishes events from global services that are not region specific such as IAM, STS and CloudFront. It is feasible that a rogue actor compromising an AWS account might want to disable this field to remove trace of their actions.`,
Links: []string{
"https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-concepts.html#cloudtrail-concepts-global-service-events",
},
Terraform: &scan.EngineMetadata{
GoodExamples: terraformIncludeGlobalServiceEventsGoodExamples,
BadExamples: terraformIncludeGlobalServiceEventsBadExamples,
Links: terraformIncludeGlobalServiceEventsLinks,
RemediationMarkdown: terraformIncludeGlobalServiceEventsRemediationMarkdown,
},
CloudFormation: &scan.EngineMetadata{
GoodExamples: cloudFormationIncludeGlobalServiceEventsGoodExamples,
BadExamples: cloudFormationIncludeGlobalServiceEventsBadExamples,
Links: cloudFormationIncludeGlobalServiceEventsLinks,
RemediationMarkdown: cloudFormationIncludeGlobalServiceEventsRemediationMarkdown,
},
Severity: severity.Medium,
},
func(s *state.State) (results scan.Results) {
for _, trail := range s.AWS.CloudTrail.Trails {
if trail.IncludeGlobalServiceEvents.IsFalse() {
results.Add(
"Trail is not publishing events from global services such as IAM to the log files.",
trail.IncludeGlobalServiceEvents,
)
} else {
results.AddPassed(&trail)
}
}
return
},
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package cloudtrail

var terraformIncludeGlobalServiceEventsGoodExamples = []string{
`
resource "aws_cloudtrail" "good_example" {
include_global_service_events = true
s3_bucket_name = "abcdefgh"
}
`,
}

var terraformIncludeGlobalServiceEventsBadExamples = []string{
`
resource "aws_cloudtrail" "bad_example" {
include_global_service_events = false
s3_bucket_name = "abcdefgh"
}
`,
}

var terraformIncludeGlobalServiceEventsLinks = []string{
`https://registry.terraform.io/providers/rgeraskin/aws2/latest/docs/resources/cloudtrail#include_global_service_events`,
}

var terraformIncludeGlobalServiceEventsRemediationMarkdown = ``
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package cloudtrail

import (
"testing"

defsecTypes "github.com/aquasecurity/defsec/pkg/types"

"github.com/aquasecurity/defsec/pkg/state"

"github.com/aquasecurity/defsec/pkg/providers/aws/cloudtrail"
"github.com/aquasecurity/defsec/pkg/scan"

"github.com/stretchr/testify/assert"
)

func TestCheckIncludeGlobalServiceEvents(t *testing.T) {
tests := []struct {
name string
input cloudtrail.CloudTrail
expected bool
}{
{
name: "AWS CloudTrail without include global service events",
input: cloudtrail.CloudTrail{
Trails: []cloudtrail.Trail{
{
Metadata: defsecTypes.NewTestMetadata(),
IncludeGlobalServiceEvents: defsecTypes.Bool(false, defsecTypes.NewTestMetadata()),
},
},
},
expected: true,
},
{
name: "AWS CloudTrail with include global service events",
input: cloudtrail.CloudTrail{
Trails: []cloudtrail.Trail{
{
Metadata: defsecTypes.NewTestMetadata(),
IncludeGlobalServiceEvents: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()),
},
},
},
expected: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var testState state.State
testState.AWS.CloudTrail = test.input
results := checkIncludeGlobalServiceEvents.Evaluate(&testState)
var found bool
for _, result := range results {
if result.Status() == scan.StatusFailed && result.Rule().LongID() == checkIncludeGlobalServiceEvents.Rule().LongID() {
found = true
}
}
if test.expected {
assert.True(t, found, "Rule should have been found")
} else {
assert.False(t, found, "Rule should not have been found")
}
})
}
}

0 comments on commit acd0447

Please sign in to comment.