stelligent / cfn_nag

Linting tool for CloudFormation templates
MIT License
1.26k stars 212 forks source link

Error checking security in template `block in audit_impl': undefined method `to_i' #295

Closed littleJabiel closed 5 years ago

littleJabiel commented 5 years ago

Hi, checking security of a cloudformation template I get the following error:

Running command cfn_nag_scan --input-path ${CLOUDFORMATION_TEMPLATE_NAME} 2>&1 
/root/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/cfn-nag-0.4.39/lib/cfn-nag/custom_rules/SecurityGroupIngressAllProtocolsRule.rb:32:in `block in audit_impl': undefined method `to_i' for {"Ref"=>"RDSTransportProtocol"}:Hash (NoMethodError) 
Did you mean?  to_s 
               to_a 
               to_h 
    from /root/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/cfn-nag-0.4.39/lib/cfn-nag/custom_rules/SecurityGroupIngressAllProtocolsRule.rb:31:in `select' 
    from /root/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/cfn-nag-0.4.39/lib/cfn-nag/custom_rules/SecurityGroupIngressAllProtocolsRule.rb:31:in `audit_impl' 
    from /root/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/cfn-nag-0.4.39/lib/cfn-nag/custom_rules/base.rb:19:in `audit' 
    from /root/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/cfn-nag-0.4.39/lib/cfn-nag/custom_rule_loader.rb:92:in `block in filter_rule_classes' 
    from /root/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/cfn-nag-0.4.39/lib/cfn-nag/custom_rule_loader.rb:85:in `each' 
    from /root/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/cfn-nag-0.4.39/lib/cfn-nag/custom_rule_loader.rb:85:in `filter_rule_classes' 
    from /root/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/cfn-nag-0.4.39/lib/cfn-nag/custom_rule_loader.rb:55:in `execute_custom_rules' 
    from /root/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/cfn-nag-0.4.39/lib/cfn-nag/cfn_nag.rb:82:in `audit' 
    from /root/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/cfn-nag-0.4.39/lib/cfn-nag/cfn_nag.rb:60:in `block in audit_aggregate_across_files' 
    from /root/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/cfn-nag-0.4.39/lib/cfn-nag/cfn_nag.rb:57:in `each' 
    from /root/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/cfn-nag-0.4.39/lib/cfn-nag/cfn_nag.rb:57:in `audit_aggregate_across_files' 
    from /root/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/cfn-nag-0.4.39/lib/cfn-nag/cfn_nag.rb:31:in `audit_aggregate_across_files_and_render_results' 
    from /root/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/cfn-nag-0.4.39/lib/cfn-nag/cfn_nag_executor.rb:47:in `execute_aggregate_scan' 
    from /root/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/cfn-nag-0.4.39/lib/cfn-nag/cfn_nag_executor.rb:27:in `scan' 
    from /root/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/cfn-nag-0.4.39/bin/cfn_nag_scan:11:in `<top (required)>' 
    from /root/.rbenv/versions/2.6.3/bin/cfn_nag_scan:23:in `load' 
    from /root/.rbenv/versions/2.6.3/bin/cfn_nag_scan:23:in `<main>' 

The cloudformation code related with the error:

  IngressRuleSQLSecurityGroupBaseRDS:
    Condition: RDSConnection
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId:
        Fn::ImportValue: !Sub "${BaseSecurityGroupStackName}:RDSBaseServiceSecurityGroupId"
      Description: "Allow instace SQL access to rds instances"
      IpProtocol: !Ref RDSTransportProtocol
      FromPort: !Ref RDSPort
      ToPort: !Ref RDSPort
      SourceSecurityGroupId: !Ref InstanceSecurityGroup`

RDSTransportProtocol is a template parameter.

could you help me please? Am I doing something wrong?

thanks

Mr-Lizard commented 5 years ago

could you attach the whole code ? That would help some, thanks !

littleJabiel commented 5 years ago

Hello, this is the template:

---
AWSTemplateFormatVersion: '2010-09-09'
Description: >
  Application Group for .NET Applications (Framework and Core) using IIS
