constructor()

in usecases/base-ct-guest/lib/blea-security-alarm-stack.ts [18:306]


  constructor(scope: cdk.Construct, id: string, props: BLEASecurityAlarmStackProps) {
    super(scope, id, props);

    // SNS Topic for Security Alarm
    const secTopic = new sns.Topic(this, 'SecurityAlarmTopic');
    new sns.Subscription(this, 'SecurityAlarmEmail', {
      endpoint: props.notifyEmail,
      protocol: sns.SubscriptionProtocol.EMAIL,
      topic: secTopic,
    });
    this.alarmTopic = secTopic;

    // Allow to publish message from CloudWatch
    secTopic.addToResourcePolicy(
      new iam.PolicyStatement({
        effect: iam.Effect.ALLOW,
        principals: [new iam.ServicePrincipal('cloudwatch.amazonaws.com')],
        actions: ['sns:Publish'],
        resources: [secTopic.topicArn],
      }),
    );

    // --------------- ConfigRule Compliance Change Notification -----------------
    // ConfigRule - Compliance Change
    //  See: https://docs.aws.amazon.com/config/latest/developerguide/monitor-config-with-cloudwatchevents.html
    //  See: https://aws.amazon.com/premiumsupport/knowledge-center/config-resource-non-compliant/?nc1=h_ls
    //  If you want to add rules to notify, add rule name text string to "configRuleName" array.
    //  Sample Rule 'bb-default-security-group-closed' is defined at lib/blea-config-rules-stack.ts
    new cwe.Rule(this, 'BLEARuleConfigRules', {
      description: 'CloudWatch Event Rule to send notification on Config Rule compliance changes.',
      enabled: true,
      eventPattern: {
        source: ['aws.config'],
        detailType: ['Config Rules Compliance Change'],
        detail: {
          configRuleName: ['bb-default-security-group-closed'],
          newEvaluationResult: {
            complianceType: ['NON_COMPLIANT'],
          },
        },
      },
      targets: [new cwet.SnsTopic(secTopic)],
    });

    // ------------------------ AWS Health Notification ---------------------------

    // AWS Health - Notify any events on AWS Health
    // See: https://aws.amazon.com/premiumsupport/knowledge-center/cloudwatch-notification-scheduled-events/?nc1=h_ls
    new cwe.Rule(this, 'BLEARuleAwsHealth', {
      description: 'Notify AWS Health event',
      enabled: true,
      eventPattern: {
        source: ['aws.health'],
        detailType: ['AWS Health Event'],
      },
      targets: [new cwet.SnsTopic(secTopic)],
    });

    // ------------ Detective guardrails from NIST standard template ----------------
    // See: https://aws.amazon.com/quickstart/architecture/compliance-nist/?nc1=h_ls

    // Security Groups Change Notification
    // See: https://aws.amazon.com/premiumsupport/knowledge-center/monitor-security-group-changes-ec2/?nc1=h_ls
    //  from NIST template
    new cwe.Rule(this, 'BLEARuleSecurityGroupChange', {
      description: 'Notify to create, update or delete a Security Group.',
      enabled: true,
      eventPattern: {
        source: ['aws.ec2'],
        detailType: ['AWS API Call via CloudTrail'],
        detail: {
          eventSource: ['ec2.amazonaws.com'],
          eventName: [
            'AuthorizeSecurityGroupIngress',
            'AuthorizeSecurityGroupEgress',
            'RevokeSecurityGroupIngress',
            'RevokeSecurityGroupEgress',
          ],
        },
      },
      targets: [new cwet.SnsTopic(secTopic)],
    });

    // Network ACL Change Notification
    //  from NIST template
    new cwe.Rule(this, 'BLEARuleNetworkAclChange', {
      description: 'Notify to create, update or delete a Network ACL.',
      enabled: true,
      eventPattern: {
        source: ['aws.ec2'],
        detailType: ['AWS API Call via CloudTrail'],
        detail: {
          eventSource: ['ec2.amazonaws.com'],
          eventName: [
            'CreateNetworkAcl',
            'CreateNetworkAclEntry',
            'DeleteNetworkAcl',
            'DeleteNetworkAclEntry',
            'ReplaceNetworkAclEntry',
            'ReplaceNetworkAclAssociation',
          ],
        },
      },
      targets: [new cwet.SnsTopic(secTopic)],
    });

    // CloudTrail Change
    //  from NIST template
    new cwe.Rule(this, 'BLEARuleCloudTrailChange', {
      description: 'Notify to change on CloudTrail log configuration',
      enabled: true,
      eventPattern: {
        detailType: ['AWS API Call via CloudTrail'],
        detail: {
          eventSource: ['cloudtrail.amazonaws.com'],
          eventName: ['StopLogging', 'DeleteTrail', 'UpdateTrail'],
        },
      },
      targets: [new cwet.SnsTopic(secTopic)],
    });

    // LogGroup Construct for CloudTrail
    //   Use LogGroup.fromLogGroupName() because...
    //   On ControlTower environment, it created by not BLEA but ControlTower. So we need to refer existent LogGroup.
    //   When you use BLEA Standalone version, the LogGroup is created by BLEA.
    //
    //   Note:
    //     MetricFilter-based detection may delay for several minutes because of latency on CloudTrail Log delivery to CloudWatchLogs
    //     Use CloudWatch Events if you can, it deliver CloudTrail event faster.
    //     IAM event occur in us-east-1 region so if you want to detect it, you need to use MetrifFilter-based detection
    //     See: https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-aws-console-sign-in-events.html
    //
    const cloudTrailLogGroup = cwl.LogGroup.fromLogGroupName(this, 'CloudTrailLogGroup', props.cloudTrailLogGroupName);

    // IAM Policy Change Notification
    //  from NIST template
    const mfIAMPolicyChange = new cwl.MetricFilter(this, 'IAMPolicyChange', {
      logGroup: cloudTrailLogGroup,
      filterPattern: {
        logPatternString:
          '{($.eventName=DeleteGroupPolicy)||($.eventName=DeleteRolePolicy)||($.eventName=DeleteUserPolicy)||($.eventName=PutGroupPolicy)||($.eventName=PutRolePolicy)||($.eventName=PutUserPolicy)||($.eventName=CreatePolicy)||($.eventName=DeletePolicy)||($.eventName=CreatePolicyVersion)||($.eventName=DeletePolicyVersion)||($.eventName=AttachRolePolicy)||($.eventName=DetachRolePolicy)||($.eventName=AttachUserPolicy)||($.eventName=DetachUserPolicy)||($.eventName=AttachGroupPolicy)||($.eventName=DetachGroupPolicy)}',
      },
      metricNamespace: 'CloudTrailMetrics',
      metricName: 'IAMPolicyEventCount',
      metricValue: '1',
    });

    new cw.Alarm(this, 'IAMPolicyChangeAlarm', {
      metric: mfIAMPolicyChange.metric({
        period: cdk.Duration.seconds(300),
      }),
      evaluationPeriods: 1,
      datapointsToAlarm: 1,
      threshold: 1,
      comparisonOperator: cw.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
      alarmDescription: 'IAM Configuration changes detected!',
      actionsEnabled: true,
      statistic: cw.Statistic.SUM,
    }).addAlarmAction(new cwa.SnsAction(secTopic));

    // Unauthorized Attempts
    //  from NIST template
    const mfUnauthorizedAttempts = new cwl.MetricFilter(this, 'UnauthorizedAttempts', {
      logGroup: cloudTrailLogGroup,
      filterPattern: {
        logPatternString: '{($.errorCode=AccessDenied)||($.errorCode=UnauthorizedOperation)}',
      },
      metricNamespace: 'CloudTrailMetrics',
      metricName: 'UnauthorizedAttemptsEventCount',
      metricValue: '1',
    });

    new cw.Alarm(this, 'UnauthorizedAttemptsAlarm', {
      metric: mfUnauthorizedAttempts.metric({
        period: cdk.Duration.seconds(300),
      }),
      evaluationPeriods: 1,
      datapointsToAlarm: 1,
      threshold: 5,
      comparisonOperator: cw.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
      alarmDescription: 'Multiple unauthorized actions or logins attempted!',
      actionsEnabled: true,
      statistic: cw.Statistic.SUM,
    }).addAlarmAction(new cwa.SnsAction(secTopic));

    // NewAccessKeyCreated
    //  from NIST template
    const mfNewAccessKeyCreated = new cwl.MetricFilter(this, 'NewAccessKeyCreated', {
      logGroup: cloudTrailLogGroup,
      filterPattern: {
        logPatternString: '{($.eventName=CreateAccessKey)}',
      },
      metricNamespace: 'CloudTrailMetrics',
      metricName: 'NewAccessKeyCreatedEventCount',
      metricValue: '1',
    });

    new cw.Alarm(this, 'NewAccessKeyCreatedAlarm', {
      metric: mfNewAccessKeyCreated.metric({
        period: cdk.Duration.seconds(300),
      }),
      evaluationPeriods: 1,
      datapointsToAlarm: 1,
      threshold: 1,
      comparisonOperator: cw.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
      alarmDescription: 'Warning: New IAM access Eey was created. Please be sure this action was neccessary.',
      actionsEnabled: true,
      statistic: cw.Statistic.SUM,
    }).addAlarmAction(new cwa.SnsAction(secTopic));

    // Detect Root Activity from CloudTrail Log (For SecurityHub CIS 1.1)
    // See: https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-standards-cis-controls-1.1
    // See: https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail-additional-examples.html
    const mfRooUserPolicy = new cwl.MetricFilter(this, 'RootUserPolicyEventCount', {
      logGroup: cloudTrailLogGroup,
      filterPattern: {
        logPatternString:
          '{$.userIdentity.type="Root" && $.userIdentity.invokedBy NOT EXISTS && $.eventType !="AwsServiceEvent"}',
      },
      metricNamespace: 'CloudTrailMetrics',
      metricName: 'RootUserPolicyEventCount',
      metricValue: '1',
    });

    new cw.Alarm(this, 'RootUserPolicyEventCountAlarm', {
      metric: mfRooUserPolicy.metric({
        period: cdk.Duration.seconds(300),
      }),
      evaluationPeriods: 1,
      datapointsToAlarm: 1,
      threshold: 1,
      comparisonOperator: cw.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
      alarmDescription: 'Root user activity detected!',
      actionsEnabled: true,
      statistic: cw.Statistic.SUM,
    }).addAlarmAction(new cwa.SnsAction(secTopic));

    // ------------------- Other security services integration ----------------------

    // SecurityHub - Imported
    //   Security Hub automatically sends all new findings and all updates to existing findings to EventBridge as Security Hub Findings - Imported events.
    //   See: https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cwe-integration-types.html
    //
    //   Security Hub Finding format
    //   See: https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-findings-format.html
    new cwe.Rule(this, 'BLEARuleSecurityHub', {
      description: 'CloudWatch Event Rule to send notification on SecurityHub all new findings and all updates.',
      enabled: true,
      eventPattern: {
        source: ['aws.securityhub'],
        detailType: ['Security Hub Findings - Imported'],
        detail: {
          findings: {
            Severity: {
              Label: ['CRITICAL', 'HIGH'],
            },
            Compliance: {
              Status: ['FAILED'],
            },
            Workflow: {
              Status: ['NEW', 'NOTIFIED'],
            },
            RecordState: ['ACTIVE'],
          },
        },
      },
      targets: [new cwet.SnsTopic(secTopic)],
    });

    // GuardDutyFindings
    //   Will alert for any Medium to High finding.
    //   See: https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_findings_cloudwatch.html
    new cwe.Rule(this, 'BLEARuleGuardDuty', {
      description: 'CloudWatch Event Rule to send notification on GuardDuty findings.',
      enabled: true,
      eventPattern: {
        source: ['aws.guardduty'],
        detailType: ['GuardDuty Finding'],
        detail: {
          severity: [
            4, 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9, 5, 5.0, 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8, 5.9, 6,
            6.0, 6.1, 6.2, 6.3, 6.4, 6.5, 6.6, 6.7, 6.8, 6.9, 7, 7.0, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.8, 7.9, 8,
            8.0, 8.1, 8.2, 8.3, 8.4, 8.5, 8.6, 8.7, 8.8, 8.9,
          ],
        },
      },
      targets: [new cwet.SnsTopic(secTopic)],
    });
  }