foundation-logging.yaml
---

Logging & Alerting CloudFormation Deployment

This CloudFormation template will setup and deploy a logging and alerting framework for this account.

AWSTemplateFormatVersion: '2010-09-09' Description: "UITS Account Foundation: Logging & Monitoring Capabilities" Metadata: Stack: Value: '0' VersionDate: Value: '20160510' Identifier: Value: template-logging Input: Description: CloudTrail bucket name Output: Description: Outputs ID of all deployed resources AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Tagging and Cost Management Parameters: - ServiceTag - EnvironmentTag - ContactNetidTag - AccountNumberTag - TicketNumberTag ParameterLabels: ServiceTag: default: "Service Name:" EnvironmentTag: default: 'Environment Type:' ContactNetidTag: default: 'Contact NetID:' AccountNumberTag: default: 'Financial Account Number:' TicketNumberTag: default: 'Ticket Number:'

Parameters

These are the input parameters for this template. All of these parameters must be supplied for this template to be deployed.

Parameters:

Notification Email Address

This address gets subscribed to the notification SNS topic that is created.

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' pCloudTrailLogBucket: Description: The name of the external S3 bucket to log CloudTrail events to. For UA this is in the root account. Type: String Default: 'uaz-cloudtrail-bucket' pAccountType: Description: Is this a Production account or a Non-Production account? Type: String AllowedValues: - Production - Non-Production Default: 'Non-Production'

Tags

The following tags are applied to all resources created by this template.

ServiceTag: Type: String Description: Exact name of the Service as defined in the service catalog. EnvironmentTag: Type: String Description: Used to distinguish between development, test, production,etc. environment types. AllowedValues: [dev, tst, prd, trn, stg, cfg, sup, rpt] Default: dev ContactNetidTag: Type: String Description: Used to identify the netid of the person most familiar with the usage of the resource. AccountNumberTag: Type: String Description: Identifies the financial system account number. TicketNumberTag: Type: String Description: Used to identify the Jira, Cherwell, or other ticketing system ticket number to link to more information about the need for the resource.

Conditions

A few conditions to track if this is being deployed in GovCloud, and if this region supports glacier or not.

Conditions: IsGovCloud: !Equals [!Ref "AWS::Region", 'us-gov-west-1'] SupportsGlacier: !Equals [!Ref pSupportsGlacier, 'true'] IsProdAccount: !Equals [!Ref pAccountType, 'Production']

Resources

Resources:

SNS Topic For Notifications

This creates an SNS topic which will receive notifications for the various alerts and triggers set up in this template. An initial email address (passed in via parameters above) is set as a subscriber.

rSecurityAlarmTopic: Type: AWS::SNS::Topic Properties: Subscription: - Endpoint: !Ref pNotifyEmail Protocol: email

SNS Topic For Lambda Alarm Triggering

This creates an SNS topic which will receive notifications for the various alerts and triggers set up in this template. The lambda-alarm-logs lambda function will then subscribe to this topic, enrich the alarms with log details, and publish a new message to the SNS rSecurityAlarmTopic

rSecurityLambdaTopic: Type: AWS::SNS::Topic

Logging S3 Bucket

Creates an S3 bucket to hold long-term logging data for the account. The first 90 days are stored as S3 standard, then after 90 days the data is transitioned to Glacier if this region supports it, or S3 Infrequent Access if it doesn't.

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 Tags: - Key: Name Value: !Sub "${AWS::StackName} S3 Logging Bucket" - Key: service Value: !Ref ServiceTag - Key: environment Value: !Ref EnvironmentTag - Key: contactnetid Value: !Ref ContactNetidTag - Key: accountnumber Value: !Ref AccountNumberTag - Key: ticketnumber Value: !Ref TicketNumberTag

Policy for S3 Logging Bucket

