cia-dist-cloudformation/src/main/resources/cia-dist-cloudformation.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: CIA CloudFormation runtime
Parameters:
WebServerInstanceType:
Description: WebServer Server EC2 instance type
Type: String
Default: c7g.2xlarge
AllowedValues:
- t4g.micro
- t4g.small
- t4g.medium
- t4g.large
- t4g.xlarge
- t4g.2xlarge
- m6g.large
- m6g.xlarge
- m6g.2xlarge
- c6g.8xlarge
- c6g.4xlarge
- m6g.12xlarge
- m6g.16xlarge
- c6g.large
- c6g.xlarge
- c6g.2xlarge
- c6g.4xlarge
- c6g.8xlarge
- c7g.large
- c7g.xlarge
- c7g.2xlarge
- c7g.4xlarge
- c7g.8xlarge
- c7g.12xlarge
- c7g.16xlarge
ConstraintDescription: must be a valid EC2 instance type.
WebServerAmi:
Description: Web server ami ubuntu arm 20.04, recommend ebs encrypted ami based on ubuntu hvm-ssd server
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/canonical/ubuntu/server/20.04/stable/current/arm64/hvm/ebs-gp2/ami-id
WebServerCount:
Description: Number of EC2 instances to launch for the WebServer server
Type: Number
Default: '1'
WebServerAppLogAppender:
Description: App log output appender console/elasticsearch/cloudwatch/awslogs/kinesis
Type: String
Default: cloudwatch
DomainNamePrefix:
Description: Domain name prefix (optional), used to setup route53 dns
Type: String
Default: cia
DomainName:
Description: Domain name (optional), used to setup route53 dns
Type: String
Default: hack23.com
DBEncryptionKmsAlias:
Description: The alias for Key Management Service encryption key alias
Type: String
Default: ''
DBName:
Default: pgdb
Description: The database name
Type: String
MinLength: '1'
MaxLength: '8'
AllowedPattern: '[a-zA-Z][a-zA-Z0-9]*'
ConstraintDescription: must begin with a letter and contain only alphanumeric characters.
DBUsername:
Default: root
NoEcho: 'true'
Description: The database admin account username
Type: String
MinLength: '1'
MaxLength: '16'
AllowedPattern: '[a-zA-Z][a-zA-Z0-9]*'
ConstraintDescription: must begin with a letter and contain only alphanumeric characters.
DBPassword:
Default: root1234
NoEcho: 'true'
Description: The database admin account password
Type: String
MinLength: '8'
RotationLambda:
Description: arn of RotationLambda used to rotate password
Default: ''
Type: String
DBSnapshotName:
Description: The name of a DB snapshot (optional, if arn:aws:rds:eu-west-1:172017021075:snapshot:cia-demo-20170711 supplied DBUsername,DBPassword need to eris,discord23)
Default: ''
Type: String
DebPackageUrl:
Default: https://repo1.maven.org/maven2/com/hack23/cia/cia-dist-deb/2022.12.26/cia-dist-deb-2022.12.26.deb
Description: The debian package url
Type: String
MinLength: '8'
DBClass:
Default: db.m6g.xlarge
Description: Database instance class
Type: String
AllowedValues:
- db.t3.micro
- db.t3.small
- db.t3.medium
- db.t3.large
- db.t3.xlarge
- db.t3.2xlarge
- db.m6g.large
- db.m6g.xlarge
- db.m6g.2xlarge
- db.m6g.4xlarge
- db.m6g.12xlarge
- db.m6g.16xlarge
- db.r6g.large
- db.r6g.xlarge
- db.r6g.2xlarge
- db.r6g.4xlarge
- db.r6g.8xlarge
- db.r6g.16xlarge
ConstraintDescription: must select a valid database instance type.
DBAllocatedStorage:
Default: '200'
Description: The size of the database (Gb)
Type: Number
MinValue: '100'
MaxValue: '6144'
ConstraintDescription: must be between 10+
Mappings:
SubnetConfig:
VPC:
CIDR: 10.40.0.0/16
Public:
CIDR: 10.40.10.0/24
Public2:
CIDR: 10.40.11.0/24
Public3:
CIDR: 10.40.12.0/24
PrivateApp:
CIDR: 10.40.30.0/24
PrivateApp2:
CIDR: 10.40.31.0/24
PrivateApp3:
CIDR: 10.40.32.0/24
Private:
CIDR: 10.40.20.0/24
Private2:
CIDR: 10.40.21.0/24
Private3:
CIDR: 10.40.22.0/24
AWSRegionAvailabilityZone:
us-east-1:
FIRST: us-east-1a
SECOND: us-east-1b
THIRD: us-east-1c
us-west-1:
FIRST: us-west-1a
SECOND: us-west-1b
THIRD: us-west-1c
us-west-2:
FIRST: us-west-2a
SECOND: us-west-2b
THIRD: us-west-2c
eu-west-1:
FIRST: eu-west-1a
SECOND: eu-west-1b
THIRD: eu-west-1c
eu-north-1:
FIRST: eu-north-1a
SECOND: eu-north-1b
THIRD: eu-north-1c
eu-central-1:
FIRST: eu-central-1a
SECOND: eu-central-1b
THIRD: eu-central-1c
ap-northeast-1:
FIRST: ap-northeast-1a
SECOND: ap-northeast-1b
THIRD: ap-northeast-1c
ap-southeast-1:
FIRST: ap-southeast-1a
SECOND: ap-southeast-1b
THIRD: ap-southeast-1c
ap-southeast-2:
FIRST: ap-southeast-2a
SECOND: ap-southeast-2b
THIRD: ap-southeast-2c
sa-east-1:
FIRST: sa-east-1a
SECOND: sa-east-1b
THIRD: sa-east-1c
cn-north-1:
FIRST: cn-north-1a
SECOND: cn-north-1b
THIRD: cn-north-1c
Conditions:
useDBSnapshot: !Not
- !Equals
- !Ref 'DBSnapshotName'
- ''
useDBEncryptionKmsAlias: !Not
- !Equals
- !Ref 'DBEncryptionKmsAlias'
- ''
useDomainName: !Not
- !Equals
- !Ref 'DomainName'
- ''
useRotationLambda: !Not
- !Equals
- !Ref 'RotationLambda'
- ''
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !FindInMap
- SubnetConfig
- VPC
- CIDR
EnableDnsSupport: 'true'
EnableDnsHostnames: 'true'
Tags:
- Key: Application
Value: !Ref 'AWS::StackId'
- Key: Network
Value: Public
FlowLogsRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- vpc-flow-logs.amazonaws.com
Action:
- sts:AssumeRole
Path: /
FlowLogsPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: FlowLogs
PolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
- logs:DescribeLogGroups
- logs:DescribeLogStreams
Effect: Allow
Resource: '*'
Roles:
- !Ref 'FlowLogsRole'
DependsOn:
- FlowLogsRole
VPCFlowLog:
Type: AWS::EC2::FlowLog
Properties:
DeliverLogsPermissionArn: !GetAtt 'FlowLogsRole.Arn'
LogGroupName: !Join
- ''
- - VPCFlowLogsGroup
- '-'
- !Ref 'AWS::StackName'
ResourceId: !Ref 'VPC'
ResourceType: VPC
TrafficType: ALL
ArtifactBucket:
Type: AWS::S3::Bucket
DependsOn: LogsBucket
DeletionPolicy: Retain
Properties:
AccessControl: Private
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
KMSMasterKeyID: !Ref 'ArtifactBucketEncryptionKey'
SSEAlgorithm: aws:kms
VersioningConfiguration:
Status: Enabled
LifecycleConfiguration:
Rules:
- Id: Rule for log prefix
Status: Enabled
Transitions:
- TransitionInDays: 30
StorageClass: STANDARD_IA
- TransitionInDays: 90
StorageClass: GLACIER
ExpirationInDays: 365
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
LoggingConfiguration:
DestinationBucketName: !Ref 'LogsBucket'
LogFilePrefix: /logs/ArtifactBucket/
Tags:
- Key: DomainName
Value: !Ref 'DomainName'
- Key: DataClassificationConfidentiality
Value: Proprietary
- Key: DataClassificationIntegrity
Value: Standard
- Key: DataClassificationAvailability
Value: Standard
ArtifactBucketEncryptionKey:
Type: AWS::KMS::Key
Properties:
Description: Key used for Server Fleet Management Solution artifact bucket
Enabled: true
EnableKeyRotation: true
KeyPolicy:
Statement:
- Sid: manage-key
Action:
- kms:*
Effect: Allow
Principal:
AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
Resource: '*'
- Sid: ssm-access-policy-statement
Action:
- kms:*
Effect: Allow
Principal:
Service: ssm.amazonaws.com
Resource: '*'
ArtifactBucketEncryptionKeyAlias:
Type: AWS::KMS::Alias
Properties:
AliasName: alias/ssm-bucket-encryption-key
TargetKeyId: !Ref 'ArtifactBucketEncryptionKey'
ArtifactBucketPolicy:
Type: AWS::S3::BucketPolicy
DeletionPolicy: Retain
Properties:
Bucket: !Ref 'ArtifactBucket'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: ArtifactBucketPermissionsCheck
Effect: Allow
Resource: !Sub '${ArtifactBucket.Arn}'
Principal:
Service: ssm.amazonaws.com
Action:
- s3:GetBucketAcl
- Sid: ArtifactBucketDeliveryDenyHttp
Effect: Deny
Principal: '*'
Action:
- s3:GetObject
Resource: !Sub '${ArtifactBucket.Arn}/*'
Condition:
Bool:
aws:SecureTransport: 'false'
- Sid: ArtifactBucketDelivery
Effect: Allow
Resource: !Sub '${ArtifactBucket.Arn}/*'
Principal:
Service: ssm.amazonaws.com
Action:
- s3:PutObject
Condition:
StringEquals:
s3:x-amz-acl: bucket-owner-full-control
MaintenanceWindowServiceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- ssm.amazonaws.com
Action:
- sts:AssumeRole
Path: /
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonSSMMaintenanceWindowRole
Policies:
- PolicyName: PassRole
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- iam:PassRole
Resource:
- '*'
MaintenanceWindow:
Type: AWS::SSM::MaintenanceWindow
Properties:
Description: Daily Maintenance Window
AllowUnassociatedTargets: false
Cutoff: 0
Schedule: rate(1 hour)
Duration: 1
Name: hourly-patching
MaintenanceWindowTarget:
Type: AWS::SSM::MaintenanceWindowTarget
Properties:
Description: Server Fleet Management Solution Instances
WindowId: !Ref 'MaintenanceWindow'
ResourceType: INSTANCE
Targets:
- Key: tag:SystemPatchComplianceMaintenanceWindowActive
Values:
- 'true'
MaintenanceWindowTaskPatchBaselineInstall:
Type: AWS::SSM::MaintenanceWindowTask
Properties:
Description: Runs Server Fleet Management Solution patch baseline
ServiceRoleArn: !GetAtt 'MaintenanceWindowServiceRole.Arn'
MaxErrors: 1
TaskArn: AWS-RunPatchBaseline
MaxConcurrency: 100%
WindowId: !Ref 'MaintenanceWindow'
Priority: 1
TaskType: RUN_COMMAND
TaskInvocationParameters:
MaintenanceWindowRunCommandParameters:
Comment: Runs patch baseline
TimeoutSeconds: 600
Parameters:
Operation:
- Install
OutputS3BucketName: !Ref 'ArtifactBucket'
Targets:
- Values:
- !Ref 'MaintenanceWindowTarget'
Key: WindowTargetIds
MaintenanceWindowTaskPatchBaselineScan:
Type: AWS::SSM::MaintenanceWindowTask
Properties:
Description: Runs Server Fleet Management Solution patch baseline
ServiceRoleArn: !GetAtt 'MaintenanceWindowServiceRole.Arn'
MaxErrors: 1
TaskArn: AWS-RunPatchBaseline
MaxConcurrency: 100%
WindowId: !Ref 'MaintenanceWindow'
Priority: 2
TaskType: RUN_COMMAND
TaskInvocationParameters:
MaintenanceWindowRunCommandParameters:
Comment: Runs patch baseline
TimeoutSeconds: 600
Parameters:
Operation:
- Scan
OutputS3BucketName: !Ref 'ArtifactBucket'
Targets:
- Values:
- !Ref 'MaintenanceWindowTarget'
Key: WindowTargetIds
UpdateSSMAgent:
Type: AWS::SSM::Association
Properties:
AssociationName: UpdateSSMAgent
Name: AWS-UpdateSSMAgent
Parameters:
allowDowngrade:
- 'false'
OutputLocation:
S3Location:
OutputS3BucketName: !Ref 'ArtifactBucket'
ScheduleExpression: rate(7 days)
Targets:
- Key: tag:UpdateSSMAgent
Values:
- 'true'
GatherSoftwareInventory:
Type: AWS::SSM::Association
DependsOn: ArtifactBucket
Properties:
AssociationName: GatherSoftwareInventory
Name: AWS-GatherSoftwareInventory
Parameters:
windowsUpdates:
- Disabled
windowsRegistry:
- Disabled
windowsRoles:
- Disabled
services:
- Disabled
OutputLocation:
S3Location:
OutputS3BucketName: !Ref 'ArtifactBucket'
ScheduleExpression: rate(1 hour)
Targets:
- Key: tag:GatherSoftwareInventory
Values:
- 'true'
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref 'VPC'
AvailabilityZone: !FindInMap
- AWSRegionAvailabilityZone
- !Ref 'AWS::Region'
- FIRST
CidrBlock: !FindInMap
- SubnetConfig
- Public
- CIDR
Tags:
- Key: Application
Value: !Ref 'AWS::StackId'
- Key: Network
Value: Public
PublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref 'VPC'
AvailabilityZone: !FindInMap
- AWSRegionAvailabilityZone
- !Ref 'AWS::Region'
- SECOND
CidrBlock: !FindInMap
- SubnetConfig
- Public2
- CIDR
Tags:
- Key: Application
Value: !Ref 'AWS::StackId'
- Key: Network
Value: Public
PublicSubnet3:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref 'VPC'
AvailabilityZone: !FindInMap
- AWSRegionAvailabilityZone
- !Ref 'AWS::Region'
- THIRD
CidrBlock: !FindInMap
- SubnetConfig
- Public3
- CIDR
Tags:
- Key: Application
Value: !Ref 'AWS::StackId'
- Key: Network
Value: Public
PrivateAppSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref 'VPC'
AvailabilityZone: !FindInMap
- AWSRegionAvailabilityZone
- !Ref 'AWS::Region'
- FIRST
CidrBlock: !FindInMap
- SubnetConfig
- PrivateApp
- CIDR
Tags:
- Key: Application
Value: !Ref 'AWS::StackId'
- Key: Network
Value: PrivateApp
PrivateAppSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref 'VPC'
AvailabilityZone: !FindInMap
- AWSRegionAvailabilityZone
- !Ref 'AWS::Region'
- SECOND
CidrBlock: !FindInMap
- SubnetConfig
- PrivateApp2
- CIDR
Tags:
- Key: Application
Value: !Ref 'AWS::StackId'
- Key: Network
Value: PrivateApp
PrivateAppSubnet3:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref 'VPC'
AvailabilityZone: !FindInMap
- AWSRegionAvailabilityZone
- !Ref 'AWS::Region'
- THIRD
CidrBlock: !FindInMap
- SubnetConfig
- PrivateApp3
- CIDR
Tags:
- Key: Application
Value: !Ref 'AWS::StackId'
- Key: Network
Value: PrivateApp
SgFormonitoringVPCEndpoint:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group to allow access to monitoring VPC Endpoint
SecurityGroupIngress:
- Description: vpc endpoint inbound access
CidrIp: !FindInMap
- SubnetConfig
- VPC
- CIDR
IpProtocol: tcp
FromPort: '443'
ToPort: '443'
SecurityGroupEgress:
- Description: vpc endpoint outbound access
CidrIp: '0.0.0.0/0'
IpProtocol: '-1'
VpcId: !Ref 'VPC'
monitoringVPCEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcEndpointType: Interface
ServiceName: !Join
- ''
- - com.amazonaws.
- !Ref 'AWS::Region'
- .monitoring
VpcId: !Ref 'VPC'
SubnetIds:
- !Ref 'PrivateAppSubnet'
- !Ref 'PrivateAppSubnet2'
- !Ref 'PrivateAppSubnet3'
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref 'SgFormonitoringVPCEndpoint'
SgForlogsVPCEndpoint:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group to allow access to logs VPC Endpoint
SecurityGroupIngress:
- Description: vpc endpoint inbound access
CidrIp: !FindInMap
- SubnetConfig
- VPC
- CIDR
IpProtocol: tcp
FromPort: '443'
ToPort: '443'
SecurityGroupEgress:
- Description: vpc endpoint outbound access
CidrIp: '0.0.0.0/0'
IpProtocol: '-1'
VpcId: !Ref 'VPC'
logsVPCEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcEndpointType: Interface
ServiceName: !Join
- ''
- - com.amazonaws.
- !Ref 'AWS::Region'
- .logs
VpcId: !Ref 'VPC'
SubnetIds:
- !Ref 'PrivateAppSubnet'
- !Ref 'PrivateAppSubnet2'
- !Ref 'PrivateAppSubnet3'
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref 'SgForlogsVPCEndpoint'
SgForssmVPCEndpoint:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group to allow access to ssm VPC Endpoint
SecurityGroupIngress:
- Description: vpc endpoint inbound access
CidrIp: !FindInMap
- SubnetConfig
- VPC
- CIDR
IpProtocol: tcp
FromPort: '443'
ToPort: '443'
SecurityGroupEgress:
- Description: vpc endpoint outbound access
CidrIp: '0.0.0.0/0'
IpProtocol: '-1'
VpcId: !Ref 'VPC'
ssmVPCEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcEndpointType: Interface
ServiceName: !Join
- ''
- - com.amazonaws.
- !Ref 'AWS::Region'
- .ssm
VpcId: !Ref 'VPC'
SubnetIds:
- !Ref 'PrivateAppSubnet'
- !Ref 'PrivateAppSubnet2'
- !Ref 'PrivateAppSubnet3'
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref 'SgForssmVPCEndpoint'
SgForssmmessagesVPCEndpoint:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group to allow access to ssmmessages VPC Endpoint
SecurityGroupIngress:
- Description: vpc endpoint inbound access
CidrIp: !FindInMap
- SubnetConfig
- VPC
- CIDR
IpProtocol: tcp
FromPort: '443'
ToPort: '443'
SecurityGroupEgress:
- Description: vpc endpoint outbound access
CidrIp: '0.0.0.0/0'
IpProtocol: '-1'
VpcId: !Ref 'VPC'
ssmmessagesVPCEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcEndpointType: Interface
ServiceName: !Join
- ''
- - com.amazonaws.
- !Ref 'AWS::Region'
- .ssmmessages
VpcId: !Ref 'VPC'
SubnetIds:
- !Ref 'PrivateAppSubnet'
- !Ref 'PrivateAppSubnet2'
- !Ref 'PrivateAppSubnet3'
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref 'SgForssmmessagesVPCEndpoint'
SgForecVPCEndpoint:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group to allow access to ec2 VPC Endpoint
SecurityGroupIngress:
- Description: vpc endpoint inbound access
CidrIp: !FindInMap
- SubnetConfig
- VPC
- CIDR
IpProtocol: tcp
FromPort: '443'
ToPort: '443'
SecurityGroupEgress:
- Description: vpc endpoint outbound access
CidrIp: '0.0.0.0/0'
IpProtocol: '-1'
VpcId: !Ref 'VPC'
ecVPCEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcEndpointType: Interface
ServiceName: !Join
- ''
- - com.amazonaws.
- !Ref 'AWS::Region'
- .ec2
VpcId: !Ref 'VPC'
SubnetIds:
- !Ref 'PrivateAppSubnet'
- !Ref 'PrivateAppSubnet2'
- !Ref 'PrivateAppSubnet3'
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref 'SgForecVPCEndpoint'
SgForecmessagesVPCEndpoint:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group to allow access to ec2messages VPC Endpoint
SecurityGroupIngress:
- Description: vpc endpoint inbound access
CidrIp: !FindInMap
- SubnetConfig
- VPC
- CIDR
IpProtocol: tcp
FromPort: '443'
ToPort: '443'
SecurityGroupEgress:
- Description: vpc endpoint outbound access
CidrIp: '0.0.0.0/0'
IpProtocol: '-1'
VpcId: !Ref 'VPC'
ecmessagesVPCEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcEndpointType: Interface
ServiceName: !Join
- ''
- - com.amazonaws.
- !Ref 'AWS::Region'
- .ec2messages
VpcId: !Ref 'VPC'
SubnetIds:
- !Ref 'PrivateAppSubnet'
- !Ref 'PrivateAppSubnet2'
- !Ref 'PrivateAppSubnet3'
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref 'SgForecmessagesVPCEndpoint'
sVPCEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Join
- ''
- - com.amazonaws.
- !Ref 'AWS::Region'
- .s3
VpcId: !Ref 'VPC'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal: '*'
Action:
- '*'
Resource:
- '*'
RouteTableIds:
- !Ref 'PrivateAppRouteTable'
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref 'VPC'
AvailabilityZone: !FindInMap
- AWSRegionAvailabilityZone
- !Ref 'AWS::Region'
- FIRST
CidrBlock: !FindInMap
- SubnetConfig
- Private
- CIDR
Tags:
- Key: Application
Value: !Ref 'AWS::StackId'
- Key: Network
Value: Private
PrivateSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref 'VPC'
AvailabilityZone: !FindInMap
- AWSRegionAvailabilityZone
- !Ref 'AWS::Region'
- SECOND
CidrBlock: !FindInMap
- SubnetConfig
- Private2
- CIDR
Tags:
- Key: Application
Value: !Ref 'AWS::StackId'
- Key: Network
Value: Private
PrivateSubnet3:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref 'VPC'
AvailabilityZone: !FindInMap
- AWSRegionAvailabilityZone
- !Ref 'AWS::Region'
- THIRD
CidrBlock: !FindInMap
- SubnetConfig
- Private3
- CIDR
Tags:
- Key: Application
Value: !Ref 'AWS::StackId'
- Key: Network
Value: Private
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Application
Value: !Ref 'AWS::StackId'
- Key: Network
Value: Public
GatewayToInternet:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref 'VPC'
InternetGatewayId: !Ref 'InternetGateway'
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref 'VPC'
Tags:
- Key: Application
Value: !Ref 'AWS::StackId'
- Key: Network
Value: Public
PublicRoute:
Type: AWS::EC2::Route
DependsOn: GatewayToInternet
Properties:
RouteTableId: !Ref 'PublicRouteTable'
DestinationCidrBlock: '0.0.0.0/0'
GatewayId: !Ref 'InternetGateway'
PublicSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref 'PublicSubnet'
RouteTableId: !Ref 'PublicRouteTable'
Public2SubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref 'PublicSubnet2'
RouteTableId: !Ref 'PublicRouteTable'
Public3SubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref 'PublicSubnet3'
RouteTableId: !Ref 'PublicRouteTable'
PrivateAppRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref 'VPC'
Tags:
- Key: Application
Value: !Ref 'AWS::StackId'
- Key: Network
Value: Private
PrivateAppRoute:
Type: AWS::EC2::Route
DependsOn: GatewayToInternet
Properties:
RouteTableId: !Ref 'PrivateAppRouteTable'
DestinationCidrBlock: '0.0.0.0/0'
NatGatewayId: !Ref 'NAT'
PrivateAppRouteTable2:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref 'VPC'
Tags:
- Key: Application
Value: !Ref 'AWS::StackId'
- Key: Network
Value: Private
PrivateAppRoute2:
Type: AWS::EC2::Route
DependsOn: GatewayToInternet
Properties:
RouteTableId: !Ref 'PrivateAppRouteTable2'
DestinationCidrBlock: '0.0.0.0/0'
NatGatewayId: !Ref 'NAT2'
PrivateAppRouteTable3:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref 'VPC'
Tags:
- Key: Application
Value: !Ref 'AWS::StackId'
- Key: Network
Value: Private
PrivateAppRoute3:
Type: AWS::EC2::Route
DependsOn: GatewayToInternet
Properties:
RouteTableId: !Ref 'PrivateAppRouteTable3'
DestinationCidrBlock: '0.0.0.0/0'
NatGatewayId: !Ref 'NAT3'
PrivateAppSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref 'PrivateAppSubnet'
RouteTableId: !Ref 'PrivateAppRouteTable'
PrivateApp2SubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref 'PrivateAppSubnet2'
RouteTableId: !Ref 'PrivateAppRouteTable2'
PrivateApp3SubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref 'PrivateAppSubnet3'
RouteTableId: !Ref 'PrivateAppRouteTable3'
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref 'VPC'
Tags:
- Key: Application
Value: !Ref 'AWS::StackId'
- Key: Network
Value: Private
PrivateRoute:
Type: AWS::EC2::Route
DependsOn: NAT
Properties:
RouteTableId: !Ref 'PrivateRouteTable'
DestinationCidrBlock: '0.0.0.0/0'
NatGatewayId: !Ref 'NAT'
PrivateRouteTable2:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref 'VPC'
Tags:
- Key: Application
Value: !Ref 'AWS::StackId'
- Key: Network
Value: Private
PrivateRoute2:
Type: AWS::EC2::Route
DependsOn: NAT
Properties:
RouteTableId: !Ref 'PrivateRouteTable2'
DestinationCidrBlock: '0.0.0.0/0'
NatGatewayId: !Ref 'NAT2'
PrivateRouteTable3:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref 'VPC'
Tags:
- Key: Application
Value: !Ref 'AWS::StackId'
- Key: Network
Value: Private
PrivateRoute3:
Type: AWS::EC2::Route
DependsOn: NAT
Properties:
RouteTableId: !Ref 'PrivateRouteTable3'
DestinationCidrBlock: '0.0.0.0/0'
NatGatewayId: !Ref 'NAT3'
PrivateSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref 'PrivateSubnet'
RouteTableId: !Ref 'PrivateRouteTable'
Private2SubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref 'PrivateSubnet2'
RouteTableId: !Ref 'PrivateRouteTable2'
Private3SubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref 'PrivateSubnet3'
RouteTableId: !Ref 'PrivateRouteTable3'
PublicNetworkAcl:
Type: AWS::EC2::NetworkAcl
Properties:
VpcId: !Ref 'VPC'
Tags:
- Key: Application
Value: !Ref 'AWS::StackId'
- Key: Network
Value: Public
PrivateAppNetworkAcl:
Type: AWS::EC2::NetworkAcl
Properties:
VpcId: !Ref 'VPC'
Tags:
- Key: Application
Value: !Ref 'AWS::StackId'
- Key: Network
Value: Public
PrivateNetworkAcl:
Type: AWS::EC2::NetworkAcl
Properties:
VpcId: !Ref 'VPC'
Tags:
- Key: Application
Value: !Ref 'AWS::StackId'
- Key: Network
Value: Private
InboundPostgresqlPrivateNetworkAclEntry:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref 'PrivateNetworkAcl'
RuleNumber: '100'
Protocol: '6'
RuleAction: allow
Egress: 'false'
CidrBlock: !FindInMap
- SubnetConfig
- PrivateApp
- CIDR
PortRange:
From: '5432'
To: '5432'
InboundPostgresqlPrivate2NetworkAclEntry:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref 'PrivateNetworkAcl'
RuleNumber: '200'
Protocol: '6'
RuleAction: allow
Egress: 'false'
CidrBlock: !FindInMap
- SubnetConfig
- PrivateApp2
- CIDR
PortRange:
From: '5432'
To: '5432'
InboundPostgresqlPrivate3NetworkAclEntry:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref 'PrivateNetworkAcl'
RuleNumber: '300'
Protocol: '6'
RuleAction: allow
Egress: 'false'
CidrBlock: !FindInMap
- SubnetConfig
- PrivateApp3
- CIDR
PortRange:
From: '5432'
To: '5432'
OutboundPostgresqlPrivateNetworkAclEntry:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref 'PrivateNetworkAcl'
RuleNumber: '400'
Protocol: '6'
RuleAction: allow
Egress: 'true'
CidrBlock: !FindInMap
- SubnetConfig
- PrivateApp
- CIDR
PortRange:
From: '1024'
To: '65535'
OutboundPostgresqlPrivate2NetworkAclEntry:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref 'PrivateNetworkAcl'
RuleNumber: '500'
Protocol: '6'
RuleAction: allow
Egress: 'true'
CidrBlock: !FindInMap
- SubnetConfig
- PrivateApp2
- CIDR
PortRange:
From: '1024'
To: '65535'
OutboundPostgresqlPrivate3NetworkAclEntry:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref 'PrivateNetworkAcl'
RuleNumber: '600'
Protocol: '6'
RuleAction: allow
Egress: 'true'
CidrBlock: !FindInMap
- SubnetConfig
- PrivateApp3
- CIDR
PortRange:
From: '1024'
To: '65535'
InboundPrivateAppNetworkAclEntry:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref 'PrivateAppNetworkAcl'
RuleNumber: '700'
Protocol: '6'
RuleAction: allow
Egress: 'false'
CidrBlock: '0.0.0.0/0'
PortRange:
From: '1024'
To: '65535'
InboundDenyRdpPrivateAppNetworkAclEntry:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref 'PrivateAppNetworkAcl'
RuleNumber: '70'
Protocol: '6'
RuleAction: deny
Egress: 'false'
CidrBlock: '0.0.0.0/0'
PortRange:
From: '3389'
To: '3389'
OutboundPrivateAppNetworkAclEntry:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref 'PrivateAppNetworkAcl'
RuleNumber: '800'
Protocol: '6'
RuleAction: allow
Egress: 'true'
CidrBlock: '0.0.0.0/0'
PortRange:
From: '80'
To: '65535'
OutboundDenyRdpPrivateAppNetworkAclEntry:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref 'PrivateAppNetworkAcl'
RuleNumber: '80'
Protocol: '6'
RuleAction: deny
Egress: 'true'
CidrBlock: '0.0.0.0/0'
PortRange:
From: '3389'
To: '3389'
InboundHttpsPortPublicNetworkAclEntry:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref 'PublicNetworkAcl'
RuleNumber: '100'
Protocol: '6'
RuleAction: allow
Egress: 'false'
CidrBlock: '0.0.0.0/0'
PortRange:
From: '443'
To: '443'
InboundHttpNatPortPublicNetworkAclEntry:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref 'PublicNetworkAcl'
RuleNumber: '200'
Protocol: '6'
RuleAction: allow
Egress: 'false'
CidrBlock: 10.40.0.0/16
PortRange:
From: '80'
To: '80'
InboundEphemeralPortPublicNetworkAclEntry:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref 'PublicNetworkAcl'
RuleNumber: '300'
Protocol: '6'
RuleAction: allow
Egress: 'false'
CidrBlock: '0.0.0.0/0'
PortRange:
From: '1024'
To: '65535'
InboundEphemeralPortDenyRdpPublicNetworkAclEntry:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref 'PublicNetworkAcl'
RuleNumber: '30'
Protocol: '6'
RuleAction: deny
Egress: 'false'
CidrBlock: '0.0.0.0/0'
PortRange:
From: '3389'
To: '3389'
OutboundHttpPublicNetworkAclEntry:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref 'PublicNetworkAcl'
RuleNumber: '400'
Protocol: '6'
RuleAction: allow
Egress: 'true'
CidrBlock: '0.0.0.0/0'
PortRange:
From: '80'
To: '80'
OutboundHttpsPublicNetworkAclEntry:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref 'PublicNetworkAcl'
RuleNumber: '500'
Protocol: '6'
RuleAction: allow
Egress: 'true'
CidrBlock: '0.0.0.0/0'
PortRange:
From: '443'
To: '443'
OutboundEphemeralPublicNetworkAclEntry:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref 'PublicNetworkAcl'
RuleNumber: '600'
Protocol: '6'
RuleAction: allow
Egress: 'true'
CidrBlock: '0.0.0.0/0'
PortRange:
From: '1024'
To: '65535'
OutboundEphemeralDenyRdpPublicNetworkAclEntry:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref 'PublicNetworkAcl'
RuleNumber: '60'
Protocol: '6'
RuleAction: deny
Egress: 'true'
CidrBlock: '0.0.0.0/0'
PortRange:
From: '3389'
To: '3389'
PublicSubnetNetworkAclAssociation:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId: !Ref 'PublicSubnet'
NetworkAclId: !Ref 'PublicNetworkAcl'
Public2SubnetNetworkAclAssociation:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId: !Ref 'PublicSubnet2'
NetworkAclId: !Ref 'PublicNetworkAcl'
Public3SubnetNetworkAclAssociation:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId: !Ref 'PublicSubnet3'
NetworkAclId: !Ref 'PublicNetworkAcl'
PrivateAppSubnetNetworkAclAssociation:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId: !Ref 'PrivateAppSubnet'
NetworkAclId: !Ref 'PrivateAppNetworkAcl'
PrivateApp2SubnetNetworkAclAssociation:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId: !Ref 'PrivateAppSubnet2'
NetworkAclId: !Ref 'PrivateAppNetworkAcl'
PrivateApp3SubnetNetworkAclAssociation:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId: !Ref 'PrivateAppSubnet3'
NetworkAclId: !Ref 'PrivateAppNetworkAcl'
PrivateSubnetNetworkAclAssociation:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId: !Ref 'PrivateSubnet'
NetworkAclId: !Ref 'PrivateNetworkAcl'
Private2SubnetNetworkAclAssociation:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId: !Ref 'PrivateSubnet2'
NetworkAclId: !Ref 'PrivateNetworkAcl'
Private3SubnetNetworkAclAssociation:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId: !Ref 'PrivateSubnet3'
NetworkAclId: !Ref 'PrivateNetworkAcl'
DatabaseSecurityGroup:
Type: AWS::EC2::SecurityGroup
DependsOn:
- WebServerSecurityGroup
Properties:
GroupDescription: Database Access
VpcId: !Ref 'VPC'
SecurityGroupIngress:
- Description: Postgres inbound access from server
IpProtocol: tcp
FromPort: '5432'
ToPort: '5432'
SourceSecurityGroupId: !Ref 'WebServerSecurityGroup'
SecurityGroupEgress:
- Description: Postgres outbound access to server
IpProtocol: tcp
FromPort: '5432'
ToPort: '5432'
SourceSecurityGroupId: !Ref 'WebServerSecurityGroup'
DatabaseParamGroup:
Type: AWS::RDS::DBParameterGroup
Properties:
Description: Database Parameter Group + pg_stat_statements
Family: postgres14
Parameters:
max_prepared_transactions: LEAST({DBInstanceClassMemory/9531392},5000)
max_parallel_workers_per_gather: '4'
rds.force_ssl: '1'
rds.force_admin_logging_level: notice
rds.force_autovacuum_logging_level: notice
rds.log_retention_period: '2400'
shared_preload_libraries: pg_stat_statements, pgaudit
pgaudit.log: ddl
pg_stat_statements.track: all
pg_stat_statements.max: '10000'
myDBSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupDescription: DB Private Subnet
SubnetIds:
- !Ref 'PrivateSubnet'
- !Ref 'PrivateSubnet2'
- !Ref 'PrivateSubnet3'
RDSInstanceRotationSecret:
Type: AWS::SecretsManager::Secret
Properties:
Description: This is the secret for my RDS instance
SecretString: !Join
- ''
- - '{"username" : "'
- !Ref 'DBUsername'
- '","password" : "'
- !Ref 'DBPassword'
- '"}'
Tags:
- Key: SecretGroup
Value: RDS
SecretRDSInstanceAttachment:
Type: AWS::SecretsManager::SecretTargetAttachment
Properties:
SecretId: !Ref 'RDSInstanceRotationSecret'
TargetId: !Ref 'Database'
TargetType: AWS::RDS::DBInstance
MySecretRotationSchedule:
Type: AWS::SecretsManager::RotationSchedule
DependsOn: SecretRotationLambdaInvokePermission
Condition: useRotationLambda
Properties:
SecretId: !Ref 'RDSInstanceRotationSecret'
RotationLambdaARN: !Ref 'RotationLambda'
RotationRules:
AutomaticallyAfterDays: 1
SecretRotationLambdaInvokePermission:
Type: AWS::Lambda::Permission
DependsOn: SecretRDSInstanceAttachment
Condition: useRotationLambda
Properties:
FunctionName: MySecretsManagerRotationFunction
Action: lambda:InvokeFunction
Principal: secretsmanager.amazonaws.com
Database:
Type: AWS::RDS::DBInstance
DependsOn:
- PrivateRoute
- DatabaseSecurityGroup
Properties:
DBName: !If
- useDBSnapshot
- !Ref 'AWS::NoValue'
- !Ref 'DBName'
StorageEncrypted: 'true'
KmsKeyId: !If
- useDBEncryptionKmsAlias
- !Ref 'DBEncryptionKmsAlias'
- !Ref 'AWS::NoValue'
AllocatedStorage: !Ref 'DBAllocatedStorage'
CopyTagsToSnapshot: 'true'
Tags:
- Key: DomainName
Value: !Ref 'DomainName'
- Key: DataClassificationConfidentiality
Value: Proprietary
- Key: DataClassificationIntegrity
Value: Standard
- Key: DataClassificationAvailability
Value: Standard
DBInstanceClass: !Ref 'DBClass'
Engine: postgres
EngineVersion: '14.4'
MultiAZ: 'true'
PubliclyAccessible: 'false'
BackupRetentionPeriod: '7'
EnablePerformanceInsights: 'true'
EnableCloudwatchLogsExports:
- postgresql
- upgrade
StorageType: gp2
DBInstanceIdentifier: rotation-instance
MasterUsername: !If
- useDBSnapshot
- !Ref 'AWS::NoValue'
- !Join
- ''
- - '{{resolve:secretsmanager:'
- !Ref 'RDSInstanceRotationSecret'
- :SecretString:username}}
MasterUserPassword: !If
- useDBSnapshot
- !Ref 'AWS::NoValue'
- !Join
- ''
- - '{{resolve:secretsmanager:'
- !Ref 'RDSInstanceRotationSecret'
- :SecretString:password}}
DBSnapshotIdentifier: !If
- useDBSnapshot
- !Ref 'DBSnapshotName'
- !Ref 'AWS::NoValue'
DBSubnetGroupName: !Ref 'myDBSubnetGroup'
DBParameterGroupName: !Ref 'DatabaseParamGroup'
VPCSecurityGroups:
- !GetAtt 'DatabaseSecurityGroup.GroupId'
LogsBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
Properties:
AccessControl: LogDeliveryWrite
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
LifecycleConfiguration:
Rules:
- Id: Rule for log prefix
Status: Enabled
Transitions:
- TransitionInDays: 30
StorageClass: STANDARD_IA
- TransitionInDays: 90
StorageClass: GLACIER
ExpirationInDays: 365
VersioningConfiguration:
Status: Enabled
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
LoggingConfiguration:
LogFilePrefix: /logs/LogsBucket/
Tags:
- Key: DomainName
Value: !Ref 'DomainName'
- Key: DataClassificationConfidentiality
Value: Proprietary
- Key: DataClassificationIntegrity
Value: Standard
- Key: DataClassificationAvailability
Value: Standard
LogsBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref 'LogsBucket'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: ELBAccessLogsAllowAws
Effect: Allow
Resource: !Join
- ''
- - 'arn:aws:s3:::'
- !Ref 'LogsBucket'
- /*
Principal:
AWS:
- '156460612806'
Action:
- s3:PutObject
- Sid: ELBAccessLogsDenyHttp
Effect: Deny
Principal: '*'
Action:
- s3:GetObject
Resource: !Join
- ''
- - 'arn:aws:s3:::'
- !Ref 'LogsBucket'
- /*
Condition:
Bool:
aws:SecureTransport: 'false'
NAT:
DependsOn: GatewayToInternet
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt 'EIP.AllocationId'
SubnetId: !Ref 'PublicSubnet'
Tags:
- Key: Network
Value: NAT
NAT2:
DependsOn: GatewayToInternet
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt 'EIP2.AllocationId'
SubnetId: !Ref 'PublicSubnet2'
Tags:
- Key: Network
Value: NAT
NAT3:
DependsOn: GatewayToInternet
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt 'EIP3.AllocationId'
SubnetId: !Ref 'PublicSubnet3'
Tags:
- Key: Network
Value: NAT
EIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
EIP2:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
EIP3:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
PublicElasticLoadBalancerV2:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Subnets:
- !Ref 'PublicSubnet'
- !Ref 'PublicSubnet2'
- !Ref 'PublicSubnet3'
SecurityGroups:
- !Ref 'PublicLoadBalancerSecurityGroup'
LoadBalancerAttributes:
- Key: access_logs.s3.enabled
Value: 'true'
- Key: access_logs.s3.bucket
Value: !Ref 'LogsBucket'
- Key: access_logs.s3.prefix
Value: Logs
- Key: idle_timeout.timeout_seconds
Value: '60'
- Key: routing.http.drop_invalid_header_fields.enabled
Value: 'true'
- Key: routing.http.desync_mitigation_mode
Value: strictest
DependsOn: LogsBucketPolicy
AppPolicy:
Type: AWS::ResilienceHub::ResiliencyPolicy
Properties:
DataLocationConstraint: AnyLocation
Policy:
Software:
RpoInSecs: 300
RtoInSecs: 14400
Hardware:
RpoInSecs: 0
RtoInSecs: 120
AZ:
RpoInSecs: 0
RtoInSecs: 120
PolicyDescription: MissionCritical
PolicyName: MissionCritical
Tier: MissionCritical
Application:
Type: AWS::ResilienceHub::App
DependsOn: WebServerFleet
Properties:
AppAssessmentSchedule: Daily
AppTemplateBody: !Sub '{"resources":[{"logicalResourceId":{"identifier":"LogsBucket","logicalStackName":"${AWS::StackName}","resourceGroupName":null},"type":"AWS::S3::Bucket","name":"s3bucket-logs"},{"logicalResourceId":{"identifier":"NAT","logicalStackName":"${AWS::StackName}","resourceGroupName":null},"type":"AWS::EC2::NatGateway","name":"natgateway-vpc"},{"logicalResourceId":{"identifier":"NAT2","logicalStackName":"${AWS::StackName}","resourceGroupName":null},"type":"AWS::EC2::NatGateway","name":"natgateway-vpc2"},{"logicalResourceId":{"identifier":"NAT3","logicalStackName":"${AWS::StackName}","resourceGroupName":null},"type":"AWS::EC2::NatGateway","name":"natgateway-vpc3"},{"logicalResourceId":{"identifier":"WebServerFleet","logicalStackName":"${AWS::StackName}","resourceGroupName":null},"type":"AWS::AutoScaling::AutoScalingGroup","name":"autoscalinggroup-fleet"},{"logicalResourceId":{"identifier":"ArtifactBucket","logicalStackName":"${AWS::StackName}","resourceGroupName":null},"type":"AWS::S3::Bucket","name":"s3bucket-art"},{"logicalResourceId":{"identifier":"Database","logicalStackName":"${AWS::StackName}","resourceGroupName":null},"type":"AWS::RDS::DBInstance","name":"rdsdbinstance-app"},{"logicalResourceId":{"identifier":"DNSRecordCNAME2PublicElasticLoadBalancerV2","logicalStackName":"${AWS::StackName}","resourceGroupName":null},"type":"AWS::Route53::RecordSet","name":"route53recordset-app"}],"appComponents":[{"name":"storage-s3bucket-logs","type":"AWS::ResilienceHub::StorageAppComponent","resourceNames":["s3bucket-logs"]},{"name":"networking-nat","type":"AWS::ResilienceHub::NetworkingAppComponent","resourceNames":["natgateway-vpc","natgateway-vpc2","natgateway-vpc3"]},{"name":"compute-app-fleet","type":"AWS::ResilienceHub::ComputeAppComponent","resourceNames":["autoscalinggroup-fleet"]},{"name":"storage-s3bucket-art","type":"AWS::ResilienceHub::StorageAppComponent","resourceNames":["s3bucket-art"]},{"name":"database-rds","type":"AWS::ResilienceHub::DatabaseAppComponent","resourceNames":["rdsdbinstance-app"]},{"name":"networking-dns","type":"AWS::ResilienceHub::NetworkingAppComponent","resourceNames":["route53recordset-app"]},{"name":"appcommon","type":"AWS::ResilienceHub::AppCommonAppComponent","resourceNames":[]}],"excludedResources":{"logicalResourceIds":[]},"version":2.0}'
Description: Citizen Intelligence Agency
Name: !Sub 'CIA'
ResiliencyPolicyArn: !Ref 'AppPolicy'
ResourceMappings:
- LogicalStackName: !Ref 'AWS::StackName'
MappingType: CfnStack
PhysicalResourceId:
Identifier: !Sub '${AWS::StackId}'
Type: Arn
AppAlarmTopic:
Type: AWS::SNS::Topic
DependsOn: Application
Properties:
Subscription:
- Endpoint: !Sub 'admin@${DomainName}'
Protocol: email
KmsMasterKeyId: alias/aws/sns
AWSResilienceHubRDSInstanceOverUtilizedCpuAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmActions:
- !Ref 'AppAlarmTopic'
AlarmDescription: Alarm by AWS Resilience Hub that reports when database CPU utilization is over 90%
AlarmName: !Sub 'AWSResilienceHub-RDSInstanceOverUtilizedCpuAlarm-2020-04-01_CIA_rotation-instance'
ComparisonOperator: GreaterThanThreshold
DatapointsToAlarm: 3
Dimensions:
- Name: DBInstanceIdentifier
Value: rotation-instance
EvaluationPeriods: 5
MetricName: CPUUtilization
Namespace: AWS/RDS
Period: 60
Statistic: Maximum
Threshold: 90
TreatMissingData: missing
Unit: Percent
AWSResilienceHubRDSInstanceLowMemoryAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmActions:
- !Ref 'AppAlarmTopic'
AlarmDescription: Alarm by AWS Resilience Hub that reports when database free memory is under 10MB
AlarmName: !Sub 'AWSResilienceHub-RDSInstanceLowMemoryAlarm-2020-04-01_CIA_rotation-instance'
ComparisonOperator: LessThanOrEqualToThreshold
DatapointsToAlarm: 3
Dimensions:
- Name: DBInstanceIdentifier
Value: rotation-instance
EvaluationPeriods: 5
MetricName: FreeableMemory
Namespace: AWS/RDS
Period: 60
Statistic: Minimum
Threshold: 10485760
TreatMissingData: missing
AWSResilienceHubApplicationLoadBalancerElbHttp4xxCountAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: !Sub 'AWSResilienceHub-ApplicationLoadBalancerElbHttp4xxCountAlarm-2020-04-01_CIA'
AlarmDescription: Alerts when the number of HTTP 4XX server error codes, which originate from the Application Load Balancer, is greater than or equal to the specified threshold
AlarmActions:
- !Ref 'AppAlarmTopic'
MetricName: HTTPCode_ELB_4XX_Count
Namespace: AWS/ApplicationELB
Dimensions:
- Name: LoadBalancer
Value: !Ref 'PublicElasticLoadBalancerV2'
Period: 60
Statistic: Sum
Threshold: 100
EvaluationPeriods: 5
DatapointsToAlarm: 3
ComparisonOperator: GreaterThanOrEqualToThreshold
TreatMissingData: notBreaching
Unit: Count
AWSResilienceHubApplicationLoadBalancerElbHttp5xxCountAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: !Sub 'AWSResilienceHub-ApplicationLoadBalancerElbHttp5xxCountAlarm-2020-04-01_CIA'
AlarmDescription: Alerts when the number of HTTP 5XX server error codes, which originate from the Application Load Balancer, is greater than or equal to the specified threshold
AlarmActions:
- !Ref 'AppAlarmTopic'
MetricName: HTTPCode_ELB_5XX_Count
Namespace: AWS/ApplicationELB
Dimensions:
- Name: LoadBalancer
Value: !Ref 'PublicElasticLoadBalancerV2'
Period: 60
Statistic: Sum
Threshold: 100
EvaluationPeriods: 5
DatapointsToAlarm: 3
ComparisonOperator: GreaterThanOrEqualToThreshold
TreatMissingData: notBreaching
Unit: Count
AWSResilienceHubAsgAbnormalRequestPerTargetAlarmCIAHttpsAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmActions:
- !Ref 'AppAlarmTopic'
AlarmDescription: Alarm by AWS Resilience Hub that reports when target group request count is anomalous with band width 1
AlarmName: !Sub 'AWSResilienceHub-AsgAbnormalRequestPerTargetAlarm-2020-07-13_CIA-Https'
ComparisonOperator: GreaterThanUpperThreshold
EvaluationPeriods: 15
DatapointsToAlarm: 10
Metrics:
- Expression: ANOMALY_DETECTION_BAND(m1,1)
Id: ad1
- Id: m1
MetricStat:
Metric:
MetricName: RequestCountPerTarget
Namespace: AWS/ApplicationELB
Dimensions:
- Name: TargetGroup
Value: !Ref 'TargetGroupHttps'
Period: 60
Stat: Average
ThresholdMetricId: ad1
TreatMissingData: missing
AWSResilienceHubAsgManyDyingHostsAlarmrWebServerFleetAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmActions:
- !Ref 'AppAlarmTopic'
AlarmDescription: Alarm by AWS Resilience Hub that reports when over time an ASG has many unhealthy hosts
AlarmName: !Sub 'AWSResilienceHub-AsgManyDyingHostsAlarm-2020-07-13_CIA_WebServerFleet'
ComparisonOperator: GreaterThanThreshold
EvaluationPeriods: 180
DatapointsToAlarm: 5
Dimensions:
- Name: TargetGroup
Value: !Ref 'TargetGroupHttps'
- Name: LoadBalancer
Value: !Ref 'PublicElasticLoadBalancerV2'
MetricName: UnHealthyHostCount
Namespace: AWS/ApplicationELB
Period: 60
Statistic: Maximum
Threshold: 1
TreatMissingData: missing
WebACLAssociation:
Type: AWS::WAFv2::WebACLAssociation
DependsOn: BasicSecurityACL
Properties:
ResourceArn: !Ref 'PublicElasticLoadBalancerV2'
WebACLArn: !GetAtt 'BasicSecurityACL.Arn'
BasicSecurityACL:
Type: AWS::WAFv2::WebACL
DependsOn: PublicElasticLoadBalancerV2
Properties:
Name: BasicSecurityACL
Scope: REGIONAL
Description: This is a BasicSecurityACL
DefaultAction:
Allow: {}
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: BasicSecurityACLMetric
Rules:
- Name: RuleWithAWSManagedRulesAmazonIpReputationList
Priority: 0
OverrideAction:
None: {}
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: RuleWithAWSManagedRulesAmazonIpReputationList
Statement:
ManagedRuleGroupStatement:
VendorName: AWS
Name: AWSManagedRulesAmazonIpReputationList
ExcludedRules: []
- Name: RuleWithAWSManagedRulesAnonymousIpList
Priority: 1
OverrideAction:
None: {}
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: RuleWithAWSManagedRulesAnonymousIpList
Statement:
ManagedRuleGroupStatement:
VendorName: AWS
Name: AWSManagedRulesAnonymousIpList
ExcludedRules: []
- Name: RuleWithAWSManagedRulesCommonRuleSet
Priority: 2
OverrideAction:
None: {}
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: RuleWithAWSManagedRulesCommonRuleSet
Statement:
ManagedRuleGroupStatement:
VendorName: AWS
Name: AWSManagedRulesCommonRuleSet
ExcludedRules:
- GenericLFI_BODY
- GenericRFI_BODY
- Name: RuleWithAWSManagedRulesKnownBadInputsRuleSet
Priority: 3
OverrideAction:
None: {}
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: RuleWithAWSManagedRulesKnownBadInputsRuleSet
Statement:
ManagedRuleGroupStatement:
VendorName: AWS
Name: AWSManagedRulesKnownBadInputsRuleSet
ExcludedRules: []
- Name: RuleWithAWSManagedRulesLinuxRuleSet
Priority: 4
OverrideAction:
None: {}
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: RuleWithAWSManagedRulesLinuxRuleSet
Statement:
ManagedRuleGroupStatement:
VendorName: AWS
Name: AWSManagedRulesLinuxRuleSet
ExcludedRules: []
- Name: RuleWithAWSManagedRulesUnixRuleSet
Priority: 5
OverrideAction:
None: {}
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: RuleWithAWSManagedRulesUnixRuleSet
Statement:
ManagedRuleGroupStatement:
VendorName: AWS
Name: AWSManagedRulesUnixRuleSet
ExcludedRules: []
DNSRecordCNAME2PublicElasticLoadBalancerV2:
Type: AWS::Route53::RecordSet
Condition: useDomainName
DependsOn: PublicElasticLoadBalancerV2
Properties:
HostedZoneName: !Join
- ''
- - !Ref 'DomainName'
- .
Comment: !Join
- ''
- - !Ref 'DomainNamePrefix'
- .
- !Ref 'DomainName'
- .
Name: !Join
- ''
- - !Ref 'DomainNamePrefix'
- .
- !Ref 'DomainName'
- .
Type: CNAME
TTL: '900'
ResourceRecords:
- !GetAtt 'PublicElasticLoadBalancerV2.DNSName'
LoadBalancerListenerHttpsCert:
Type: AWS::CertificateManager::Certificate
Properties:
CertificateTransparencyLoggingPreference: ENABLED
DomainName: !Join
- ''
- - !Ref 'DomainNamePrefix'
- .
- !Ref 'DomainName'
- ''
ValidationMethod: DNS
LoadBalancerListenerHttpsV2:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref 'TargetGroupHttps'
LoadBalancerArn: !Ref 'PublicElasticLoadBalancerV2'
Certificates:
- CertificateArn: !Ref 'LoadBalancerListenerHttpsCert'
Port: 443
Protocol: HTTPS
SslPolicy: ELBSecurityPolicy-FS-1-2-Res-2019-08
TargetGroupHttps:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: CIA-Https
Port: 8443
Protocol: HTTPS
VpcId: !Ref 'VPC'
HealthCheckPort: 8443
HealthCheckIntervalSeconds: 5
HealthCheckProtocol: HTTPS
HealthCheckPath: /healthcheck/
HealthCheckTimeoutSeconds: 4
HealthyThresholdCount: 2
UnhealthyThresholdCount: 6
Matcher:
HttpCode: '200'
TargetGroupAttributes:
- Key: stickiness.enabled
Value: 'true'
- Key: stickiness.type
Value: lb_cookie
- Key: stickiness.lb_cookie.duration_seconds
Value: '180'
DependsOn:
- PublicElasticLoadBalancerV2
PublicLoadBalancerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Public ELB Security Group with HTTPS access on port 443 from the internet
VpcId: !Ref 'VPC'
SecurityGroupIngress:
- Description: Inbound https from public
IpProtocol: tcp
FromPort: '443'
ToPort: '443'
CidrIp: '0.0.0.0/0'
SecurityGroupEgress:
- Description: Outbound https from loadbalancer to server
IpProtocol: tcp
FromPort: '8443'
ToPort: '8443'
CidrIp: !FindInMap
- SubnetConfig
- VPC
- CIDR
WebServerFleet:
Type: AWS::AutoScaling::AutoScalingGroup
DependsOn:
- PublicRoute
- Database
Properties:
AvailabilityZones:
- !GetAtt 'PrivateAppSubnet.AvailabilityZone'
- !GetAtt 'PrivateAppSubnet2.AvailabilityZone'
- !GetAtt 'PrivateAppSubnet3.AvailabilityZone'
VPCZoneIdentifier:
- !Ref 'PrivateAppSubnet'
- !Ref 'PrivateAppSubnet2'
- !Ref 'PrivateAppSubnet3'
LaunchConfigurationName: !Ref 'WebServerLaunchConfig'
MinSize: '2'
MaxSize: '3'
DesiredCapacity: !Ref 'WebServerCount'
TargetGroupARNs:
- !Ref 'TargetGroupHttps'
Tags:
- Key: Network
Value: Public
PropagateAtLaunch: 'true'
- Key: SecurityBaselineCheck
Value: 'true'
PropagateAtLaunch: 'true'
- Key: UpdateSSMAgent
Value: 'true'
PropagateAtLaunch: 'true'
- Key: GatherSoftwareInventory
Value: 'true'
PropagateAtLaunch: 'true'
- Key: SystemPatchComplianceMaintenanceWindowActive
Value: 'true'
PropagateAtLaunch: 'true'
CreationPolicy:
ResourceSignal:
Timeout: PT45M
Count: !Ref 'WebServerCount'
UpdatePolicy:
AutoScalingRollingUpdate:
MinInstancesInService: '1'
MaxBatchSize: '1'
PauseTime: PT15M
WaitOnResourceSignals: 'true'
WebServerScaleUpPolicy:
Type: AWS::AutoScaling::ScalingPolicy
Properties:
AdjustmentType: ChangeInCapacity
AutoScalingGroupName: !Ref 'WebServerFleet'
Cooldown: '60'
ScalingAdjustment: '1'
WebServerScaleDownPolicy:
Type: AWS::AutoScaling::ScalingPolicy
Properties:
AdjustmentType: ChangeInCapacity
AutoScalingGroupName: !Ref 'WebServerFleet'
Cooldown: '60'
ScalingAdjustment: '-1'
CPUAlarmHigh:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmDescription: Scale-up if CPU > 60% for 30 minutes
MetricName: CPUUtilization
Namespace: AWS/EC2
Statistic: Average
Period: '900'
EvaluationPeriods: '2'
Threshold: '60'
AlarmActions:
- !Ref 'WebServerScaleUpPolicy'
Dimensions:
- Name: AutoScalingGroupName
Value: !Ref 'WebServerFleet'
ComparisonOperator: GreaterThanThreshold
CPUAlarmLow:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmDescription: Scale-down if CPU < 40% for 30 minutes
MetricName: CPUUtilization
Namespace: AWS/EC2
Statistic: Average
Period: '900'
EvaluationPeriods: '2'
Threshold: '40'
AlarmActions:
- !Ref 'WebServerScaleDownPolicy'
Dimensions:
- Name: AutoScalingGroupName
Value: !Ref 'WebServerFleet'
ComparisonOperator: LessThanThreshold
InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref 'EC2SSMRole'
InstanceProfileName: EC2SSMInstanceProfile
EC2SSMRole:
Type: AWS::IAM::Role
Properties:
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: rdssecretaccess
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- secretsmanager:DescribeSecret
- secretsmanager:GetSecretValue
Resource: !Ref 'RDSInstanceRotationSecret'
- PolicyName: limitedssm
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ssm:PutInventory
- ssm:PutComplianceItems
- ssm:PutConfigurePackageResult
- ssm:UpdateAssociationStatus
- ssm:UpdateInstanceAssociationStatus
- ssm:UpdateInstanceInformation
- ssm:ListAssociations
- ssm:DescribeAssociation
- ssm:ListInstanceAssociations
- ec2:DescribeInstanceStatus
- ec2messages:AcknowledgeMessage
- ec2messages:DeleteMessage
- ec2messages:FailMessage
- ec2messages:GetEndpoint
- ec2messages:GetMessages
- ec2messages:SendReply
- ssmmessages:CreateControlChannel
- ssmmessages:CreateDataChannel
- ssmmessages:OpenControlChannel
- ssmmessages:OpenDataChannel
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:DescribeLogGroups
- logs:DescribeLogStreams
- logs:PutLogEvents
- ssm:CreateAssociation
- ssm:CreateAssociationBatch
- ssm:RegisterTargetWithMaintenanceWindow
- ssm:RegisterTaskWithMaintenanceWindow
- ssm:StartAutomationExecution
- ssm:UpdateMaintenanceWindowTarget
- ssm:UpdateMaintenanceWindowTask
- cloudwatch:DescribeAlarmHistory
- cloudwatch:DescribeAlarms
- cloudwatch:GetMetricData
- cloudwatch:GetMetricStatistics
- cloudwatch:ListMetrics
- cloudwatch:PutMetricAlarm
- cloudwatch:PutMetricData
Resource: '*'
- PolicyName: allowgathersoftware
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: ssm:GetDocument
Resource: !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}::document/AWS-GatherSoftwareInventory'
- PolicyName: allowrunpatchline
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: ssm:GetDocument
Resource: !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}::document/AWS-RunPatchBaseline'
- PolicyName: allowupdatessmagent
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: ssm:GetDocument
Resource: !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}::document/AWS-UpdateSSMAgent'
RoleName: EC2SSMRole
WebServerLaunchConfig:
Type: AWS::AutoScaling::LaunchConfiguration
Metadata:
AWS::CloudFormation::Init:
config:
packages:
apt:
openjdk-17-jdk-headless: []
ntp: []
ntpdate: []
ufw: []
unzip: []
language-pack-en: []
apt-listchanges: []
debian-goodies: []
needrestart: []
apt-show-versions: []
sysstat: []
debsecan: []
debsums: []
fail2ban: []
files:
/etc/cfn/cfn-hup.conf:
content: !Join
- ''
- - "[main]\n"
- stack=
- !Ref 'AWS::StackId'
- "\n"
- region=
- !Ref 'AWS::Region'
- "\n"
mode: '000400'
owner: root
group: root
/etc/cfn/hooks.d/cfn-auto-reloader.conf:
content: !Join
- ''
- - "[cfn-auto-reloader-hook]\n"
- "triggers=post.update\n"
- "path=Resources.WebServerLaunchConfig.Metadata.AWS::CloudFormation::Init\n"
- 'action=/opt/aws/bin/cfn-init -v '
- ' --stack '
- !Ref 'AWS::StackName'
- ' --resource WebServerLaunchConfig '
- ' --region '
- !Ref 'AWS::Region'
- "\n"
- "runas=root\n"
/lib/systemd/system/cfn-hup.service:
content: !Join
- ''
- - "[Unit]\n"
- "Description=cfn-hup daemon\n\n"
- "[Service]\n"
- "Type=simple\n"
- "ExecStart=/opt/aws/bin/cfn-hup\n"
- "Restart=always\n\n"
- "[Install]\n"
- WantedBy=multi-user.target
/etc/awslogs.conf:
content: !Join
- ''
- - '[general]'
- "\n"
- state_file = /var/awslogs/state/agent-state
- "\n"
- "\n"
- '[userdata]'
- "\n"
- datetime_format = %Y-%m-%d %H:%M:%S
- "\n"
- file = /var/log/user-data.log
- "\n"
- buffer_duration = 5000
- "\n"
- log_stream_name = {instance_id}-{hostname}-{ip_address}-userdata
- "\n"
- initial_position = start_of_file
- "\n"
- log_group_name = ec2awslogs
- "\n"
- "\n"
- '[auth]'
- "\n"
- datetime_format = %Y-%m-%d %H:%M:%S
- "\n"
- file = /var/log/auth.log
- "\n"
- buffer_duration = 5000
- "\n"
- log_stream_name = {instance_id}-{hostname}-{ip_address}-auth
- "\n"
- initial_position = start_of_file
- "\n"
- log_group_name = ec2awslogs
- "\n"
- "\n"
- '[syslog]'
- "\n"
- datetime_format = %Y-%m-%d %H:%M:%S
- "\n"
- file = /var/log/syslog
- "\n"
- buffer_duration = 5000
- "\n"
- log_stream_name = {instance_id}-{hostname}-{ip_address}-syslog
- "\n"
- initial_position = start_of_file
- "\n"
- log_group_name = ec2awslogs
- "\n"
- "\n"
- '[kern.log]'
- "\n"
- datetime_format = %Y-%m-%d %H:%M:%S
- "\n"
- file = /var/log/kern.log
- "\n"
- buffer_duration = 5000
- "\n"
- log_stream_name = {instance_id}-{hostname}-{ip_address}-kern.log
- "\n"
- initial_position = start_of_file
- "\n"
- log_group_name = ec2awslogs
- "\n"
- '[rkhunter.log]'
- "\n"
- datetime_format = %Y-%m-%d %H:%M:%S
- "\n"
- file = /var/log/rkhunter.log
- "\n"
- buffer_duration = 5000
- "\n"
- log_stream_name = {instance_id}-{hostname}-{ip_address}-rkhunter.log
- "\n"
- initial_position = start_of_file
- "\n"
- log_group_name = ec2awslogs
- "\n"
- '[lynis.log]'
- "\n"
- datetime_format = %Y-%m-%d %H:%M:%S
- "\n"
- file = /var/log/lynis.log
- "\n"
- buffer_duration = 5000
- "\n"
- log_stream_name = {instance_id}-{hostname}-{ip_address}-lynis.log
- "\n"
- initial_position = start_of_file
- "\n"
- log_group_name = ec2awslogs
- "\n"
- '[ciaapp.log]'
- "\n"
- datetime_format = %Y-%m-%d %H:%M:%S
- "\n"
- file = /opt/cia/cia-base/logs/*.log
- "\n"
- buffer_duration = 5000
- "\n"
- log_stream_name = {instance_id}-{hostname}-{ip_address}-ciaapp.log
- "\n"
- initial_position = start_of_file
- "\n"
- log_group_name = ec2awslogs
- "\n"
mode: '000644'
owner: root
group: root
services: {}
Properties:
ImageId: !Ref 'WebServerAmi'
MetadataOptions:
HttpEndpoint: enabled
HttpPutResponseHopLimit: 1
HttpTokens: required
IamInstanceProfile: !Ref 'InstanceProfile'
EbsOptimized: 'true'
SecurityGroups:
- !Ref 'WebServerSecurityGroup'
InstanceType: !Ref 'WebServerInstanceType'
AssociatePublicIpAddress: 'false'
UserData: !Base64
Fn::Join:
- ''
- - "#!/bin/bash -xe\n"
- "exec > >(tee /var/log/user-data.log) 2>&1 \n"
- "export DEBIAN_FRONTEND=noninteractive\n"
- "apt_get_install()\n"
- "{\n"
- DEBIAN_FRONTEND=noninteractive apt-get -y \n
- -o DPkg::Options::=--force-confnew \n
- "install $@\n"
- "}\n"
- "timedatectl set-timezone Europe/Stockholm \n"
- "dpkg-reconfigure -f noninteractive tzdata \n"
- "echo 'LANG=\"en_US.UTF-8\"'> /etc/default/locale\n"
- "locale-gen en_US.UTF-8 en_GB.UTF-8 sv_SE.UTF-8 \n"
- "dpkg-reconfigure --frontend=noninteractive locales \n"
- "apt-get update\n"
- "apt-get -y install apt-transport-https\n"
- "apt-get update\n"
- "apt-get -y dist-upgrade\n"
- "apt-get -y install python3-pip python3-setuptools unattended-upgrades chrony\n"
- "echo 'server 169.254.169.123 prefer iburst minpoll 4 maxpoll 4' >> /etc/chrony/chrony.conf \n"
- "sudo /etc/init.d/chrony restart\n"
- "sudo pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz\n"
- "mkdir -p /opt/aws/bin\n"
- "sudo ln -s /usr/local/bin/cfn* /opt/aws/bin\n"
- "##systemctl enable cfn-hup.service \n"
- "##systemctl start cfn-hup.service \n"
- "# Install the sample application\n"
- '/opt/aws/bin/cfn-init -v '
- ' --stack '
- !Ref 'AWS::StackName'
- ' --resource WebServerLaunchConfig '
- ' --region '
- !Ref 'AWS::Region'
- "\n"
- "ufw default allow outgoing\n"
- "ufw default deny incoming\n"
- "sudo /etc/init.d/ssh stop\n"
- "sudo apt-get -y purge openssh-server\n"
- "ufw allow 8443/tcp\n"
- "ufw allow 53\n"
- "ufw allow 123/udp\n"
- "ufw enable\n"
- "#ufw disable\n"
- "ufw status verbose\n"
- "echo 'Sysctl hardening'\n"
- "echo 'kernel.core_uses_pid = 1' >> /etc/sysctl.conf \n"
- "echo 'kernel.sysrq = 0' >> /etc/sysctl.conf \n"
- "echo 'kernel.kptr_restrict = 2' >> /etc/sysctl.conf \n"
- "echo 'net.ipv4.conf.all.rp_filter = 1' >> /etc/sysctl.conf \n"
- "echo 'net.ipv4.conf.default = 1' >> /etc/sysctl.conf \n"
- "echo 'net.ipv4.conf.all.accept_redirects = 0' >> /etc/sysctl.conf \n"
- "echo 'net.ipv4.conf.default.accept_redirects = 0' >> /etc/sysctl.conf \n"
- "echo 'net.ipv4.conf.all.accept_redirects = 0' >> /etc/sysctl.conf \n"
- "echo 'net.ipv4.conf.all.log_martians = 1' >> /etc/sysctl.conf \n"
- "echo 'net.ipv4.conf.default.log_martians = 1' >> /etc/sysctl.conf \n"
- "echo 'net.ipv4.conf.all.log_martians = 1' >> /etc/sysctl.conf \n"
- "echo 'net.ipv4.conf.all.send_redirects = 0' >> /etc/sysctl.conf \n"
- "echo 'net.ipv4.conf.default.accept_source_route = 0' >> /etc/sysctl.conf \n"
- "echo 'net.ipv4.tcp_syncookies = 1' >> /etc/sysctl.conf \n"
- "echo 'net.ipv4.tcp_timestamps = 0' >> /etc/sysctl.conf \n"
- "echo 'net.ipv6.conf.all.accept_redirects = 0' >> /etc/sysctl.conf \n"
- "echo 'net.ipv6.conf.default.accept_redirects = 0' >> /etc/sysctl.conf \n"
- "sysctl -p\n"
- "cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local\n"
- "sudo snap install amazon-ssm-agent --classic\n"
- "sudo systemctl start snap.amazon-ssm-agent.amazon-ssm-agent.service\n"
- "sudo apt-get -y install curl python software-properties-common xz-utils bzip2 gnupg wget graphviz\n"
- "sudo wget -O /tmp/awslogs-agent-setup.py https://s3.amazonaws.com/aws-cloudwatch/downloads/latest/awslogs-agent-setup.py\n"
- "sudo chmod 775 /tmp/awslogs-agent-setup.py\n"
- "sudo mkdir -p /var/awslogs/etc/\n"
- "sudo /tmp/awslogs-agent-setup.py -n -r eu-west-1 -c /etc/awslogs.conf\n"
- "sudo apt-get -y install libwww-perl libdatetime-perl\n"
- "# INSTALL OPEN JDK\n"
- "sudo ln -s /usr/lib/jvm/java-17-openjdk-arm64 /usr/lib/jvm/jdk-17\n"
- "wget https://corretto.aws/downloads/latest/amazon-corretto-19-aarch64-linux-jdk.deb\n"
- "dpkg -i amazon-corretto-19-aarch64-linux-jdk.deb\n"
- "rm amazon-corretto-19-aarch64-linux-jdk.deb\n"
- "ln -s /usr/lib/jvm/java-19-amazon-corretto /usr/lib/jvm/jdk-19\n"
- 'wget '
- !Ref 'DebPackageUrl'
- " -O cia-dist-deb.deb \n"
- "dpkg -i --force all cia-dist-deb.deb \n"
- "/etc/init.d/cia stop\n"
- echo 'database.name=
- !Ref 'DBName'
- "' >> /opt/cia/cia-base/webapps/cia/WEB-INF/database.properties\n"
- echo 'database.username=allhaildiscordia
- "' >> /opt/cia/cia-base/webapps/cia/WEB-INF/database.properties\n"
- echo 'database.password=legalizeit
- "' >> /opt/cia/cia-base/webapps/cia/WEB-INF/database.properties\n"
- echo 'database.aws.secret.arn=
- !Ref 'RDSInstanceRotationSecret'
- "' >> /opt/cia/cia-base/webapps/cia/WEB-INF/database.properties\n"
- echo 'database.aws.secret=true
- "' >> /opt/cia/cia-base/webapps/cia/WEB-INF/database.properties\n"
- echo 'database.hostname=
- !GetAtt 'Database.Endpoint.Address'
- "' >> /opt/cia/cia-base/webapps/cia/WEB-INF/database.properties\n"
- "sed -i -e 's_\">/cia<_\">/<_' /opt/cia/cia-base/webapps/cia.xml\n"
- "TOKEN=$(curl -X PUT 'http://169.254.169.254/latest/api/token' -H 'X-aws-ec2-metadata-token-ttl-seconds: 21600')\n"
- "LOCAL_IP=$(curl -H 'X-aws-ec2-metadata-token: $TOKEN' http://169.254.169.254/latest/meta-data/local-ipv4)\n"
- "HISTCONTROL=ignorespace\n"
- "KEY_PWD=$(< /dev/urandom tr -dc A-Z-a-z-0-9 | head -c${1:-32};echo)\n"
- "rm -rf /opt/cia/cia-base/etc/keystore.p12\n"
- '/usr/lib/jvm/jdk-17/bin/keytool -genkey -noprompt -deststoretype pkcs12 -sigalg SHA256withRSA -validity 365 -keyalg RSA -keysize 4096 '
- ' -alias jetty -dname ''CN=$LOCAL_IP, OU=None, O=None, L=None, S=None, C=SE'' -keystore /opt/cia/cia-base/etc/keystore.p12 '
- " -storepass $KEY_PWD -keypass $KEY_PWD\n"
- "chown cia:cia /opt/cia/cia-base/etc/keystore.p12\n"
- "chmod 700 /opt/cia/cia-base/etc/keystore.p12\n"
- " sed -i -e 's_jetty.sslContext.keyManagerPassword=changeit_jetty.sslContext.keyManagerPassword='$KEY_PWD'_' /opt/cia/cia-base/start.d/ssl.ini\n"
- " sed -i -e 's_jetty.sslContext.keyStorePassword=changeit_jetty.sslContext.keyStorePassword='$KEY_PWD'_' /opt/cia/cia-base/start.d/ssl.ini\n"
- "TOTAL_MEM_KB=$(awk '/MemTotal:/ { print $2 }' /proc/meminfo)\n"
- "let JAVAMEMSIZE=$TOTAL_MEM_KB-3046*1024\n"
- "sed -i -e 's_4096m_'$JAVAMEMSIZE'k_' /etc/default/cia\n"
- '# sed -i -e ''s/# -DUSE_LOG_APPENDER='
- !Ref 'WebServerAppLogAppender'
- / -DUSE_LOG_APPENDER=
- !Ref 'WebServerAppLogAppender'
- "/' /etc/default/cia\n"
- "wget https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem\n"
- "mkdir /opt/cia/.postgresql\n"
- "cp rds-combined-ca-bundle.pem /opt/cia/.postgresql/root.crt\n"
- "chown -R cia:cia /opt/cia/.postgresql\n"
- "chmod -R 700 /opt/cia/.postgresql\n"
- "/usr/lib/jvm/jdk-17/bin/keytool -import -keystore /etc/ssl/certs/java/cacerts -file rds-combined-ca-bundle.pem -storepass 'changeit' -noprompt -alias aws-rds-2016\n"
- "/etc/init.d/cia stop\n"
- "/etc/init.d/cia start\n"
- "update-rc.d cia defaults\n"
- "# Signal completion\n"
- '/opt/aws/bin/cfn-signal -e $? '
- ' --stack '
- !Ref 'AWS::StackId'
- ' --resource WebServerFleet '
- ' --region '
- !Ref 'AWS::Region'
- "\n"
WebServerSecurityGroup:
Type: AWS::EC2::SecurityGroup
DependsOn: PublicLoadBalancerSecurityGroup
Properties:
GroupDescription: Allow access from load balancer and bastion as well as outbound HTTP and HTTPS traffic
VpcId: !Ref 'VPC'
SecurityGroupIngress:
- Description: Loadbalancer inbound access
IpProtocol: tcp
FromPort: '8443'
ToPort: '8443'
SourceSecurityGroupId: !Ref 'PublicLoadBalancerSecurityGroup'
SecurityGroupEgress:
- Description: Loadbalancer outbound access
IpProtocol: tcp
FromPort: '8443'
ToPort: '8443'
SourceSecurityGroupId: !Ref 'PublicLoadBalancerSecurityGroup'
- Description: Server outbound http access
IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: '0.0.0.0/0'
- Description: Server outbound https access
IpProtocol: tcp
FromPort: '443'
ToPort: '443'
CidrIp: '0.0.0.0/0'
- Description: Server outbound postgres access
IpProtocol: tcp
FromPort: '5432'
ToPort: '5432'
CidrIp: !FindInMap
- SubnetConfig
- VPC
- CIDR
- Description: Server outbound ntp access
IpProtocol: udp
FromPort: '123'
ToPort: '123'
CidrIp: '0.0.0.0/0'
- Description: Server outbound udp dns access
IpProtocol: udp
FromPort: '53'
ToPort: '53'
CidrIp: '0.0.0.0/0'
- Description: Server outbound tcp dns access
IpProtocol: tcp
FromPort: '53'
ToPort: '53'
CidrIp: '0.0.0.0/0'
Outputs:
WebSite:
Description: URL of the website
Value: !Join
- ''
- - https://
- !GetAtt 'PublicElasticLoadBalancerV2.DNSName'
JDBCConnectionString:
Description: JDBC connection string for database
Value: !Join
- ''
- - jdbc:postgresql://
- !GetAtt 'Database.Endpoint.Address'
- ':'
- !GetAtt 'Database.Endpoint.Port'
- /
- !Ref 'DBName'