Metadata:
  Template:
    Name: "__TEMPLATE__NAME__"
    Version: "__TEMPLATE__VERSION__"
    RepositoryUrl: "__TEMPLATE__REPOSITORY__URL__"
    RepositoryCommitId: "__TEMPLATE__REPOSITORY__COMMIT__ID__"
    BuildDate: "__TEMPLATE__BUILD__DATE__"
    ArtifactUrl: "__TEMPLATE__ARTIFACT__URL__"

  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Application - Resources Values
        Parameters:
          - ApplicationGroup
          - Scope
      - Label:
          default: Tags - Resources Values
        Parameters:
          - Project
      - Label:
          default: EC2 - Resources Values
        Parameters:
          - InstanceType
          - AmiId
          - KeyName
      - Label:
          default: Network - Resources Values
        Parameters:
          - BaseVpcStackName
          - BaseDomainNameStackName
          - RDSAccess
          - RDSTransportProtocol
          - RDSPort
          - ElastiCacheAccess
          - ElastiCacheTransportProtocol
          - ElastiCachePort
      - Label:
          default: Autoscaling Group - Resources Values
        Parameters:
          - ScheduleAction
          - AutoScaleAlarms
          - ScheduledActionUp
          - ScheduledActionDown
          - AutoScalingGroupCapacity
          - AutoScalingGroupMinCapacity
          - AutoScalingGroupMaxCapacity
          - AutoScalingHealthCheck
          - LaunchConfigurationVersion
          - HealthcheckPath
      - Label:
          default: Security - Resources Values
        Parameters:
          - VSTSTokenCondition
          - BaseSecurityGroupStackName
          - SESAccess
      - Label:
          default: Artifacts - Resources Values
        Parameters:
          - ArtifactRetention
      - Label:
          default: Logs Public load balancer
        Parameters:
          - BucketLogsNameSuffix
          - EnableLogsPublicLoadBalancer
    ParameterLabels:
      Project:
        default: Project name
      AmiId:
        default: The image id
      InstanceType:
        default: Instance Type
      KeyName:
        default: Key Name, used for connections to ec2 instances behind autoscaling group
      ApplicationGroup:
        default: 'Application Group Name, this determine how you access your applications is used in: http://[appgroupname].vpcdomain'
      ScheduleAction:
        default: Schedule destroy/create instances behind AutoscalingGroup
      AutoScaleAlarms:
        default: Define Sclae Up/Down alarms policies
      AutoScalingGroupCapacity:
        default: Autoscaling Group Desired Capacity
      AutoScalingGroupMinCapacity:
        default: Autoscaling Group Min Capacity
      AutoScalingGroupMaxCapacity:
        default: Autoscaling Group Max Capacity
      AutoScalingHealthCheck:
        default: Autoscaling HealthCheck Type
      LaunchConfigurationVersion:
        default: Launch Configuration Version
      Scope:
        default: Scope, determine if the application balancer is created on public or privated subnets
      SESAccess:
        default: Add a policy to the instance role to allow sending emails with SES
      BaseVpcStackName:
        default: Base VPC stack name, which cloudformation stack provides VPC exports values to this stack
      BaseDomainNameStackName:
        default: Base Domain Name stack name, which cloudformation stack provides hosted zones and ACM exports values to this stack
      BaseSecurityGroupStackName:
        default: Base Security groups stack name, wich cloudformation stack provide the base security groups
      VSTSTokenCondition:
        default: Random number for Role condition between VSTS and AWS Asummed Role
      ArtifactRetention:
        default: Artifacts retention days, codedeploy use this bucket for its artifacts.
      ScheduledActionUp:
        default: Recurrence in crontab format for start up the instances behind autoscaling group
      ScheduledActionDown:
        default: Recurrence in crontab format for shutdown the instances behind autoscaling group
      RDSAccess:
        default: Create an ingress rule to allow traffic to RDS
      RDSTransportProtocol:
        default: Protocol to use for the RDS connection
      RDSPort:
        default: Port for grant RDS access to the app-stack
      ElastiCacheAccess:
        default: Create an ingress rule to allow traffic to ElastiCache
      ElastiCacheTransportProtocol:
        default: Protocol to use for the ElastiCache connection
      ElastiCachePort:
        default: Port for grant ElastiCache access to the app-stack
      HealthcheckPath:
        default: Path for the target group health check
      BucketLogsNameSuffix:
        default: suffix import value key
      EnableLogsPublicLoadBalancer:
        default: flag to activate public LB logs
