AWSTemplateFormatVersion: '2010-09-09' Conditions: IsGovCloud: Fn::Equals: - Ref: AWS::Region - us-gov-west-1 SupportsGlacier: Fn::Equals: - Ref: pSupportsGlacier - 'true' Description: Initializes global resources and logging/monitoring capabilities Metadata: Identifier: Value: template-logging Input: Description: CloudTrail bucket name Output: Description: Outputs ID of all deployed resources Stack: Value: '0' VersionDate: Value: '20160510' Outputs: rArchiveLogsBucket: Value: Ref: rArchiveLogsBucket rSecurityAlarmTopic: Value: Ref: rSecurityAlarmTopic Parameters: pNotifyEmail: Default: '' Description: Notification email for security events Type: String pSupportsGlacier: Default: 'true' Description: Determines hether this region supports Glacier (passed in from Main template) Type: String Resources: rArchiveLogsBucket: DeletionPolicy: Retain Properties: AccessControl: LogDeliveryWrite LifecycleConfiguration: Rules: - ExpirationInDays: '2555' Id: Transition90daysRetain7yrs Status: Enabled Transition: StorageClass: Fn::If: - SupportsGlacier - GLACIER - STANDARD_IA TransitionInDays: '90' VersioningConfiguration: Status: Enabled Type: AWS::S3::Bucket rArchiveLogsBucketPolicy: DependsOn: rArchiveLogsBucket Properties: Bucket: Ref: rArchiveLogsBucket PolicyDocument: Statement: - Action: s3:* Condition: Bool: aws:SecureTransport: false Effect: Deny Principal: '*' Resource: - Fn::Join: - '' - - 'arn:' - Fn::If: - IsGovCloud - aws-us-gov - aws - ':s3:::' - Ref: rArchiveLogsBucket - /* Sid: Enforce HTTPS Connections - Action: s3:Delete* Effect: Deny Principal: '*' Resource: - Fn::Join: - '' - - 'arn:' - Fn::If: - IsGovCloud - aws-us-gov - aws - ':s3:::' - Ref: rArchiveLogsBucket - /* Sid: Restrict Delete* Actions - Action: s3:PutObject Condition: StringNotEquals: s3:x-amz-server-side-encryption: AES256 Effect: Deny Principal: '*' Resource: - Fn::Join: - '' - - 'arn:' - Fn::If: - IsGovCloud - aws-us-gov - aws - ':s3:::' - Ref: rArchiveLogsBucket - /* Sid: DenyUnEncryptedObjectUploads Type: AWS::S3::BucketPolicy rCloudTrailBucket: DeletionPolicy: Retain Properties: AccessControl: Private LoggingConfiguration: DestinationBucketName: Ref: rArchiveLogsBucket LogFilePrefix: cloudtraillogs VersioningConfiguration: Status: Enabled Type: AWS::S3::Bucket rCloudTrailChange: Properties: FilterPattern: '{($.eventSource = cloudtrail.amazonaws.com) && (($.eventName != Describe*) && ($.eventName != Get*) && ($.eventName != Lookup*) && ($.eventName != Lookup*))}' LogGroupName: Ref: rCloudTrailLogGroup MetricTransformations: - MetricName: CloudTrailChangeCount MetricNamespace: CloudTrailMetrics MetricValue: '1' Type: AWS::Logs::MetricFilter rCloudTrailChangeAlarm: DependsOn: rIAMCreateAccessKeyAlarm Properties: AlarmActions: - Ref: rSecurityAlarmTopic AlarmDescription: 'Warning: Changes to CloudTrail log configuration detected in this account' AlarmName: DetectCloudTrailChanges ComparisonOperator: GreaterThanOrEqualToThreshold EvaluationPeriods: '1' MetricName: CloudTrailChangeCount Namespace: CloudTrailMetrics Period: '300' Statistic: Sum Threshold: '1' Type: AWS::CloudWatch::Alarm rCloudTrailLogGroup: Properties: RetentionInDays: '90' Type: AWS::Logs::LogGroup rCloudTrailLoggingLocal: DependsOn: rCloudTrailS3Policy Properties: CloudWatchLogsLogGroupArn: Fn::GetAtt: - rCloudTrailLogGroup - Arn CloudWatchLogsRoleArn: Fn::GetAtt: - rCloudWatchLogsRole - Arn EnableLogFileValidation: true IncludeGlobalServiceEvents: true IsLogging: true S3BucketName: Ref: rCloudTrailBucket Type: AWS::CloudTrail::Trail rCloudTrailProfile: DependsOn: rCloudTrailRole Properties: Path: / Roles: - Ref: rCloudTrailRole Type: AWS::IAM::InstanceProfile rCloudTrailRole: Properties: AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Effect: Allow Principal: Service: - ec2.amazonaws.com Path: / Policies: - PolicyDocument: Statement: - Action: - s3:ListBucket Effect: Allow Resource: - Fn::Join: - '' - - 'arn:' - Fn::If: - IsGovCloud - aws-us-gov - aws - ':s3:::' - Ref: rCloudTrailBucket - Action: - s3:PutObject - s3:GetObject Effect: Allow Resource: - Fn::Join: - '' - - 'arn:' - Fn::If: - IsGovCloud - aws-us-gov - aws - ':s3:::' - Ref: rCloudTrailBucket - /* Version: '2012-10-17' PolicyName: cloudtrail-limited-actions Type: AWS::IAM::Role rCloudTrailS3Policy: DependsOn: rCloudTrailBucket Properties: Bucket: Ref: rCloudTrailBucket PolicyDocument: Statement: - Action: s3:GetBucketAcl Effect: Allow Principal: Service: cloudtrail.amazonaws.com Resource: - Fn::Join: - '' - - 'arn:' - Fn::If: - IsGovCloud - aws-us-gov - aws - ':s3:::' - Ref: rCloudTrailBucket Sid: AWSCloudTrailAclCheck20150319 - Action: s3:PutObject Condition: StringEquals: s3:x-amz-acl: bucket-owner-full-control Effect: Allow Principal: Service: cloudtrail.amazonaws.com Resource: - Fn::Join: - '' - - 'arn:' - Fn::If: - IsGovCloud - aws-us-gov - aws - ':s3:::' - Ref: rCloudTrailBucket - /* Sid: AWSCloudTrailWrite20150319 - Action: s3:* Condition: Bool: aws:SecureTransport: false Effect: Deny Principal: '*' Resource: - Fn::Join: - '' - - 'arn:' - Fn::If: - IsGovCloud - aws-us-gov - aws - ':s3:::' - Ref: rCloudTrailBucket - /* Sid: Enforce HTTPS Connections - Action: s3:Delete* Effect: Deny Principal: '*' Resource: - Fn::Join: - '' - - 'arn:' - Fn::If: - IsGovCloud - aws-us-gov - aws - ':s3:::' - Ref: rCloudTrailBucket - /* Sid: Restrict Delete* Actions - Action: s3:PutObject Condition: StringNotEquals: s3:x-amz-server-side-encryption: AES256 Effect: Deny Principal: '*' Resource: - Fn::Join: - '' - - 'arn:' - Fn::If: - IsGovCloud - aws-us-gov - aws - ':s3:::' - Ref: rCloudTrailBucket - /* Sid: DenyUnEncryptedObjectUploads Type: AWS::S3::BucketPolicy rCloudWatchLogsRole: Properties: AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Effect: Allow Principal: Service: - cloudtrail.amazonaws.com Path: / Policies: - PolicyDocument: Statement: - Action: - logs:CreateLogStream Effect: Allow Resource: - Fn::Join: - '' - - 'arn:' - Fn::If: - IsGovCloud - aws-us-gov - aws - ':logs:' - Ref: AWS::Region - ':' - Ref: AWS::AccountId - ':log-group:' - Ref: rCloudTrailLogGroup - :log-stream:* Sid: AWSCloudTrailCreateLogStream20141101 - Action: - logs:PutLogEvents Effect: Allow Resource: - Fn::Join: - '' - - 'arn:' - Fn::If: - IsGovCloud - aws-us-gov - aws - ':logs:' - Ref: AWS::Region - ':' - Ref: AWS::AccountId - ':log-group:' - Ref: rCloudTrailLogGroup - :log-stream:* Sid: AWSCloudTrailPutLogEvents20141101 Version: '2012-10-17' PolicyName: cloudwatchlogsrole Type: AWS::IAM::Role rIAMCreateAccessKey: DependsOn: rIAMCreateAccessKeyAlarm Properties: FilterPattern: '{($.eventName=CreateAccessKey)}' LogGroupName: Ref: rCloudTrailLogGroup MetricTransformations: - MetricName: NewAccessKeyCreated MetricNamespace: CloudTrailMetrics MetricValue: '1' Type: AWS::Logs::MetricFilter rIAMCreateAccessKeyAlarm: DependsOn: rIAMPolicyChangesAlarm Properties: AlarmActions: - Ref: rSecurityAlarmTopic AlarmDescription: 'Warning: New IAM access key was created. Please be sure this action was neccessary.' AlarmName: DetectAccessKeyCreation ComparisonOperator: GreaterThanOrEqualToThreshold EvaluationPeriods: '1' MetricName: NewAccessKeyCreated Namespace: CloudTrailMetrics Period: '300' Statistic: Sum Threshold: '1' Type: AWS::CloudWatch::Alarm rIAMPolicyChangesAlarm: DependsOn: rUnauthorizedAttemptAlarm Properties: AlarmActions: - Ref: rSecurityAlarmTopic AlarmDescription: IAM Configuration changes detected! AlarmName: DetectIAMPolicyChanges ComparisonOperator: GreaterThanOrEqualToThreshold EvaluationPeriods: '1' MetricName: IAMPolicyEventCount Namespace: CloudTrailMetrics Period: '300' Statistic: Sum Threshold: '1' Type: AWS::CloudWatch::Alarm rIAMPolicyChangesMetricFilter: Properties: FilterPattern: '{($.eventName=DeleteGroupPolicy)||($.eventName=DeleteRolePolicy)||($.eventName=DeleteUserPolicy)||($.eventName=PutGroupPolicy)||($.eventName=PutRolePolicy)||($.eventName=PutUserPolicy)||($.eventName=CreatePolicy)||($.eventName=DeletePolicy)||($.eventName=CreatePolicyVersion)||($.eventName=DeletePolicyVersion)||($.eventName=AttachRolePolicy)||($.eventName=DetachRolePolicy)||($.eventName=AttachUserPolicy)||($.eventName=DetachUserPolicy)||($.eventName=AttachGroupPolicy)||($.eventName=DetachGroupPolicy)}' LogGroupName: Ref: rCloudTrailLogGroup MetricTransformations: - MetricName: IAMPolicyEventCount MetricNamespace: CloudTrailMetrics MetricValue: '1' Type: AWS::Logs::MetricFilter rIAMRootActivity: Properties: FilterPattern: '{ $.userIdentity.type = "Root" && $.userIdentity.invokedBy NOT EXISTS && $.eventType != "AwsServiceEvent" }' LogGroupName: Ref: rCloudTrailLogGroup MetricTransformations: - MetricName: RootUserPolicyEventCount MetricNamespace: CloudTrailMetrics MetricValue: '1' Type: AWS::Logs::MetricFilter rNetworkAclChangesAlarm: Properties: AlarmActions: - Ref: rSecurityAlarmTopic AlarmDescription: Alarms when an API call is made to create, update or delete a Network ACL. AlarmName: CloudTrailNetworkAclChanges ComparisonOperator: GreaterThanOrEqualToThreshold EvaluationPeriods: '1' MetricName: NetworkAclEventCount Namespace: CloudTrailMetrics Period: '300' Statistic: Sum Threshold: '1' Type: AWS::CloudWatch::Alarm rNetworkAclChangesMetricFilter: Properties: FilterPattern: '{ ($.eventName = CreateNetworkAcl) || ($.eventName = CreateNetworkAclEntry) || ($.eventName = DeleteNetworkAcl) || ($.eventName = DeleteNetworkAclEntry) || ($.eventName = ReplaceNetworkAclEntry) || ($.eventName = ReplaceNetworkAclAssociation) }' LogGroupName: Ref: rCloudTrailLogGroup MetricTransformations: - MetricName: NetworkAclEventCount MetricNamespace: CloudTrailMetrics MetricValue: '1' Type: AWS::Logs::MetricFilter rRootActivityAlarm: DependsOn: rSecurityGroupChangesAlarm Properties: AlarmActions: - Ref: rSecurityAlarmTopic AlarmDescription: Root user activity detected! AlarmName: CloudTrailIAMRootActivity ComparisonOperator: GreaterThanOrEqualToThreshold EvaluationPeriods: '1' MetricName: RootUserPolicyEventCount Namespace: CloudTrailMetrics Period: '300' Statistic: Sum Threshold: '1' Type: AWS::CloudWatch::Alarm rSecurityAlarmTopic: Properties: Subscription: - Endpoint: Ref: pNotifyEmail Protocol: email Type: AWS::SNS::Topic rSecurityGroupChangesAlarm: DependsOn: rNetworkAclChangesAlarm Properties: AlarmActions: - Ref: rSecurityAlarmTopic AlarmDescription: Alarms when an API call is made to create, update or delete a Security Group. AlarmName: CloudTrailSecurityGroupChanges ComparisonOperator: GreaterThanOrEqualToThreshold EvaluationPeriods: '1' MetricName: SecurityGroupEventCount Namespace: CloudTrailMetrics Period: '300' Statistic: Sum Threshold: '1' Type: AWS::CloudWatch::Alarm rSecurityGroupChangesMetricFilter: Properties: FilterPattern: '{ ($.eventName = AuthorizeSecurityGroupIngress) || ($.eventName = AuthorizeSecurityGroupEgress) || ($.eventName = RevokeSecurityGroupIngress) || ($.eventName = RevokeSecurityGroupEgress) || ($.eventName = CreateSecurityGroup) || ($.eventName = DeleteSecurityGroup) }' LogGroupName: Ref: rCloudTrailLogGroup MetricTransformations: - MetricName: SecurityGroupEventCount MetricNamespace: CloudTrailMetrics MetricValue: '1' Type: AWS::Logs::MetricFilter rUnauthorizedAttemptAlarm: DependsOn: rRootActivityAlarm Properties: AlarmActions: - Ref: rSecurityAlarmTopic AlarmDescription: Multiple unauthorized actions or logins attempted! AlarmName: IAMUnauthorizedActionsAttempted ComparisonOperator: GreaterThanOrEqualToThreshold EvaluationPeriods: '1' MetricName: UnauthorizedAttemptCount Namespace: CloudTrailMetrics Period: '300' Statistic: Sum Threshold: '5' Type: AWS::CloudWatch::Alarm rUnauthorizedAttempts: Properties: FilterPattern: '{($.errorCode=AccessDenied)||($.errorCode=UnauthorizedOperation)}' LogGroupName: Ref: rCloudTrailLogGroup MetricTransformations: - MetricName: UnauthorizedAttemptCount MetricNamespace: CloudTrailMetrics MetricValue: '1' Type: AWS::Logs::MetricFilter