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