Parameters:
  BaseVpcStackName:
    Type: String
    Description: The base stack that contains the VPC, subnets, etc.
    Default: vpc-main
  BaseDomainNameStackName:
    Type: String
    Description: The base stack that contains
    Default: domain-name-main
  BaseSecurityGroupStackName:
    Type: String
    Description: The base stack that contains
    Default: sg-vpc-main
  AmiId:
    Type: 'AWS::EC2::Image::Id'
    Description: The image id
  KeyName:
    Type: 'AWS::EC2::KeyPair::KeyName'
    Description: The EC2 Key Pair to allow Rdesktop access to the instances
    ConstraintDescription: must be the name of an existing EC2 KeyPair.
    MinLength: 4
  Project:
    Type: String
    Description: Project name
    MinLength: 2
    MaxLength: 28
    AllowedPattern: '([a-z0-9\-]){2,28}'
    ConstraintDescription: Must be in lower case, alphanumeric and between 2 and 28 characters
  ApplicationGroup:
    Type: String
    Description: Application group name, could be batch, public, private, etc.
    MinLength: 2
    MaxLength: 18
    AllowedPattern: '([a-z0-9\-]){2,18}'
    ConstraintDescription: Must be in lower case, alphanumeric (including hyphen) and between 2 and 18 characters
  Scope:
    Type: String
    Description: Visibility Scope, this define if your application group is public/private access
    Default: private
    AllowedValues:
      - private
      - public
  ArtifactRetention:
    Type: Number
    Description: The number of days to retaint artifact in bucket for CodeDeploy
    Default: 365
    MinValue: 30
    MaxValue: 720

  ScheduleAction:
    Type: String
    Description: Define if create the ScheduleAction in AutoScalingGroup, this destroy or not the instans behind it.
    Default: true
    AllowedValues:
      - true
      - false
  AutoScaleAlarms:
    Type: String
    Description: Define if create the AutoScaleAlarms in AutoScalingGroup, this allow to scale up and down base on alarms.
    Default: false
    AllowedValues:
      - true
      - false
  ScheduledActionUp:
    Type: String
    Description: Define the in crontab format to Start up the instances behind autoscaling group
    Default: '0 6 * * 1-5'
    AllowedPattern: '^((?:[1-9]?\d|\*)\s*(?:(?:[\/-][1-9]?\d)|(?:,[1-9]?\d)+)?\s*){5}$'
    ConstraintDescription: check format https://en.wikipedia.org/wiki/Cron

  ScheduledActionDown:
    Type: String
    Description: Define the in crontab format the recurrence to shutdown the instances behind autoscaling group
    Default: '0 20 * * 1-5'
    AllowedPattern: '^((?:[1-9]?\d|\*)\s*(?:(?:[\/-][1-9]?\d)|(?:,[1-9]?\d)+)?\s*){5}$'
    ConstraintDescription: check format https://en.wikipedia.org/wiki/Cron
  AutoScalingGroupCapacity:
    Type: Number
    Description: The AutoscalingGroup desired instance capacity
    Default: 1
    MinValue: 1
    MaxValue: 30
    ConstraintDescription: must be between 1 and 3 EC2 instances.
  AutoScalingGroupMinCapacity:
    Type: Number
    Description: The AutoscalingGroup Minimal instance capacity
    Default: 1
    MinValue: 1
    MaxValue: 30
    ConstraintDescription: must be between 1 and 3 EC2 instances.
  AutoScalingGroupMaxCapacity:
    Type: Number
    Description: The AutoscalingGroup Maximun instance capacity
    Default: 3
    MinValue: 1
    MaxValue: 30
    ConstraintDescription: must be between 1 and 3 EC2 instances.
  AutoScalingHealthCheck:
    Type: String
    Description: The AutoscalingGroup HealthCheck type.
    Default: ELB
    AllowedValues:
      - EC2
      - ELB
  HealthcheckPath:
    Type: String
    Description: Health check path
    Default: '/health/'
  LaunchConfigurationVersion:
    Type: String
    Default: '0000'
    Description: LaunchConfiguration version (should be unique)
    MinLength: 4
    MaxLength: 4
  InstanceType:
    Type: String
    Description: EC2 instance type
    Default: t2.medium
    AllowedValues:
      - t2.small
      - t2.medium
      - t2.large
      - t2.xlarge
      - t3.small
      - t3.medium
      - t3.large
      - t3.xlarge
    ConstraintDescription: must be a valid EC2 instance type
  VSTSTokenCondition:
    Type: String
    Description: Random number to generate a unique condition between the role assumed by VSTS and the VSTS plugins
    Default: '000000000'
    MinLength: 8
    MaxLength: 15
    ConstraintDescription: must be between 9 and 15 digit numbers.
  SESAccess:
    Type: String
    Description: Add a policy to the instance role to allow sending emails with SES.
    Default: false
    AllowedValues:
      - true
      - false
  RDSAccess:
    Type: String
    Description: Add an ingress rule to allow RDS connectivity.
    Default: false
    AllowedValues:
      - true
      - false
  RDSPort:
    Type: String
    Description: port for RDS service access
    MinLength: 1
    MaxLength: 5
    Default: "1433"
    AllowedPattern: '([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])|\-1'
    ConstraintDescription: must be a valid port number (1-65535) or (-1) for any.
  RDSTransportProtocol:
    Type: String
    Description: RDS Transport protocol
    Default: tcp
    AllowedValues:
      - tcp
      - udp
  ElastiCacheAccess:
    Type: String
    Description: Add an ingress rule to allow ElastiCache connectivity.
    Default: false
    AllowedValues:
      - true
      - false
  ElastiCachePort:
    Type: String
    Description: port for ElastiCache service access
    MinLength: 1
    MaxLength: 5
    Default: "6379"
    AllowedPattern: '([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])|\-1'
    ConstraintDescription: must be a valid port number (1-65535) or (-1) for any.
  ElastiCacheTransportProtocol:
    Type: String
    Description: ElastiCache Transport protocol
    Default: tcp
    AllowedValues:
      - tcp
      - udp
  BucketLogsNameSuffix:
    Type: String
    Description: suffix import value bucket name
    Default: BucketLogsName
  EnableLogsPublicLoadBalancer:
    Type: String
    Description: flag to activate LB logs
    Default: false
    AllowedValues:
      - true
      - false
  Environment:
    Type: String
    Description: suffix import value bucket name
    Default: development