This is the policy attached to the Logging Bucket. It Enforces the following:

  • Requires HTTPS Connections for access
  • Restricts the ability to delete objects
  • Requires all uploads be set for Server Side Encryption (SSE)
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:${AwsBase}:s3:::${rArchiveLogsBucket}/* - { AwsBase: !If [IsGovCloud, aws-us-gov, aws] } Condition: Bool: aws:SecureTransport: false - Sid: Restrict Delete* Actions Action: s3:Delete* Effect: Deny Principal: "*" Resource: - !Sub - arn:${AwsBase}:s3:::${rArchiveLogsBucket}/* - { AwsBase: !If [IsGovCloud, aws-us-gov, aws] } - Sid: DenyUnEncryptedObjectUploads Effect: Deny Principal: "*" Action: s3:PutObject Resource: - !Sub - arn:${AwsBase}:s3:::${rArchiveLogsBucket}/* - { AwsBase: !If [IsGovCloud, aws-us-gov, aws] } Condition: StringNotEquals: s3:x-amz-server-side-encryption: AES256

CloudTrail Activation

Enables CloudTrail for this account, and logs events to the CloudTrail S3 bucket.

rCloudTrailLoggingLocal: Type: AWS::CloudTrail::Trail Properties: S3BucketName: !Ref pCloudTrailLogBucket IsLogging: true EnableLogFileValidation: true IncludeGlobalServiceEvents: true IsMultiRegionTrail: true CloudWatchLogsLogGroupArn: !GetAtt rCloudTrailLogGroup.Arn CloudWatchLogsRoleArn: !GetAtt rCloudWatchLogsRole.Arn Tags: - Key: Name Value: !Sub "${AWS::StackName} CloudTrail" - Key: service Value: !Ref ServiceTag - Key: environment Value: !Ref EnvironmentTag - Key: contactnetid Value: !Ref ContactNetidTag - Key: accountnumber Value: !Ref AccountNumberTag - Key: ticketnumber Value: !Ref TicketNumberTag

CloudWatch Logs Role

This role allows the CloudTrail service to write CloudWatch Logs events.

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:${AwsBase}:logs:${AWS::Region}:${AWS::AccountId}:log-group:${rCloudTrailLogGroup}:log-stream:* - { AwsBase: !If [IsGovCloud, aws-us-gov, aws] } - Sid: AWSCloudTrailPutLogEvents20141101 Effect: Allow Action: - logs:PutLogEvents Resource: - !Sub - arn:${AwsBase}:logs:${AWS::Region}:${AWS::AccountId}:log-group:${rCloudTrailLogGroup}:log-stream:* - { AwsBase: !If [IsGovCloud, aws-us-gov, aws] }

CloudTrail Log Group

Creates a CloudTrail Log group with a retantion of 90 days. This log group is referenced above where we turned on CloudTrail for this account.

rCloudTrailLogGroup: Type: AWS::Logs::LogGroup Properties: RetentionInDays: '90'

IAM Changes Filter

Monitor changes to IAM access.

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'

Network ACL Changes Filter

Monitor changes to ACLs.

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'

Network ACL Alarm

Send to SNS rSecurityLambdaTopic topic whenever Network ACLs are changed.

rNetworkAclChangesAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmName: CloudTrailNetworkAclChanges AlarmDescription: Alarms when an API call is made to create, update or delete a Network ACL. AlarmActions: - !Ref rSecurityLambdaTopic MetricName: NetworkAclEventCount Namespace: CloudTrailMetrics ComparisonOperator: GreaterThanOrEqualToThreshold EvaluationPeriods: '1' Period: '300' Statistic: Sum Threshold: '1'

Security Group Metric Filter

Track changes to Security Groups.

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'

Security Group Alarm

Send to SNS rSecurityLambdaTopic topic whenever Security Groups are changed.

rSecurityGroupChangesAlarm: Type: AWS::CloudWatch::Alarm Condition: IsProdAccount Properties: AlarmName: CloudTrailSecurityGroupChanges AlarmDescription: Alarms when an API call is made to create, update or delete a Security Group. AlarmActions: - !Ref rSecurityLambdaTopic MetricName: SecurityGroupEventCount Namespace: CloudTrailMetrics ComparisonOperator: GreaterThanOrEqualToThreshold EvaluationPeriods: '1' Period: '300' Statistic: Sum Threshold: '1'

Root User Metric Filter

Track Root User activity.

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'

Root User Alarm

Send to SNS rSecurityLambdaTopic topic whenever root user activity is detected.

rRootActivityAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmName: CloudTrailIAMRootActivity AlarmDescription: Root user activity detected! AlarmActions: - !Ref rSecurityLambdaTopic MetricName: RootUserPolicyEventCount Namespace: CloudTrailMetrics ComparisonOperator: GreaterThanOrEqualToThreshold EvaluationPeriods: '1' Period: '300' Statistic: Sum Threshold: '1'

Unauthorized Metric Filter

Track any unauthorized activity.

rUnauthorizedAttempts: Type: AWS::Logs::MetricFilter Properties: LogGroupName: !Ref rCloudTrailLogGroup FilterPattern: "{($.errorCode=AccessDenied)||($.errorCode=UnauthorizedOperation)}" MetricTransformations: - MetricNamespace: CloudTrailMetrics MetricName: UnauthorizedAttemptCount MetricValue: '1'

Unauthorized Alarm

Send to SNS rSecurityLambdaTopic topic whenever unauthorized activity is detected.

rUnauthorizedAttemptAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmName: IAMUnauthorizedActionsAttempted AlarmDescription: Multiple unauthorized actions or logins attempted! AlarmActions: - !Ref rSecurityLambdaTopic MetricName: UnauthorizedAttemptCount Namespace: CloudTrailMetrics ComparisonOperator: GreaterThanOrEqualToThreshold EvaluationPeriods: '1' Period: '300' Statistic: Sum Threshold: '5'

IAM Policy Alarm

Send to SNS rSecurityLambdaTopic topic whenever IAM Policy changes are detected.

rIAMPolicyChangesAlarm: Type: AWS::CloudWatch::Alarm Condition: IsProdAccount Properties: AlarmName: DetectIAMPolicyChanges AlarmDescription: IAM Configuration changes detected! AlarmActions: - !Ref rSecurityLambdaTopic MetricName: IAMPolicyEventCount Namespace: CloudTrailMetrics ComparisonOperator: GreaterThanOrEqualToThreshold EvaluationPeriods: '1' Period: '300' Statistic: Sum Threshold: '1'

Access Keys Alarm

Send to SNS rSecurityLambdaTopic topic whenever new Access Keys are created.

rIAMCreateAccessKeyAlarm: Type: AWS::CloudWatch::Alarm Condition: IsProdAccount Properties: AlarmName: DetectAccessKeyCreation AlarmDescription: 'Warning: New IAM access key was created. Please be sure this action was neccessary.' AlarmActions: - !Ref rSecurityLambdaTopic MetricName: NewAccessKeyCreated Namespace: CloudTrailMetrics ComparisonOperator: GreaterThanOrEqualToThreshold EvaluationPeriods: '1' Period: '300' Statistic: Sum Threshold: '1'

Access Keys Metric Filter

Track whenever Access Keys are created.

rIAMCreateAccessKey: Type: AWS::Logs::MetricFilter Properties: LogGroupName: !Ref rCloudTrailLogGroup FilterPattern: "{($.eventName=CreateAccessKey)}" MetricTransformations: - MetricNamespace: CloudTrailMetrics MetricName: NewAccessKeyCreated MetricValue: '1'

CloudTrail Change Alarm

Send to SNS rSecurityLambdaTopic topic whenever changes to CloudTrail are detected.

rCloudTrailChangeAlarm: Type: AWS::CloudWatch::Alarm Condition: IsProdAccount Properties: AlarmName: DetectCloudTrailChanges AlarmDescription: 'Warning: Changes to CloudTrail log configuration detected in this account' AlarmActions: - !Ref rSecurityLambdaTopic MetricName: CloudTrailChangeCount Namespace: CloudTrailMetrics ComparisonOperator: GreaterThanOrEqualToThreshold EvaluationPeriods: '1' Period: '300' Statistic: Sum Threshold: '1'

CloudTrail Changes Metric Filter

Track any changes to CloudTrail.

rCloudTrailChange: Type: AWS::Logs::MetricFilter Properties: LogGroupName: !Ref rCloudTrailLogGroup FilterPattern: "{($.eventSource=cloudtrail.amazonaws.com)}" MetricTransformations: - MetricNamespace: CloudTrailMetrics MetricName: CloudTrailChangeCount MetricValue: '1'

Outputs

Outputs: rArchiveLogsBucket: Value: !Ref rArchiveLogsBucket Export: Name: !Sub "${AWS::StackName}-archive-logs-bucket" rCloudTrailLogGroup: Value: !Ref rCloudTrailLogGroup Export: Name: !Sub "${AWS::StackName}-logs-group" rSecurityAlarmTopic: Value: !Ref rSecurityAlarmTopic Export: Name: !Sub "${AWS::StackName}-alarm-topic" rSecurityLambdaTopic: Value: !Ref rSecurityLambdaTopic Export: Name: !Sub "${AWS::StackName}-lambda-topic"