<commit_before>AWSTemplateFormatVersion: 2010-09-09
Description: >-
  concerto-ec2-basic-tls (2020-05-15): Standalone Concerto, everything on one instance, not monitored
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Concerto
        Parameters:
          - ConcertoVersion
          - ConcertoPassword
          - ConcertoContentURL
          - ConcertoSessionLimit
          - ConcertoTimezone
          - ConcertoStorage
      - Label:
          default: Web
        Parameters:
          - DomainName
          - Email
          - KeyName
          - WebServerInstanceType
      - Label:
          default: Database
        Parameters:
          - DBPassword
      - Label:
          default: Network
        Parameters:
          - CIDR
          - CIDRA
    ParameterLabels:
      ConcertoVersion:
        default: Tag
      ConcertoPassword:
        default: Password
      ConcertoContentURL:
        default: URL
      ConcertoSessionLimit:
        default: Session limit
      ConcertoTimezone:
        default: Timezone
      ConcertoStorage:
        default: Storage
      DomainName:
        default: Domain
      Email:
        default: E-Mail
      KeyName:
        default: Key pair
      WebServerInstanceType:
        default: Instance type
      DBPassword:
        default: Password
      CIDRA:
        default: CIDR for Subnet A

Parameters:
  ConcertoVersion:
    Description: Concerto version to use. Can be any tag from https://github.com/campsych/concerto-platform, defaults to latest stable.
    Type: String
    Default: 5.0
  ConcertoPassword:
    Description: Concerto admin account password (recommended to generate a random one)
    Type: String
    NoEcho: true
    MinLength: 8
    ConstraintDescription: min. 8 characters.
  ConcertoContentURL:
    Description: URL with Concerto content backup to preload (optional).
    Type: String
    Default: .
  ConcertoSessionLimit:
    Description: How many concurrent sessions to allow (unlimited if not set).
    Type: Number
    Default: 10
  ConcertoTimezone:
    Description: Timezone to use, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones.
    Type: String
    Default: Europe/London
  ConcertoStorage:
    Default: 8
    Description: The size of the storage volume (Gb)
    Type: Number
    MinValue: 8
    MaxValue: 1024
    ConstraintDescription: must be between 8 and 1024Gb.
  DomainName:
    Description: The domain name to be used for Concerto, you need to own it
    Type: String
    AllowedPattern: (?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]
    ConstraintDescription: must be a valid domain name.
  Email:
    Description: Your email to be used with Lets Encrypt CA
    Type: String
    AllowedPattern: ([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)
    ConstraintDescription: must be a valid email.
  KeyName:
    Description: Name of an existing EC2 KeyPair to enable SSH access to the instance
    Type: AWS::EC2::KeyPair::KeyName
    ConstraintDescription: must be the name of an existing EC2 KeyPair.
  WebServerInstanceType:
    Description: Web server instance type.
    Type: String
    Default: t2.micro
    AllowedValues:
      - t2.micro
      - t3.micro
      - t2.small
      - t3.small
      - t2.medium
      - t3.medium
      - t2.large
      - t3.large
    ConstraintDescription: must be a valid EC2 instance type.
  DBPassword:
    Description: Database admin account password (recommended to generate a random one, different than Concerto's)
    Type: String
    NoEcho: true
    MinLength: 8
    ConstraintDescription: min. 8 characters
  CIDR:
    Description: The private IP address range to be used for VPC
    Type: String
    MinLength: 9
    MaxLength: 18
    Default: 10.10.0.0/16
    AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
  CIDRA:
    Description: Address range within the VPC to be used for the subnet
    Type: String
    MinLength: 9
    MaxLength: 18
    Default: 10.10.0.0/24
    AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.

Mappings:
  RegionMap:
    us-east-1:
      ami: ami-0de53d8956e8dcf80
    us-east-2:
      ami: ami-02bcbb802e03574ba
    us-west-1:
      ami: ami-0019ef04ac50be30f
    us-west-2:
      ami: ami-061392db613a6357b
    eu-west-1:
      ami: ami-07683a44e80cd32c5
    eu-west-2:
      ami: ami-09ead922c1dad67e4
    eu-west-3:
      ami: ami-0451ae4fd8dd178f7
    eu-central-1:
      ami: ami-09def150731bdbcc2
    eu-north-1:
      ami: ami-d16fe6af
    ap-northeast-1:
      ami: ami-0f9ae750e8274075b
    ap-northeast-2:
      ami: ami-047f7b46bd6dd5d84
    ap-southeast-1:
      ami: ami-0b419c3a4b01d1859
    ap-southeast-2:
      ami: ami-04481c741a0311bbb
    ap-south-1:
      ami: ami-0889b8a448de4fc44
    ca-central-1:
      ami: ami-03338e1f67dae0168
    sa-east-1:
      ami: ami-0669a96e355eac82f

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref CIDR
      Tags:
        - Key: Name
          Value: !Ref AWS::StackName
  SubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: !Ref CIDRA
      Tags:
        - Key: Name
          Value: !Ref AWS::StackName
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Ref AWS::StackName
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway
  RouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Ref AWS::StackName
  Route:
    Type: AWS::EC2::Route
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  SubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref SubnetA
      RouteTableId: !Ref RouteTable
  NetworkAcl:
    Type: AWS::EC2::NetworkAcl
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Ref AWS::StackName
  InboundHTTPNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: 100
      Protocol: 6
      RuleAction: allow
      Egress: false
      CidrBlock: 0.0.0.0/0
      PortRange:
        From: 80
        To: 80
  InboundHTTPSNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: 101
      Protocol: 6
      RuleAction: allow
      Egress: false
      CidrBlock: 0.0.0.0/0
      PortRange:
        From: 443
        To: 443
  InboundSSHNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: 102
      Protocol: 6
      RuleAction: allow
      Egress: false
      CidrBlock: 0.0.0.0/0
      PortRange:
        From: 22
        To: 22
  InboundResponsePortsNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: 103
      Protocol: 6
      RuleAction: allow
      Egress: false
      CidrBlock: 0.0.0.0/0
      PortRange:
        From: 1024
        To: 65535
  OutBoundHTTPNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: 100
      Protocol: 6
      RuleAction: allow
      Egress: true
      CidrBlock: 0.0.0.0/0
      PortRange:
        From: 80
        To: 80
  OutBoundHTTPSNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: 101
      Protocol: 6
      RuleAction: allow
      Egress: true
      CidrBlock: 0.0.0.0/0
      PortRange:
        From: 443
        To: 443
  OutBoundResponsePortsNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: 102
      Protocol: 6
      RuleAction: allow
      Egress: true
      CidrBlock: 0.0.0.0/0
      PortRange:
        From: 1024
        To: 65535
  SubnetNetworkAclAssociation:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Properties:
      SubnetId: !Ref SubnetA
      NetworkAclId: !Ref NetworkAcl
  WebServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref VPC
      GroupDescription: Allow ingress via SSH, HTTP, HTTPS ports
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: !Ref AWS::StackName
  WebServerInstance:
    Type: AWS::EC2::Instance
    CreationPolicy:
      ResourceSignal:
        Count: 1
        Timeout: PT15M
      AWS::CloudFormation::Init:
        configSets:
          default:
            - cfn
            - updates
            - docker
            - tls
            - concerto
        cfn:
          files:
            /etc/cfn/cfn-hup.conf:
              content: !Sub |
                [main]
                stack=${AWS::StackId}
                region=${AWS::Region}
                interval=1
              mode: 000400
              owner: root
              group: root
            /etc/cfn/hooks.d/cfn-auto-reloader.conf:
              content: !Sub |
                [cfn-auto-reloader-hook]
                triggers=post.update
                path=Resources.WebServerInstance.Metadata.AWS::CloudFormation::Init
                action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region}
                runas=root
              mode: 000400
              owner: root
              group: root
          services:
            sysvinit:
              cfn-hup:
                enabled: true
                ensureRunning: true
                files:
                  - /etc/cfn/cfn-hup.conf
                  - /etc/cfn/hooks.d/cfn-auto-reloader.conf
        updates:
          packages:
            yum:
              yum-cron: []
          commands:
            01_config:
              command: |
                sed -ri 's/^(.*update_cmd\s*=).*$/\1 security/' yum-cron.conf
                sed -ri 's/^(.*apply_updates\s*=).*$/\1 yes/' yum-cron.conf
              cwd: /etc/yum
          services:
            sysvinit:
              yum-cron:
                enabled: true
                ensureRunning: true
        docker:
          commands:
            01_user:
              command: usermod -a -G docker ec2-user
            02_docker_compose:
              command: |
                curl -L https://github.com/docker/compose/releases/download/1.24.0/docker-compose-Linux-x86_64 -o /usr/local/bin/docker-compose
                chmod +x /usr/local/bin/docker-compose
          services:
            sysvinit:
              docker:
                enabled: true
                ensureRunning: true
        tls:
          files:
            /home/concerto/data/nginx/app.conf:
              content: !Sub |
                server {
                    listen [::]:80 default_server;
                    listen 80 default_server;

                    location /.well-known/acme-challenge/ {
                        root /var/www/certbot;
                        allow all;
                    }

                    location / {
                        return 301 https://$host$request_uri;
                    }
                }

                server {
                    listen [::]:443 ssl http2;
                    listen 443 ssl http2;
                    server_name ${DomainName};

                    ssl_certificate /etc/letsencrypt/dummy/${DomainName}/fullchain.pem;
                    ssl_certificate_key /etc/letsencrypt/dummy/${DomainName}/privkey.pem;
                    ssl_protocols TLSv1.2 TLSv1.3;
                    ssl_prefer_server_ciphers on;
                    ssl_dhparam /etc/letsencrypt/dhparam.pem;
                    ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
                    ssl_session_timeout 1d;
                    ssl_session_cache shared:SSL:50m;
                    add_header Strict-Transport-Security max-age=15768000;

                    client_max_body_size 50M;

                    location / {
                        proxy_pass http://concerto;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                        proxy_set_header X-Forwarded-Proto $scheme;
                        proxy_set_header Host $http_host;<commit_msg>Improve ssl settings in cf template<commit_after>AWSTemplateFormatVersion: 2010-09-09
Description: >-
  concerto-ec2-basic-tls (2020-05-20): Standalone Concerto, everything on one instance, not monitored
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Concerto
        Parameters:
          - ConcertoVersion
          - ConcertoPassword
          - ConcertoContentURL
          - ConcertoSessionLimit
          - ConcertoTimezone
          - ConcertoStorage
      - Label:
          default: Web
        Parameters:
          - DomainName
          - Email
          - KeyName
          - WebServerInstanceType
      - Label:
          default: Database
        Parameters:
          - DBPassword
      - Label:
          default: Network
        Parameters:
          - CIDR
          - CIDRA
    ParameterLabels:
      ConcertoVersion:
        default: Tag
      ConcertoPassword:
        default: Password
      ConcertoContentURL:
        default: URL
      ConcertoSessionLimit:
        default: Session limit
      ConcertoTimezone:
        default: Timezone
      ConcertoStorage:
        default: Storage
      DomainName:
        default: Domain
      Email:
        default: E-Mail
      KeyName:
        default: Key pair
      WebServerInstanceType:
        default: Instance type
      DBPassword:
        default: Password
      CIDRA:
        default: CIDR for Subnet A

Parameters:
  ConcertoVersion:
    Description: Concerto version to use. Can be any tag from https://github.com/campsych/concerto-platform, defaults to latest stable.
    Type: String
    Default: 5.0
  ConcertoPassword:
    Description: Concerto admin account password (recommended to generate a random one)
    Type: String
    NoEcho: true
    MinLength: 8
    ConstraintDescription: min. 8 characters.
  ConcertoContentURL:
    Description: URL with Concerto content backup to preload (optional).
    Type: String
    Default: .
  ConcertoSessionLimit:
    Description: How many concurrent sessions to allow (unlimited if not set).
    Type: Number
    Default: 10
  ConcertoTimezone:
    Description: Timezone to use, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones.
    Type: String
    Default: Europe/London
  ConcertoStorage:
    Default: 8
    Description: The size of the storage volume (Gb)
    Type: Number
    MinValue: 8
    MaxValue: 1024
    ConstraintDescription: must be between 8 and 1024Gb.
  DomainName:
    Description: The domain name to be used for Concerto, you need to own it
    Type: String
    AllowedPattern: (?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]
    ConstraintDescription: must be a valid domain name.
  Email:
    Description: Your email to be used with Lets Encrypt CA
    Type: String
    AllowedPattern: ([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)
    ConstraintDescription: must be a valid email.
  KeyName:
    Description: Name of an existing EC2 KeyPair to enable SSH access to the instance
    Type: AWS::EC2::KeyPair::KeyName
    ConstraintDescription: must be the name of an existing EC2 KeyPair.
  WebServerInstanceType:
    Description: Web server instance type.
    Type: String
    Default: t2.micro
    AllowedValues:
      - t2.micro
      - t3.micro
      - t2.small
      - t3.small
      - t2.medium
      - t3.medium
      - t2.large
      - t3.large
    ConstraintDescription: must be a valid EC2 instance type.
  DBPassword:
    Description: Database admin account password (recommended to generate a random one, different than Concerto's)
    Type: String
    NoEcho: true
    MinLength: 8
    ConstraintDescription: min. 8 characters
  CIDR:
    Description: The private IP address range to be used for VPC
    Type: String
    MinLength: 9
    MaxLength: 18
    Default: 10.10.0.0/16
    AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
  CIDRA:
    Description: Address range within the VPC to be used for the subnet
    Type: String
    MinLength: 9
    MaxLength: 18
    Default: 10.10.0.0/24
    AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.

Mappings:
  RegionMap:
    us-east-1:
      ami: ami-0de53d8956e8dcf80
    us-east-2:
      ami: ami-02bcbb802e03574ba
    us-west-1:
      ami: ami-0019ef04ac50be30f
    us-west-2:
      ami: ami-061392db613a6357b
    eu-west-1:
      ami: ami-07683a44e80cd32c5
    eu-west-2:
      ami: ami-09ead922c1dad67e4
    eu-west-3:
      ami: ami-0451ae4fd8dd178f7
    eu-central-1:
      ami: ami-09def150731bdbcc2
    eu-north-1:
      ami: ami-d16fe6af
    ap-northeast-1:
      ami: ami-0f9ae750e8274075b
    ap-northeast-2:
      ami: ami-047f7b46bd6dd5d84
    ap-southeast-1:
      ami: ami-0b419c3a4b01d1859
    ap-southeast-2:
      ami: ami-04481c741a0311bbb
    ap-south-1:
      ami: ami-0889b8a448de4fc44
    ca-central-1:
      ami: ami-03338e1f67dae0168
    sa-east-1:
      ami: ami-0669a96e355eac82f

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref CIDR
      Tags:
        - Key: Name
          Value: !Ref AWS::StackName
  SubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: !Ref CIDRA
      Tags:
        - Key: Name
          Value: !Ref AWS::StackName
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Ref AWS::StackName
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway
  RouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Ref AWS::StackName
  Route:
    Type: AWS::EC2::Route
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  SubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref SubnetA
      RouteTableId: !Ref RouteTable
  NetworkAcl:
    Type: AWS::EC2::NetworkAcl
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Ref AWS::StackName
  InboundHTTPNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: 100
      Protocol: 6
      RuleAction: allow
      Egress: false
      CidrBlock: 0.0.0.0/0
      PortRange:
        From: 80
        To: 80
  InboundHTTPSNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: 101
      Protocol: 6
      RuleAction: allow
      Egress: false
      CidrBlock: 0.0.0.0/0
      PortRange:
        From: 443
        To: 443
  InboundSSHNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: 102
      Protocol: 6
      RuleAction: allow
      Egress: false
      CidrBlock: 0.0.0.0/0
      PortRange:
        From: 22
        To: 22
  InboundResponsePortsNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: 103
      Protocol: 6
      RuleAction: allow
      Egress: false
      CidrBlock: 0.0.0.0/0
      PortRange:
        From: 1024
        To: 65535
  OutBoundHTTPNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: 100
      Protocol: 6
      RuleAction: allow
      Egress: true
      CidrBlock: 0.0.0.0/0
      PortRange:
        From: 80
        To: 80
  OutBoundHTTPSNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: 101
      Protocol: 6
      RuleAction: allow
      Egress: true
      CidrBlock: 0.0.0.0/0
      PortRange:
        From: 443
        To: 443
  OutBoundResponsePortsNetworkAclEntry:
    Type: AWS::EC2::NetworkAclEntry
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: 102
      Protocol: 6
      RuleAction: allow
      Egress: true
      CidrBlock: 0.0.0.0/0
      PortRange:
        From: 1024
        To: 65535
  SubnetNetworkAclAssociation:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Properties:
      SubnetId: !Ref SubnetA
      NetworkAclId: !Ref NetworkAcl
  WebServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref VPC
      GroupDescription: Allow ingress via SSH, HTTP, HTTPS ports
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: !Ref AWS::StackName
  WebServerInstance:
    Type: AWS::EC2::Instance
    CreationPolicy:
      ResourceSignal:
        Count: 1
        Timeout: PT15M
      AWS::CloudFormation::Init:
        configSets:
          default:
            - cfn
            - updates
            - docker
            - tls
            - concerto
        cfn:
          files:
            /etc/cfn/cfn-hup.conf:
              content: !Sub |
                [main]
                stack=${AWS::StackId}
                region=${AWS::Region}
                interval=1
              mode: 000400
              owner: root
              group: root
            /etc/cfn/hooks.d/cfn-auto-reloader.conf:
              content: !Sub |
                [cfn-auto-reloader-hook]
                triggers=post.update
                path=Resources.WebServerInstance.Metadata.AWS::CloudFormation::Init
                action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region}
                runas=root
              mode: 000400
              owner: root
              group: root
          services:
            sysvinit:
              cfn-hup:
                enabled: true
                ensureRunning: true
                files:
                  - /etc/cfn/cfn-hup.conf
                  - /etc/cfn/hooks.d/cfn-auto-reloader.conf
        updates:
          packages:
            yum:
              yum-cron: []
          commands:
            01_config:
              command: |
                sed -ri 's/^(.*update_cmd\s*=).*$/\1 security/' yum-cron.conf
                sed -ri 's/^(.*apply_updates\s*=).*$/\1 yes/' yum-cron.conf
              cwd: /etc/yum
          services:
            sysvinit:
              yum-cron:
                enabled: true
                ensureRunning: true
        docker:
          commands:
            01_user:
              command: usermod -a -G docker ec2-user
            02_docker_compose:
              command: |
                curl -L https://github.com/docker/compose/releases/download/1.25.5/docker-compose-Linux-x86_64 -o /usr/local/bin/docker-compose
                chmod +x /usr/local/bin/docker-compose
          services:
            sysvinit:
              docker:
                enabled: true
                ensureRunning: true
        tls:
          files:
            /home/concerto/data/certbot/conf/cert.pem:
              content: |
                -----BEGIN CERTIFICATE-----
                MI<KEY>IB<KEY>
                <KEY>
                T<KEY>
                B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
                B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
                KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
                OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
                jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
                qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
                rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
                HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
                hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
                ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
                3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
                NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
                ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
                TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
                jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
                oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
                4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
                mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
                emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
                -----END CERTIFICATE-----

                -----BEGIN CERTIFICATE-----
                MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
                MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
                DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
                SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
                GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
                AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
                q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
                SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
                Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
                a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
                /PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
                AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
                CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
                bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
                c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
                VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
                ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
                MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
                Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
                AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
                uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
                wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
                X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
                PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
                KOqkqm57TH<KEY>
                -----END CERTIFICATE-----

                -----BEGIN CERTIFICATE-----
                MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAw
                TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
                cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1
                WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
                RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEi
                MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrX
                NSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf
                89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHl
                Npi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7Dc
                Gu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgz
                uEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMB
                AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU
                BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB
                FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSo
                SmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js
                LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF
                BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG
                AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD
                VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB
                ABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGx
                A/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRM
                UM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2
                DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1
                eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOu
                OsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vw
                p7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY
                2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0
                ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKR
                PB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5b
                rUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt
                -----END CERTIFICATE-----

                -----BEGIN CERTIFICATE-----
                MI<KEY>
                <KEY>
                xY0AwGv+jYTctfKVMajo9pCgQ0qTdFyzPkNpS4kiR3RRPplkw80kAfmELyh3FyKn
                3cNsCExmLzd0xW+TjrBGNxZh0VCYyLAPT1hTfKz22i2WYVCtQ9wKpk+etVK5nI7v
                Tt9GszHcIPxpwqMgdT7sOBs2TmZm0t/1ZqSTL3umDpQ+YD1KSxxvurRNHDyRWG4v
                TcTacNvtATl2wEnn6TW1FAaQweWS4hD9a7m0hQIDAQABo4IBfTCCAXkwEgYDVR0T
                AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
                CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
                bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
                c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
                VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
                ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
                MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
                Y3JsMB0GA1UdDgQWBBTFsatOTLHNZDCTfsGEmQWr5gPiJTANBgkqhkiG9w0BAQsF
                AAOCAQEANlaeSdstfAtqFN3jdRZJFjx9X+Ob3PIDlekPYQ1OQ1Uw43rE1FUj7hUw
                g2MJKfs9b7M0WoQg7C20nJY/ajsg7pWhUG3J6rlkDTfVY9faeWi0qsPYXE6BpBDr
                5BrW/Xv8yT8U2BiEAmNggWq8dmFl82fghmLzHBM8X8NZ3ZwA1fGePA53AP5IoD+0
                ArpW8Ik1sSuQBjZ8oQLfN+G8OoY7MNRopyLyQQCNy4aWfE+xYnoVoa5+yr+aPiX0
                7YQrY/cKawAn7QB4PyF5//IKSAVs7mAuB68wbMdE3FKfOHfJ24W4z/bIJTrTY8Y5
                Sr4AUhtzf8oVDrHZYWRrP4joIcOu/Q==
                -----END CERTIFICATE-----

                -----BEGIN CERTIFICATE-----
                MIIFjTCCA3WgAwIBAgIRAJObmZ6kjhYNW0JZtD0gE9owDQYJKoZIhvcNAQELBQAw
                TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
                cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0NDM0
                WhcNMjExMDA2MTU0NDM0WjBKMQswCQYDV<KEY>EW<KEY>
                RW<KEY>
                MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDhJHRCe7eRMdlz/ziq2M5EXLc5
                CtxErg29RbmXN2evvVBPX9MQVGv3QdqOY+ZtW8DoQKmMQfzRA4n/YmEJYNYHBXia
                kL0aZD5P3M93L4lry2evQU3FjQDAa/6NhNy18pUxqOj2kKBDSpN0XLM+Q2lLiSJH
                dFE+mWTDzSQB+YQvKHcXIqfdw2wITGYvN3TFb5OOsEY3FmHRUJjIsA9PWFN8rPba
                LZZhUK1D3AqmT561Urmcju9O30azMdwg/GnCoyB1Puw4GzZOZmbS3/VmpJMve6YO
                lD5gPUpLHG+6tE0cPJFYbi9NxNpw2+0BOXbASefpNbUUBpDB5ZLiEP1rubSFAgMB
                AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU
                BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB
                FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBTF
                satOTLHNZDCTfsGEmQWr5gPiJTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js
                LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF
                BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG
                AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD
                VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB
                AF4tI1yGjZgld9lP01+zftU3aSV0un0d2GKUMO7GxvwTLWAKQz/eT+u3J4+GvpD+
                BMfopIxkJcDCzMChjjZtZZwJpIY7BatVrO6OkEmaRNITtbZ/hCwNkUnbk3C7EG3O
                GJZlo9b2wzA8v9WBsPzHpTvLfOr+dS57LLPZBhp3ArHaLbdk33lIONRPt9sseDEk
                mdHnVmGmBRf4+J0Wy67mddOvz5rHH8uzY94raOayf20gzzcmqmot4hPXtDG4Y49M
                oFMMT2kcWck3EOTAH6QiGWkGJ7cxMfSL3S0niA6wgFJtfETETOZu8AVDgENgCJ3D
                S0bz/dhVKvs3WRkaKuuR/W0nnC2VDdaFj4+CRF8LGtn/8ERaH48TktH5BDyDVcF9
                zfJ75Scxcy23jAL2N6w3n/t3nnqoXt9Im4FprDr+mP1g2Z6Lf2YA0jE3kZalgZ6l
                NHu4CmvJYoOTSJw9X2qlGl1K+B4U327rG1tRxgjM76pN6lIS02PMECoyKJigpOSB
                u4V8+LVaUMezCJH9Qf4EKeZTHddQ1t96zvNd2s9ewSKx/DblXbKsBDzIdHJ+qi6+
                F9DIVM5/ICdtDdulOO+dr/BXB+pBZ3uVxjRANvJKKpdxkePyluITSNZHbanWRN07
                gMvwBWOL060i4VrL9er1sBQrRjU9iNpZQGTnLVAxQVFu
                -----END CERTIFICATE-----
            /home/concerto/data/nginx/app.conf:
              content: !Sub |
                server {
                    listen [::]:80 default_server;
                    listen 80 default_server;
                    server_tokens off;

                    location /.well-known/acme-challenge/ {
                        root /var/www/certbot;
                        allow all;
                    }

                    location / {
                        return 301 https://$host$request_uri;
                    }
                }

                server {
                    listen [::]:443 ssl http2 default_server;
                    listen 443 ssl http2 default_server;
                    server_tokens off;

                    ssl_certificate /etc/letsencrypt/dummy/${DomainName}/fullchain.pem;
                    ssl_certificate_key /etc/letsencrypt/dummy/${DomainName}/privkey.pem;
                    ssl_dhparam /etc/letsencrypt/dhparam.pem;
                    ssl_protocols TLSv1.2 TLSv1.3;
                    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
                    ssl_prefer_server_ciphers off;
                    ssl_session_timeout 1d;
                    ssl_session_cache shared:SSL:10m;
                    ssl_session_tickets off;
                    ssl_stapling on;
                    ssl_stapling_verify on;
                    ssl_trusted_certificate /etc/letsencrypt/cert.pem;
                    add_header Strict-Transport-Security max-age=15768000 always;

                    client_max_body_size 50M;

                    location / {
                        proxy_pass http://concerto;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                        proxy_set_header X-Forwarded-Proto $scheme;
                        proxy_set_header Host $http_host;
                        proxy_hide_header X-Powered-By;
{'fraction_non_alphanumeric': 0.07357661083690697, 'fraction_numerical': 0.05725921730452841, 'mean_word_length': 1.999352855524996, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 15, 'https://': 8, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 26, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1992308', 'n_tokens_mistral': 15944, 'n_tokens_neox': 14425, 'n_words': 2038}