Conditions:
  CreatePublic: !Equals [!Ref Scope, 'public']
  AddPolicyToUseSES: !Equals [!Ref SESAccess, true]
  RDSConnection: !Equals [!Ref RDSAccess, true]
  ElastiCacheConnection: !Equals [!Ref ElastiCacheAccess, true]
  EnvironmentIsDevelopment: !Equals [!Ref Environment, 'development']
  EnvironmentIsProduction: !Equals [!Ref Environment, 'production']
  CreateScheduleActions: !Equals [!Ref ScheduleAction, true]
  CreateAutoScaleAlarms: !And [!Equals [!Ref AutoScaleAlarms, true], !Not [!Condition EnvironmentIsDevelopment]]
  EnableLogsPublicLB: !Equals [!Ref EnableLogsPublicLoadBalancer, 'true']

Resources:
  InstanceServiceRole:
    Type: 'AWS::IAM::Role'
    Properties:
      RoleName: !Sub
        - '${Project}-${EnvironmentName}-${ApplicationGroup}-instance-service-role'
        - {EnvironmentName: !ImportValue 'account:environment'}
      Path: '/'
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy'
        - 'arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Policies:
        - PolicyName: 'AllowInstanceNotificateCFSignals'
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: "Allow"
                Action:
                  - 'cloudformation:ListStackSetOperations'
                  - 'cloudformation:DescribeStackInstance'
                  - 'cloudformation:ListStackInstances'
                  - 'cloudformation:DescribeStackResources'
                  - 'cloudformation:SignalResource'
                  - 'cloudformation:DescribeStackResource'
                  - 'cloudformation:DescribeStacks'
                  - 'cloudformation:ListStackSetOperationResults'
                  - 'cloudformation:GetStackPolicy'
                  - 'cloudformation:DescribeStackEvents'
                  - 'cloudformation:DescribeStackSet'
                  - 'cloudformation:ListStackSets'
                  - 'cloudformation:GetTemplate'
                  - 'cloudformation:DescribeStackSetOperation'
                  - 'cloudformation:DescribeChangeSet'
                  - 'cloudformation:ListChangeSets'
                  - 'cloudformation:ListStackResources'
                Resource:
                  - 'arn:aws:cloudformation:*:*:stack/*/*'
                  - 'arn:aws:cloudformation:*:*:stackset/*:*'
              - Effect: "Allow"
                Action:
                  - 'cloudformation:EstimateTemplateCost'
                  - 'cloudformation:ListExports'
                  - 'cloudformation:ListStacks'
                  - 'cloudformation:ListImports'
                  - 'cloudformation:DescribeAccountLimits'
                  - 'cloudformation:GetTemplateSummary'
                Resource: '*'
        - PolicyName: 'AllowInstanceReadCodeDeployBucket'
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: "Allow"
                Action:
                  - s3:ListBucketByTags
                  - s3:GetLifecycleConfiguration
                  - s3:GetBucketTagging
                  - s3:GetInventoryConfiguration
                  - s3:GetObjectVersionTagging
                  - s3:ListBucketVersions
                  - s3:GetBucketLogging
                  - s3:ListBucket
                  - s3:GetAccelerateConfiguration
                  - s3:GetBucketPolicy
                  - s3:GetObjectVersionTorrent
                  - s3:GetObjectAcl
                  - s3:GetEncryptionConfiguration
                  - s3:GetBucketRequestPayment
                  - s3:GetObjectVersionAcl
                  - s3:GetObjectTagging
                  - s3:GetMetricsConfiguration
                  - s3:HeadBucket
                  - s3:GetBucketPublicAccessBlock
                  - s3:GetBucketPolicyStatus
                  - s3:ListBucketMultipartUploads
                  - s3:GetBucketWebsite
                  - s3:GetBucketVersioning
                  - s3:GetBucketAcl
                  - s3:GetBucketNotification
                  - s3:GetReplicationConfiguration
                  - s3:ListMultipartUploadParts
                  - s3:GetObject
                  - s3:GetObjectTorrent
                  - s3:GetAccountPublicAccessBlock
                  - s3:ListAllMyBuckets
                  - s3:GetBucketCORS
                  - s3:GetAnalyticsConfiguration
                  - s3:GetObjectVersionForReplication
                  - s3:GetBucketLocation
                  - s3:GetObjectVersion
                Resource:
                  - !Sub
                    - 'arn:aws:s3:::${Project}-${EnvironmentName}-${ApplicationGroup}-codedeploy/*'
                    - {EnvironmentName: !ImportValue 'account:environment'}
                  - !Sub 'arn:aws:s3:::aws-codedeploy-${AWS::Region}/*'
        - PolicyName: 'AllowInstanceReadAutoScalingGroup'
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: "Allow"
                Action:
                  - 'autoscaling:DescribeAutoScalingInstances'
                Resource: '*'
  # Policy
  SESPolicy:
    Condition: AddPolicyToUseSES
    Type: AWS::IAM::Policy
    DependsOn: InstanceServiceRole
    Properties:
      PolicyName: 'AllowSendMailFromSES'
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: "Allow"
            Action:
            - 'ses:ListTemplates'
            - 'ses:ListCustomVerificationEmailTemplates'
            - 'ses:VerifyEmailIdentity'
            - 'ses:GetIdentityPolicies'
            - 'ses:GetSendQuota'
            - 'ses:DescribeConfigurationSet'
            - 'ses:ListReceiptFilters'
            - 'ses:GetIdentityMailFromDomainAttributes'
            - 'ses:VerifyDomainDkim'
            - 'ses:VerifyDomainIdentity'
            - 'ses:SendEmail'
            - 'ses:ListConfigurationSets'
            - 'ses:GetIdentityDkimAttributes'
            - 'ses:DescribeReceiptRuleSet'
            - 'ses:ListReceiptRuleSets'
            - 'ses:GetTemplate'
            - 'ses:ListIdentities'
            - 'ses:VerifyEmailAddress'
            - 'ses:GetCustomVerificationEmailTemplate'
            - 'ses:SendRawEmail'
            - 'ses:GetSendStatistics'
            - 'ses:GetIdentityVerificationAttributes'
            - 'ses:GetIdentityNotificationAttributes'
            - 'ses:ListIdentityPolicies'
            - 'ses:DescribeReceiptRule'
            - 'ses:DescribeActiveReceiptRuleSet'
            - 'ses:GetAccountSendingEnabled'
            - 'ses:ListVerifiedEmailAddresses'
            Resource: '*'
      Roles:
        - !Sub
          - '${Project}-${EnvironmentName}-${ApplicationGroup}-instance-service-role'
          - {EnvironmentName: !ImportValue 'account:environment'}

  InstanceProfile:
    Type: 'AWS::IAM::InstanceProfile'
    Properties:
      InstanceProfileName: !Sub
        - '${Project}-${EnvironmentName}-${ApplicationGroup}-instance-profile'
        - {EnvironmentName: !ImportValue 'account:environment'}
      Path: '/'
      Roles:
        - !Ref InstanceServiceRole

  AutoScalingGroup:
    Type: 'AWS::AutoScaling::AutoScalingGroup'
    Properties:
      AutoScalingGroupName: !Sub
        - '${Project}-${EnvironmentName}-${ApplicationGroup}-asg'
        - {EnvironmentName: !ImportValue 'account:environment'}
      VPCZoneIdentifier:
        Fn::If:
          - EnvironmentIsProduction
          - Fn::Split:
              - ','
              - Fn::ImportValue: !Sub '${BaseVpcStackName}:PrivateSubnets'
          -
            - Fn::ImportValue: !Sub '${BaseVpcStackName}:PrivateSubnet1'
      LaunchConfigurationName: !Ref LaunchConfig
      MinSize: !Ref AutoScalingGroupMinCapacity
      MaxSize: !Ref AutoScalingGroupMaxCapacity
      DesiredCapacity: !Ref AutoScalingGroupCapacity
      HealthCheckType: !Ref AutoScalingHealthCheck
      HealthCheckGracePeriod: 600
      TargetGroupARNs:
        - !Ref ALBHTTPTargetGroup

    CreationPolicy:
      ResourceSignal:
        Timeout: PT25M
        Count: !Ref AutoScalingGroupCapacity
    UpdatePolicy:
      AutoScalingRollingUpdate:
        MinInstancesInService: 1
        MaxBatchSize: 1
        WaitOnResourceSignals: true
        PauseTime: PT20M
        SuspendProcesses:
          - 'ScheduledActions'
          - 'AlarmNotification'

  ScheduledActionUpDaily:
    Type: AWS::AutoScaling::ScheduledAction
    Condition: CreateScheduleActions
    Properties:
      AutoScalingGroupName: !Ref AutoScalingGroup
      MaxSize: !Ref AutoScalingGroupMaxCapacity
      MinSize: !Ref AutoScalingGroupMinCapacity
      DesiredCapacity: !Ref AutoScalingGroupCapacity
      Recurrence: !Ref ScheduledActionUp

  ScheduledActionDownDaily:
    Type: AWS::AutoScaling::ScheduledAction
    Condition: CreateScheduleActions
    Properties:
      AutoScalingGroupName: !Ref AutoScalingGroup
      MaxSize: 0
      MinSize: 0
      DesiredCapacity: 0
      Recurrence: !Ref ScheduledActionDown

  ScaleUpPolicy:
    Type: AWS::AutoScaling::ScalingPolicy
    Condition: CreateAutoScaleAlarms
    Properties:
      AdjustmentType: ChangeInCapacity
      AutoScalingGroupName: !Ref AutoScalingGroup
      Cooldown: '300'
      ScalingAdjustment: 2

  ScaleDownPolicy:
    Type: AWS::AutoScaling::ScalingPolicy
    Condition: CreateAutoScaleAlarms
    Properties:
      AdjustmentType: ChangeInCapacity
      AutoScalingGroupName: !Ref AutoScalingGroup
      Cooldown: '300'
      ScalingAdjustment: -1

  CPUAlarmHigh:
    Type: AWS::CloudWatch::Alarm
    Condition: CreateAutoScaleAlarms
    Properties:
      EvaluationPeriods: 3
      Statistic: Average
      Threshold: 80
      AlarmDescription: Alarm if CPU too high
      Period: 300
      AlarmActions:
        - !Ref ScaleUpPolicy
      Namespace: AWS/EC2
      Dimensions:
        - Name: AutoScalingGroupName
          Value: !Ref AutoScalingGroup
      ComparisonOperator: GreaterThanThreshold
      MetricName: CPUUtilization

  CPUAlarmLow:
    Type: AWS::CloudWatch::Alarm
    Condition: CreateAutoScaleAlarms
    Properties:
      EvaluationPeriods: 6
      Statistic: Average
      Threshold: 40
      AlarmDescription: Alarm if CPU too low
      Period: 300
      AlarmActions:
        - !Ref ScaleDownPolicy
      Namespace: AWS/EC2
      Dimensions:
        - Name: AutoScalingGroupName
          Value: !Ref AutoScalingGroup
      ComparisonOperator: LessThanThreshold
      MetricName: CPUUtilization

  ALBHTTPTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub
        - 'tg-http-${ApplicationGroup}-${EnvironmentName}'
        - {EnvironmentName: !ImportValue 'account:environment'}
      HealthCheckPath: !Ref HealthcheckPath
      HealthCheckPort: '80'
      HealthCheckProtocol: HTTP
      HealthyThresholdCount: 2
      Port: 80
      Protocol: HTTP
      UnhealthyThresholdCount: 5
      Matcher:
        HttpCode: '200,301,302'
      VpcId:
        Fn::ImportValue: !Sub '${BaseVpcStackName}:VpcId'
      TargetGroupAttributes:
        - Key: stickiness.enabled
          Value: 'true'
        - Key: stickiness.type
          Value: lb_cookie
        - Key: stickiness.lb_cookie.duration_seconds
          Value: '40'
        - Key: deregistration_delay.timeout_seconds
          Value: '60'

  LaunchConfig:
    Type: AWS::AutoScaling::LaunchConfiguration
    Metadata:
      Comment: AMS instance
    Properties:
      KeyName: !Ref KeyName
      IamInstanceProfile: !Ref InstanceProfile
      AssociatePublicIpAddress: false
      LaunchConfigurationName: !Join
        - '-'
        - - lc
          - !Ref Project
          - !Ref ApplicationGroup
          - !Ref Environment
          - !Ref LaunchConfigurationVersion
      ImageId: !Ref AmiId
      SecurityGroups:
        - !Ref InstanceSecurityGroup
        - Fn::ImportValue: !Sub "${BaseSecurityGroupStackName}:InstancesBaseServiceSecurityGroupId"
        - Fn::ImportValue: !Sub "${BaseSecurityGroupStackName}:InstancesBaseRemoteAccessSecurityGroupId"
      InstanceType: !Ref InstanceType

  ApplicationLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      LoadBalancerAttributes:
        !If
        - EnableLogsPublicLB
        -
          - Key: access_logs.s3.enabled
            Value: 'true'
          - Key: access_logs.s3.bucket
            Value:
              Fn::ImportValue: !Sub '${Project}-${ApplicationGroup}-${Environment}-${BucketLogsNameSuffix}'
          - Key: access_logs.s3.prefix
            Value: loadbalancer
        -
          - Key: access_logs.s3.enabled
            Value: 'false'
      Name: !Sub
        - 'alb-${Project}-${EnvironmentName}-${ApplicationGroup}'
        - {EnvironmentName: !ImportValue 'account:environment'}
      Subnets: !If
        - CreatePublic
        - Fn::Split:
          - ','
          - Fn::ImportValue: !Sub '${BaseVpcStackName}:PublicSubnets'
        - Fn::Split:
          - ','
          - Fn::ImportValue: !Sub '${BaseVpcStackName}:PrivateSubnets'
      Scheme: !If
        - CreatePublic
        - 'internet-facing'
        - 'internal'
      SecurityGroups:
        - !Ref ALBSecurityGroup

  ALBHTTPListener:
    Type: 'AWS::ElasticLoadBalancingV2::Listener'
    Properties:
      DefaultActions:
        - RedirectConfig:
            Host: "#{host}"
            Path: "/#{path}"
            Port: '443'
            Protocol: "HTTPS"
            Query: "#{query}"
            StatusCode: HTTP_302
          Type: redirect
      LoadBalancerArn: !Ref ApplicationLoadBalancer
      Port: 80
      Protocol: HTTP

  ALBHTTPSListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref ALBHTTPTargetGroup
      LoadBalancerArn: !Ref ApplicationLoadBalancer
      Port: 443
      Protocol: HTTPS
      Certificates:
        - CertificateArn:
            Fn::ImportValue: !Sub '${BaseDomainNameStackName}:DomainCertificateNameArn'

  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub
        - 'sgn-instance-${Project}-${EnvironmentName}-${ApplicationGroup}'
        - {EnvironmentName: !ImportValue 'account:environment'}
      GroupDescription: Enable HTTP access via port 80 locked down to the ELB and SSH access
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          SourceSecurityGroupId: !Select [0, !GetAtt ApplicationLoadBalancer.SecurityGroups]
      SecurityGroupEgress:
        - IpProtocol: '-1'
          FromPort: 0
          ToPort: 0
          CidrIp: 0.0.0.0/0
      VpcId:
        Fn::ImportValue: !Sub '${BaseVpcStackName}:VpcId'

  ALBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub
        - 'sgn-alb-${Project}-${EnvironmentName}-${ApplicationGroup}'
        - {EnvironmentName: !ImportValue 'account:environment'}
      GroupDescription: Enable HTTP access via port 80 locked down to the ELB and SSH access
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
      SecurityGroupEgress:
        - IpProtocol: '-1'
          FromPort: 0
          ToPort: 0
          CidrIp: 0.0.0.0/0
      VpcId:
        Fn::ImportValue: !Sub '${BaseVpcStackName}:VpcId'

  ALBDNSRecordGroup:
    Type: 'AWS::Route53::RecordSetGroup'
    Properties:
      Comment: Record to call balancer ApplicationGroup using our domain name
      HostedZoneId: !If
        - CreatePublic
        - Fn::ImportValue: !Sub '${BaseDomainNameStackName}:PublicHostedZoneId'
        - Fn::ImportValue: !Sub '${BaseDomainNameStackName}:PrivateHostedZoneId'
      RecordSets:
        - Name: !Sub
            - '${ApplicationGroup}.${HostedZoneName}.'
            - HostedZoneName:
                Fn::ImportValue: !Sub '${BaseDomainNameStackName}:HostedZoneName'
          Type: A
          AliasTarget:
            HostedZoneId: !GetAtt ApplicationLoadBalancer.CanonicalHostedZoneID
            DNSName: !GetAtt ApplicationLoadBalancer.DNSName

  IngressRuleSQLSecurityGroupBaseRDS:
    Condition: RDSConnection
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId:
        Fn::ImportValue: !Sub "${BaseSecurityGroupStackName}:RDSBaseServiceSecurityGroupId"
      Description: "Allow instace SQL access to rds instances"
      IpProtocol: !Ref RDSTransportProtocol
      FromPort: !Ref RDSPort
      ToPort: !Ref RDSPort
      SourceSecurityGroupId: !Ref InstanceSecurityGroup

  IngressRuleSecurityGroupBaseElastiCache:
    Condition: ElastiCacheConnection
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId:
        Fn::ImportValue: !Sub "${BaseSecurityGroupStackName}:ElastiCacheBaseServiceSecurityGroupId"
      Description: "Allow instace access to ElastiCache resources"
      IpProtocol: !Ref ElastiCacheTransportProtocol
      FromPort: !Ref ElastiCachePort
      ToPort: !Ref ElastiCachePort
      SourceSecurityGroupId: !Ref InstanceSecurityGroup

  ALBDNSRecordGroupPublicOnPrivate:
    Type: AWS::Route53::RecordSetGroup
    Condition: CreatePublic
    Properties:
      Comment: Record to call balancer ApplicationGroup in public scope
      HostedZoneId:
        Fn::ImportValue: !Sub '${BaseDomainNameStackName}:PrivateHostedZoneId'
      RecordSets:
        - Name: !Sub
            - '${ApplicationGroup}.${HostedZoneName}.'
            - HostedZoneName:
                Fn::ImportValue: !Sub '${BaseDomainNameStackName}:HostedZoneName'
          Type: A
          AliasTarget:
            HostedZoneId: !GetAtt ApplicationLoadBalancer.CanonicalHostedZoneID
            DNSName: !GetAtt ApplicationLoadBalancer.DNSName

  # Codedeploy
  CodeDeployServiceRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub
        - '${Project}-${EnvironmentName}-${ApplicationGroup}-codedeploy-service-role'
        - {EnvironmentName: !ImportValue 'account:environment'}
      Path: '/'
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - codedeploy.amazonaws.com
            Action:
              - 'sts:AssumeRole'

  CodeDeployApplication:
    Type: AWS::CodeDeploy::Application
    Properties:
      ApplicationName: !Sub
        - '${Project}-${EnvironmentName}-${ApplicationGroup}'
        - {EnvironmentName: !ImportValue 'account:environment'}
      ComputePlatform: Server

  # DeployementGroup Webservice
  DeploymentGroupWebservice:
    Type: AWS::CodeDeploy::DeploymentGroup
    Properties:
      ApplicationName: !Ref CodeDeployApplication
      AutoRollbackConfiguration:
        Enabled: true
        Events:
          - DEPLOYMENT_FAILURE
      DeploymentGroupName: !Sub
        - '${Project}-${EnvironmentName}-${ApplicationGroup}-webservice-asg'
        - {EnvironmentName: !ImportValue 'account:environment'}
      DeploymentConfigName: CodeDeployDefault.OneAtATime
      DeploymentStyle:
        DeploymentType: IN_PLACE
        DeploymentOption: !If
          - EnvironmentIsDevelopment
          - WITHOUT_TRAFFIC_CONTROL
          - WITH_TRAFFIC_CONTROL
      AutoScalingGroups:
        - !Ref AutoScalingGroup
      LoadBalancerInfo:
        TargetGroupInfoList:
          - Name: !GetAtt ALBHTTPTargetGroup.TargetGroupName
      ServiceRoleArn: !GetAtt CodeDeployServiceRole.Arn

  # DeploymentGroup Winservice
  DeploymentGroupWinservice:
    Type: 'AWS::CodeDeploy::DeploymentGroup'
    Properties:
      ApplicationName: !Ref CodeDeployApplication
      AutoRollbackConfiguration:
        Enabled: true
        Events:
          - DEPLOYMENT_FAILURE
      DeploymentGroupName: !Sub
        - '${Project}-${EnvironmentName}-${ApplicationGroup}-winservice-asg'
        - {EnvironmentName: !ImportValue 'account:environment'}
      DeploymentConfigName: CodeDeployDefault.OneAtATime
      DeploymentStyle:
        DeploymentType: IN_PLACE
        DeploymentOption: WITHOUT_TRAFFIC_CONTROL
      AutoScalingGroups:
        - !Ref AutoScalingGroup
      LoadBalancerInfo:
        TargetGroupInfoList:
          - Name: !GetAtt ALBHTTPTargetGroup.TargetGroupName
      ServiceRoleArn: !GetAtt CodeDeployServiceRole.Arn

  S3BucketCode:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub
        - '${Project}-${EnvironmentName}-${ApplicationGroup}-codedeploy'
        - {EnvironmentName: !ImportValue 'account:environment'}
      AccessControl: BucketOwnerFullControl
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: false
        RestrictPublicBuckets: true
      LifecycleConfiguration:
        Rules:
          - Id: !Sub 'DeleteEverythingIn${ArtifactRetention}Days'
            Prefix: ''
            Status: Enabled
            ExpirationInDays: !Ref ArtifactRetention

  VSTSAssumeRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub
        - '${Project}-${EnvironmentName}-${ApplicationGroup}-vsts-assume-role'
        - {EnvironmentName: !ImportValue 'account:environment'}
      Path: '/'
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforAWSCodeDeploy'
        - 'arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole'
        - 'arn:aws:iam::aws:policy/AWSCodeDeployDeployerAccess'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              AWS:
                - arn:aws:iam:::root
            Action:
              - 'sts:AssumeRole'
            Condition:
              StringEquals:
                sts:ExternalId: !Sub '${VSTSTokenCondition}'
      Policies:
        - PolicyName: 'AllowCodeDeployVSTSPluginPutObjectsInBucket'
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: "Allow"
                Action:
                  - 's3:PutObject'
                  - 's3:GetObject'
                Resource:
                  - !Sub
                    - 'arn:aws:s3:::${Project}-${EnvironmentName}-${ApplicationGroup}-codedeploy'
                    - {EnvironmentName: !ImportValue 'account:environment'}
                  - !Sub
                    - 'arn:aws:s3:::${Project}-${EnvironmentName}-${ApplicationGroup}-codedeploy/*'
                    - {EnvironmentName: !ImportValue 'account:environment'}
        - PolicyName: 'AllowCodeDeployVSTSPluginListObjectsInBucket'
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: "Allow"
                Action:
                  - 's3:ListBucket'
                Resource:
                  - !Sub
                    - 'arn:aws:s3:::${Project}-${EnvironmentName}-${ApplicationGroup}-codedeploy'
                    - {EnvironmentName: !ImportValue 'account:environment'}
                  - !Sub
                    - 'arn:aws:s3:::${Project}-${EnvironmentName}-${ApplicationGroup}-codedeploy/*'
                    - {EnvironmentName: !ImportValue 'account:environment'}
Mr-Lizard commented 5 years ago

Thanks. Looks like the cfn_nag code is expecting a numerical value for RDSTransportProtocol, instead of 'tcp' or 'upd'. So try that for a workaround until this is fixed. TCP is protocol 6, and UDP would be protocol 17.

ghost commented 5 years ago

@littleJabiel many apologies on this bug. the rule is presuming a string or integer there, but you rightfully have a "hash" or "dictionary" referencing a parameter.

If you define a JSON file with the parameter value (foo.json) like so: { "Parameters": { "RDSTransportProtocol": "tcp" } }

and then pipe your template through cfn_nag like so:

cat bad.yml | cfn_nag --parameter-values-path foo.json

you should get the result you want.

That said, I will work on getting a fix out for the ingress and egress rules which both have this problem. Thanks for finding this!

ghost commented 5 years ago

0.4.41 should solve the issue

littleJabiel commented 5 years ago

Thank you so much! Great job!