-
-
Notifications
You must be signed in to change notification settings - Fork 353
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement support for GuardDuty Detectors #320
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
package aws | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
awsgo "github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/aws/session" | ||
"github.com/aws/aws-sdk-go/service/guardduty" | ||
"github.com/gruntwork-io/cloud-nuke/config" | ||
"github.com/gruntwork-io/cloud-nuke/logging" | ||
"github.com/gruntwork-io/go-commons/errors" | ||
) | ||
|
||
type DetectorOutputWithID struct { | ||
ID *string | ||
Output *guardduty.GetDetectorOutput | ||
} | ||
|
||
func getAllGuardDutyDetectors(session *session.Session, excludeAfter time.Time, configObj config.Config, batchSize int) ([]string, error) { | ||
svc := guardduty.New(session) | ||
|
||
var result []*string | ||
var annotatedDetectors []*DetectorOutputWithID | ||
var detectorIdsToInclude []string | ||
|
||
var next *string = nil | ||
for { | ||
list, err := svc.ListDetectors(&guardduty.ListDetectorsInput{ | ||
MaxResults: awsgo.Int64(int64(batchSize)), | ||
NextToken: next, | ||
}) | ||
if err != nil { | ||
return nil, errors.WithStackTrace(err) | ||
} | ||
|
||
result = append(result, list.DetectorIds...) | ||
if list.NextToken == nil || len(list.DetectorIds) == 0 { | ||
break | ||
} | ||
next = list.NextToken | ||
} | ||
|
||
// Due to the ListDetectors method only returning the Ids of found detectors, we need to further enrich our data about | ||
// each detector with a separate call to GetDetector for metadata including when it was created, which we need to make the | ||
// determination about whether or not the given detector should be included | ||
for _, detectorId := range result { | ||
|
||
detector, getDetectorErr := svc.GetDetector(&guardduty.GetDetectorInput{ | ||
DetectorId: detectorId, | ||
}) | ||
|
||
if getDetectorErr != nil { | ||
return nil, errors.WithStackTrace(getDetectorErr) | ||
} | ||
|
||
detectorOutputWithID := &DetectorOutputWithID{ | ||
ID: detectorId, | ||
Output: detector, | ||
} | ||
|
||
annotatedDetectors = append(annotatedDetectors, detectorOutputWithID) | ||
} | ||
|
||
for _, detector := range annotatedDetectors { | ||
if shouldIncludeDetector(detector, excludeAfter, configObj) { | ||
detectorIdsToInclude = append(detectorIdsToInclude, aws.StringValue(detector.ID)) | ||
} | ||
} | ||
|
||
return detectorIdsToInclude, nil | ||
} | ||
|
||
func shouldIncludeDetector(detector *DetectorOutputWithID, excludeAfter time.Time, configObj config.Config) bool { | ||
if detector == nil { | ||
return false | ||
} | ||
|
||
detectorCreatedAt := aws.StringValue(detector.Output.CreatedAt) | ||
|
||
createdAtDateTime, err := time.Parse(time.RFC3339, detectorCreatedAt) | ||
if err != nil { | ||
logging.Logger.Warnf("Could not parse createdAt timestamp (%s) of GuardDuty detector %s. Excluding from delete.", detectorCreatedAt, awsgo.StringValue(detector.ID)) | ||
} | ||
|
||
if excludeAfter.Before(createdAtDateTime) { | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
func nukeAllGuardDutyDetectors(session *session.Session, detectorIds []string) error { | ||
svc := guardduty.New(session) | ||
|
||
if len(detectorIds) == 0 { | ||
logging.Logger.Infof("No GuardDuty detectors to nuke in region %s", *session.Config.Region) | ||
|
||
return nil | ||
} | ||
|
||
logging.Logger.Infof("Deleting all GuardDuty detectors in region %s", *session.Config.Region) | ||
|
||
deletedIds := []string{} | ||
|
||
for _, detectorId := range detectorIds { | ||
params := &guardduty.DeleteDetectorInput{ | ||
DetectorId: aws.String(detectorId), | ||
} | ||
|
||
_, err := svc.DeleteDetector(params) | ||
|
||
if err != nil { | ||
logging.Logger.Errorf("[Failed] %s: %s", detectorId, err) | ||
} else { | ||
deletedIds = append(deletedIds, detectorId) | ||
logging.Logger.Infof("Deleted GuardDuty detector: %s", detectorId) | ||
} | ||
} | ||
Comment on lines
+106
to
+119
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is probably ok since AFAIK there can only be a limited number of detectors per region, but recommend using the same pattern as NAT Gateway (https://github.com/gruntwork-io/cloud-nuke/blob/master/aws/nat_gateway.go#L63) to implement concurrent deletion of the detectors. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, also saw this pattern in the cloudwatch_loggroup that you added! I'll file a ticket to do this in a follow-up PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Filed #324 to track. |
||
|
||
logging.Logger.Infof("[OK] %d GuardDuty Detector(s) deleted in %s", len(deletedIds), *session.Config.Region) | ||
|
||
return nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched to https://github.com/mvdan/gofumpt for auto-formatting, so that's why some of these vars and extra lines are getting cleaned up.