forked from chef-boneyard/inspec-aws-old
-
Notifications
You must be signed in to change notification settings - Fork 1
Open
Labels
Description
Test Cloud Trails Configurations
In support for over 19 cis aws foundation controls
Support tests such as
describe aws_cloudtrail_trails.where(is_multi_region_trail: true) do
it { should exist }
end
describe aws_cloudtrail_trails.where(log_file_validation_enabled: false) do
it { should_not exist }
end
aws_cloudtrail_trails.entries.each do |trail|
describe trail do
its('cloud_watch_logs_log_group_arn') { should_not be_nil }
its('status.latest_cloud_watch_logs_delivery_time') { should cmp > Time.now - 86400 }
end
end
AWS CLI
aws cloudtrail describe-trails
{
"trailList": [
{
"IncludeGlobalServiceEvents": true,
"Name": "test_tail",
"TrailARN": "arn:aws:cloudtrail:us-east-1:484747447281:trail/test_tail",
"LogFileValidationEnabled": true,
"IsMultiRegionTrail": true,
"HasCustomEventSelectors": false,
"S3BucketName": "aws-demo-s3-bucket-6731201fbe26c7f4",
"CloudWatchLogsRoleArn": "arn:aws:iam::484747447281:role/CloudTrail_CloudWatchLogs_Role",
"CloudWatchLogsLogGroupArn": "arn:aws:logs:us-east-1:484747447281:log-group:test:*",
"HomeRegion": "us-east-1"
},
{
"IncludeGlobalServiceEvents": true,
"Name": "test_tail2",
"TrailARN": "arn:aws:cloudtrail:us-east-1:484747447281:trail/test_tail2",
"LogFileValidationEnabled": true,
"IsMultiRegionTrail": false,
"HasCustomEventSelectors": false,
"S3BucketName": "aws-demo-s3-bucket-f8a94d40a2fa19ee",
"HomeRegion": "us-east-1"
}
]
}
Code Suggestion:
require_relative '_aws'
class AwsCloudTrailTrails < Inspec.resource(1)
name 'aws_cloudtrail_trails'
desc 'Verifies settings for AWS IAM users'
example '
describe aws_cloudtrail_trails.where(is_multi_region_trail: false) do
it { should_not exist }
end
describe aws_cloudtrail_trails.where(include_global_service_events: false) do
it { should_not exist }
end
'
filter = FilterTable.create
filter.add_accessor(:where)
.add_accessor(:entries)
.add(:exists?) { |x| !x.entries.empty? }
.add(:name, field: :name)
.add(:s3_bucket_name, field: :s3_bucket_name)
.add(:s3_key_prefix, field: :s3_key_prefix)
.add(:sns_topic_name, field: :sns_topic_name)
.add(:sns_topic_arn, field: :sns_topic_arn)
.add(:include_global_service_events, field: :include_global_service_events)
.add(:is_multi_region_trail, field: :is_multi_region_trail)
.add(:home_region, field: :home_region)
.add(:trail_arn, field: :trail_arn)
.add(:log_file_validation_enabled, field: :log_file_validation_enabled)
.add(:cloud_watch_logs_log_group_arn, field: :cloud_watch_logs_log_group_arn)
.add(:cloud_watch_logs_role_arn, field: :cloud_watch_logs_role_arn)
.add(:kms_key_id, field: :kms_key_id)
.add(:has_custom_event_selectors, field: :has_custom_event_selectors)
filter.connect(self, :collect_trail_details)
# No resource params => no overridden constructor
# AWS API only offers filtering on path prefix;
# little other opportunity for server-side filtering.
def collect_trail_details
backend = Backend.create
trails = backend.list_trails.trail_list.map(&:to_h)
trails.each do |trail|
begin
status = Hashie::Mash.new(backend.trail_status(name: trail[:name]).to_h)
trail[:status] = status
rescue Aws::IAM::Errors::NoSuchEntity
user[:status] = {}
end
end
trails
end
def to_s
'CloudTrails Trails'
end
# Entry cooker. Needs discussion.
# def users
# end
#===========================================================================#
# Backend Implementation
#===========================================================================#
class Backend
#=====================================================#
# Concrete Implementation
#=====================================================#
# Uses AWS API to really talk to AWS
class AwsClientApi < Backend
# TODO: delegate this out
def list_trails(query = {})
AWSConnection.new.cloudtrail_client.describe_trails(query)
end
def trail_status(query)
AWSConnection.new.cloudtrail_client.get_trail_status(query)
end
end
#=====================================================#
# Factory Interface
#=====================================================#
# TODO: move this to a mix-in
DEFAULT_BACKEND = AwsClientApi
@selected_backend = DEFAULT_BACKEND
def self.create
@selected_backend.new
end
def self.select(klass)
@selected_backend = klass
end
end
end