Amazon Inspector(1)Inspectorの概要

Inspectorとは

Amazon Inspectorは自動化されたセキュリティ評価サービスで、AWS上にデプロイされた アプリケーションに脆弱性やベストプラクティスからの逸脱などがないかを自動的に評価 し、重大性の順に項目を並べた結果リストの作成を行う。このサービスを利用することで、EC2への意図しないネットワークアクセスや脆弱性をチェックすることができ、アプリケーションのセキュリティとコンプライアンスを向上することができる Inspectorを使用することで、システムの脆弱性診断を自動化できる。

エージェント

Inspectorを使用する前に、サポートしているOS, リージョン等を確認する。また、対象のEC2インスタンスにInspectorエージェントをインストールする。インストールの際にはroot権限が必要で、System Managersの AmazonInspector-ManageAWSAgent ドキュメントを実行することで、リモートからエージェントをインストールすることも可能である。また、エージェントをインストールしたインスタンスは、テレメトリデータのやりとりのために、Inspectorのエンドポイントと通信できるようにする必要がある。なお、エージェントプログラムに更新が発生する場合は、自動的に適用されるため、手動による管理は不要 。

Inspectorが評価を行う対象のインスタンスは、評価ターゲット で規定する。タグベースで指定 することも、 全てのインスタンスを指定 することもできる。

評価とルールパッケージ

Amazon Inspectorは、指定されたターゲット内のリソースから動作や設定データを収集し、これらの情報と指定されたルールパッケージとの比較を行う。ターゲットのEC2にエージェントをインストールすることで、これらの動作を実行することが可能となる。また、ネットワーク到達可能性ルールパッケージに関しては、エージェントレスで評価を実行することができる。

指定したルールには重要度が規定されており、重要度の大きな順から、 High , Medium , Low , Informational の4つのレベルが定められている。ルールパッケージは、

  • ネットワーク到達可能性
  • 共通脆弱性識別子
  • Center for Internet Security (CIS) ベンチマーク
  • Amazon Inspector のセキュリティのベストプラクティス

の4つのルールパッケージに分類されており、どのパッケージを適用するか指定することができる。これらのルールは、 CloudWatch Eventsなどから指定した時刻に起動 することも、手動で評価を開始することもできる。

結果とモニタリング

評価後の結果には、奨励される対応策 も記述される。何らかの理由で対処できない問題については、結果から除外することもできる。

Amazon Inspector のセットアップ

CloudRrailはCloudFormationに対応しているため、CloudFormation経由でAmazon Inspectorをセットアップすることができる。

サービスにリンクされたロール と IAMロールの作成

Amazon Inspector で使用する Service-Linked Role と IAM Roleを作成する。

Resources:
  ServiceLinkedRoleForInspector:
    Type: AWS::IAM::ServiceLinkedRole
    DeletionPolicy: Retain
    Properties: 
      AWSServiceName: inspector.amazonaws.com
      Description: A service-linked role required for Amazon Inspector to access your resources.   
  IAMRoleForInspectorEvents:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: events.amazonaws.com
            Action: 'sts:AssumeRole'
      Description: A role required for CloudWatch Events to access Inspector.
      Policies:
        - PolicyName: !Sub 'DefaultSecuritySettings-AWSEventsInspectorPolicy-${AWS::Region}'
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 'inspector:StartAssessmentRun'
                Resource: '*'
      RoleName: !Sub 'DefaultSecuritySettings-InspectorEvents-${AWS::Region}'

Amazon Inspectorの有効化

Amazon Inspector の Assessment Target と Assessment Template を作成する。

本テンプレートは、上記の4つのルールパッケージに対応する。なお、AWSが公開しているルールパッケージのURLがリージョンごとに異なることから、本テンプレートは、Amazon Inspector に対応しているリージョンのうち、一部のリージョンのみに対応していることに留意する。

Resources:
  InspectorAssessmentTarget:
    DependsOn:
      - ServiceLinkedRoleForInspector
    Type: AWS::Inspector::AssessmentTarget
    Properties:
      AssessmentTargetName: DefaultSecuritySettings-Assessment-Target-All-Instances-All-Rules
  InspectorAssessmentTemplate:
    Type: AWS::Inspector::AssessmentTemplate
    Properties:
      AssessmentTargetArn: !GetAtt InspectorAssessmentTarget.Arn
      DurationInSeconds: 3600
      AssessmentTemplateName: DefaultSecuritySettings-Assessment-Template-Default-All-Rules
      RulesPackageArns:
        # Asia Pacific (Tokyo)リージョンの場合
        - arn:aws:inspector:ap-northeast-1:406045910587:rulespackage/0-gHP9oWNT
        - arn:aws:inspector:ap-northeast-1:406045910587:rulespackage/0-7WNjqgGu
        - arn:aws:inspector:ap-northeast-1:406045910587:rulespackage/0-YI95DVd7
        - arn:aws:inspector:ap-northeast-1:406045910587:rulespackage/0-bBUQnxMq

CloudWatch Events を設定

Amazon Inspector は、CloudWatch Eventsを用いてAssessment Templateを定期実行することができる。本テンプレートでは毎週月曜日午前9時になると、CloudWatch EventsがAmazon Inspectorを定期実行する。

Resources:
  CloudWatchEventsForInspector:
    Type: AWS::Events::Rule
    Properties: 
      Description: !Join
        - ''
        - - Scheduled Inspector Assessment for 
          - !GetAtt InspectorAssessmentTemplate.Arn
          - running every 7 day(s)
      Name: Amazon_Inspector_Assessment
      # Every Monday 9AM
      ScheduleExpression: cron(0 9 ? * 1 *)
      State: ENABLED
      Targets:
        - Arn: !GetAtt InspectorAssessmentTemplate.Arn
          Id: CloudWatchEventsForInspector
          RoleArn: !GetAtt IAMRoleForInspectorEvents.Arn

CloudFormation Launch Stack URL

以下のボタンから上のCloudFormationテンプレートを実行することが可能である。ソースコードは、aws-cloudformation-templates/security – GitHub にて公開。

作成されるAWSサービス CloudFormationテンプレート
セキュリティサービス全般 cloudformation-launch-stack
Inspector のみ cloudformation-launch-stack

AWS CloudTrail(2)CIS AWS Foundations Benchmarkに準拠するための通知設定

IAMパスワードポリシーの自動修復

CIS AWS Foundations Benchmark というセキュリティガイドラインが公開されており、このガイドラインは、AWSアカウントをセキュアに保つために必要なAWSのセキュリティ設定を集めたベストプラクティス集として活用できる。

この CIS AWS Foundations Benchmarkでは、CloudTrailによって検知された特定のイベントに対して以下のような対応を求めている。

  • 3.1 不正な API 呼び出しに対してログメトリクスフィルタとアラームが存在することを確認します
  • 3.2 MFA なしの AWS マネジメントコンソール サインインに対してログメトリクスフィルタとアラームが存在することを確認します
  • 3.3 「ルート」アカウントに対してログメトリクスフィルタとアラームが存在することを確認します
  • 3.4 MFA なしの IAM ポリシーの変更に対してログメトリクスフィルタとアラームが存在することを確認します
  • 3.5 MFA なしの CloudTrail 設定の変更に対してログメトリクスフィルタとアラームが存在することを確認します
  • 3.6 AWS マネジメントコンソール 認証の失敗に対してログメトリクスフィルタとアラームが存在することを確認します
  • 3.7 カスタマー作成の CMK の無効化またはスケジュールされた削除に対してログメトリクスフィルタとアラームが存在することを確認します
  • 3.8 S3 バケットの変更に対してログメトリクスフィルタとアラームが存在することを確認します
  • 3.9 AWS Config 設定の変更に対してログメトリクスフィルタとアラームが存在することを確認します
  • 3.10 セキュリティグループの変更に対するメトリクスフィルタとアラームが存在することを確認します
  • 3.11 ネットワークアクセスコントロールリスト (NACL) への変更に対するログメトリクスとアラームが存在することを確認します
  • 3.12 ネットワークゲートウェイへの変更に対するログメトリクスとアラームが存在することを確認します
  • 3.13 ルートテーブルの変更に対してログメトリクスフィルタとアラームが存在することを確認します
  • 3.14 VPC の変更に対してログメトリクスフィルタとアラームが存在することを確認します

上述のログメトリクスフィルタとアラームを設定し、CIS AWS Foundations Benchmark に 準拠するために以下の設定を行う。

  1. CloudWatch Logs の MetricFilter を作成する
  2. 上のメトリクスに対する CloudWatch Alarm を作成する
  3. 閾値を超えた場合にAmazon SNSに通知を送る
Resources:
  # CIS 3.1 a log metric filter and alarm exist for unauthorized API calls 
  CloudWatchLogsMetricFilterCloudTrailUnauthorizedAPICalls:
    Type: 'AWS::Logs::MetricFilter'
    Properties:
      FilterPattern: '{($.errorCode="*UnauthorizedOperation") || ($.errorCode="AccessDenied*")}'
      LogGroupName: !Ref CloudWatchLogsGroupForCloudTrail
      MetricTransformations:
        - MetricName: UnauthorizedAPICalls 
          MetricNamespace: LogMetrics
          MetricValue: '1'
  CloudWatchAlarmMetricFilterCloudTrailUnauthorizedAPICalls:
    Type: 'AWS::CloudWatch::Alarm'
    Properties:
      ActionsEnabled: true
      AlarmActions:
        - !Ref SnsTopicARN
      AlarmDescription: '*CloudTrail* が *不正なAPIコールを検知* しました。'
      AlarmName: !Sub 'Warning-${AWS::StackName}-CloudTrail-Unauthorized-API-Calls'
      ComparisonOperator: GreaterThanOrEqualToThreshold
      DatapointsToAlarm: 6
      EvaluationPeriods: 60
      MetricName: UnauthorizedAPICalls
      Namespace: LogMetrics
      OKActions:
        - !Ref SnsTopicARN
      Period: 60
      Statistic: Maximum
      Threshold: 1
      TreatMissingData: notBreaching
  # CIS 3.2 a log metric filter and alarm exist for AWS Management Console sign-in without MFA
  CloudWatchLogsMetricFilterCloudTrailLoginWithoutMFA:
    Type: 'AWS::Logs::MetricFilter'
    Properties:
      FilterPattern: '{($.eventName="ConsoleLogin") && ($.additionalEventData.MFAUsed !="Yes")}'
      LogGroupName: !Ref CloudWatchLogsGroupForCloudTrail
      MetricTransformations:
        - MetricName: LoginWithoutMFA 
          MetricNamespace: LogMetrics
          MetricValue: '1'
  CloudWatchAlarmMetricFilterCloudTrailLoginWithoutMFA:
    Type: 'AWS::CloudWatch::Alarm'
    Properties:
      ActionsEnabled: true
      AlarmActions:
        - !Ref SnsTopicARN
      AlarmDescription: '*CloudTrail* が *MFAなしのログインを検知* しました。'
      AlarmName: !Sub 'Warning-${AWS::StackName}-CloudTrail-Login-Without-MFA'
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      MetricName: LoginWithoutMFA
      Namespace: LogMetrics
      OKActions:
        - !Ref SnsTopicARN
      Period: 60
      Statistic: Maximum
      Threshold: 1
      TreatMissingData: notBreaching
  # CIS 3.3 a log metric filter and alarm exist for usage of "root" account 
  CloudWatchLogsMetricFilterCloudTrailForRootAcount:
    Type: 'AWS::Logs::MetricFilter'
    Properties:
      FilterPattern: '{$.userIdentity.type="Root" && $.userIdentity.invokedBy NOT EXISTS && $.eventType !="AwsServiceEvent"}'
      LogGroupName: !Ref CloudWatchLogsGroupForCloudTrail
      MetricTransformations:
        - MetricName: ForRootAcount 
          MetricNamespace: LogMetrics
          MetricValue: '1'
  CloudWatchAlarmMetricFilterCloudTrailForRootAcount:
    Type: 'AWS::CloudWatch::Alarm'
    Properties:
      ActionsEnabled: true
      AlarmActions:
        - !Ref SnsTopicARN
      AlarmDescription: '*CloudTrail* が *ルートアカウントに対する変更を検知* しました。'
      AlarmName: !Sub 'Warning-${AWS::StackName}-CloudTrail-For-RootAcount'
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      MetricName: ForRootAcount
      Namespace: LogMetrics
      OKActions:
        - !Ref SnsTopicARN
      Period: 60
      Statistic: Maximum
      Threshold: 1
      TreatMissingData: notBreaching
  # CIS 3.4 a log metric filter and alarm exist for IAM policy changes 
  CloudWatchLogsMetricFilterCloudTrailIamPolicyChanges:
    Type: 'AWS::Logs::MetricFilter'
    Properties:
      FilterPattern: '{($.eventName=DeleteGroupPolicy) || ($.eventName=DeleteRolePolicy) || ($.eventName=DeleteUserPolicy) || ($.eventName=PutGroupPolicy) || ($.eventName=PutRolePolicy) || ($.eventName=PutUserPolicy) || ($.eventName=CreatePolicy) || ($.eventName=DeletePolicy) || ($.eventName=CreatePolicyVersion) || ($.eventName=DeletePolicyVersion) || ($.eventName=AttachRolePolicy) || ($.eventName=DetachRolePolicy) || ($.eventName=AttachUserPolicy) || ($.eventName=DetachUserPolicy) || ($.eventName=AttachGroupPolicy) || ($.eventName=DetachGroupPolicy)}'
      LogGroupName: !Ref CloudWatchLogsGroupForCloudTrail
      MetricTransformations:
        - MetricName: IamPolicyChanges 
          MetricNamespace: LogMetrics
          MetricValue: '1'
  CloudWatchAlarmMetricFilterCloudTrailIamPolicyChanges:
    Type: 'AWS::CloudWatch::Alarm'
    Properties:
      ActionsEnabled: true
      AlarmActions:
        - !Ref SnsTopicARN
      AlarmDescription: '*CloudTrail* が *IAMポリシーの変更を検知* しました。'
      AlarmName: !Sub 'Warning-${AWS::StackName}-CloudTrail-IamPolicy-Changes'
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      MetricName: IamPolicyChanges
      Namespace: LogMetrics
      OKActions:
        - !Ref SnsTopicARN
      Period: 60
      Statistic: Maximum
      Threshold: 1
      TreatMissingData: notBreaching
  # CIS 3.5 a log metric filter and alarm exist for CloudTrail configuration changes 
  CloudWatchLogsMetricFilterCloudTrailCloudTrailConfigurationChanges:
    Type: 'AWS::Logs::MetricFilter'
    Properties:
      FilterPattern: '{($.eventName=CreateTrail) || ($.eventName=UpdateTrail) || ($.eventName=DeleteTrail) || ($.eventName=StartLogging) || ($.eventName=StopLogging)}'
      LogGroupName: !Ref CloudWatchLogsGroupForCloudTrail
      MetricTransformations:
        - MetricName: CloudTrailConfigurationChanges 
          MetricNamespace: LogMetrics
          MetricValue: '1'
  CloudWatchAlarmMetricFilterCloudTrailCloudTrailConfigurationChanges:
    Type: 'AWS::CloudWatch::Alarm'
    Properties:
      ActionsEnabled: true
      AlarmActions:
        - !Ref SnsTopicARN
      AlarmDescription: '*CloudTrail* が *CloudTrailの設定変更を検知* しました。'
      AlarmName: !Sub 'Warning-${AWS::StackName}-CloudTrail-CloudTrailConfiguration-Changes'
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      MetricName: CloudTrailConfigurationChanges
      Namespace: LogMetrics
      OKActions:
        - !Ref SnsTopicARN
      Period: 60
      Statistic: Maximum
      Threshold: 1
      TreatMissingData: notBreaching
  # CIS 3.6 a log metric filter and alarm exist for AWS Management Console authentication failures
  CloudWatchLogsMetricFilterCloudTrailAuthenticationFailuresDetected:
    Type: 'AWS::Logs::MetricFilter'
    Properties:
      FilterPattern: '{($.eventName=ConsoleLogin) && ($.errorMessage="Failed authentication")}'
      LogGroupName: !Ref CloudWatchLogsGroupForCloudTrail
      MetricTransformations:
        - MetricName: AuthenticationFailuresDetected 
          MetricNamespace: LogMetrics
          MetricValue: '1'
  CloudWatchAlarmMetricFilterCloudTrailAuthenticationFailuresDetected:
    Type: 'AWS::CloudWatch::Alarm'
    Properties:
      ActionsEnabled: true
      AlarmActions:
        - !Ref SnsTopicARN
      AlarmDescription: '*CloudTrail* が *ログイン認証失敗を検知* しました。'
      AlarmName: !Sub 'Warning-${AWS::StackName}-CloudTrail-AuthenticationFailures-Detected'
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      MetricName: AuthenticationFailuresDetected
      Namespace: LogMetrics
      OKActions:
        - !Ref SnsTopicARN
      Period: 60
      Statistic: Maximum
      Threshold: 1
      TreatMissingData: notBreaching
  # CIS 3.7 a log metric filter and alarm exist for disabling or scheduled deletion of customer created CMKs 
  CloudWatchLogsMetricFilterCloudTrailCustomerCreatedCMKsChanges:
    Type: 'AWS::Logs::MetricFilter'
    Properties:
      FilterPattern: '{($.eventSource=kms.amazonaws.com) && (($.eventName=DisableKey) || ($.eventName=ScheduleKeyDeletion))}'
      LogGroupName: !Ref CloudWatchLogsGroupForCloudTrail
      MetricTransformations:
        - MetricName: CustomerCreatedCMKsChanges 
          MetricNamespace: LogMetrics
          MetricValue: '1'
  CloudWatchAlarmMetricFilterCloudTrailCustomerCreatedCMKsChanges:
    Type: 'AWS::CloudWatch::Alarm'
    Properties:
      ActionsEnabled: true
      AlarmActions:
        - !Ref SnsTopicARN
      AlarmDescription: '*CloudTrail* が *カスタマー作成CMKの無効化もしくは削除を検知* しました。'
      AlarmName: !Sub 'Warning-${AWS::StackName}-CloudTrail-CustomerCreatedCMKs-Changes'
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      MetricName: CustomerCreatedCMKsChanges
      Namespace: LogMetrics
      OKActions:
        - !Ref SnsTopicARN
      Period: 60
      Statistic: Maximum
      Threshold: 1
      TreatMissingData: notBreaching
  # CIS 3.8 a log metric filter and alarm exist for S3 bucket policy changes 
  CloudWatchLogsMetricFilterCloudTrailS3BucketPolicyChanges:
    Type: 'AWS::Logs::MetricFilter'
    Properties:
      FilterPattern: '{($.eventSource=s3.amazonaws.com) && (($.eventName=PutBucketAcl) || ($.eventName=PutBucketPolicy) || ($.eventName=PutBucketCors) || ($.eventName=PutBucketLifecycle) || ($.eventName=PutBucketReplication) || ($.eventName=DeleteBucketPolicy) || ($.eventName=DeleteBucketCors) || ($.eventName=DeleteBucketLifecycle) || ($.eventName=DeleteBucketReplication))}'
      LogGroupName: !Ref CloudWatchLogsGroupForCloudTrail
      MetricTransformations:
        - MetricName: S3BucketPolicyChanges 
          MetricNamespace: LogMetrics
          MetricValue: '1'
  CloudWatchAlarmMetricFilterCloudTrailS3BucketPolicyChanges:
    Type: 'AWS::CloudWatch::Alarm'
    Properties:
      ActionsEnabled: true
      AlarmActions:
        - !Ref SnsTopicARN
      AlarmDescription: '*CloudTrail* が *S3のバケットポリシーの変更を検知* しました。'
      AlarmName: !Sub 'Notice-${AWS::StackName}-CloudTrail-S3BucketPolicy-Changes'
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      MetricName: S3BucketPolicyChanges
      Namespace: LogMetrics
      OKActions:
        - !Ref SnsTopicARN
      Period: 60
      Statistic: Maximum
      Threshold: 1
      TreatMissingData: notBreaching
  # CIS 3.9 a log metric filter and alarm exist for AWS Config configuration changes 
  CloudWatchLogsMetricFilterCloudTrailConfigConfigurationChanges:
    Type: 'AWS::Logs::MetricFilter'
    Properties: 
      FilterPattern: '{($.eventSource=config.amazonaws.com) && (($.eventName=StopConfigurationRecorder) || ($.eventName=DeleteDeliveryChannel) || ($.eventName=PutDeliveryChannel) || ($.eventName=PutConfigurationRecorder))}'
      LogGroupName: !Ref CloudWatchLogsGroupForCloudTrail
      MetricTransformations:
        - MetricName: ConfigConfigurationChanges 
          MetricNamespace: LogMetrics
          MetricValue: '1'
  CloudWatchAlarmMetricFilterCloudTrailConfigConfigurationChanges:
    Type: 'AWS::CloudWatch::Alarm'
    Properties:
      ActionsEnabled: true
      AlarmActions:
        - !Ref SnsTopicARN
      AlarmDescription: '*CloudTrail* が *Configの設定変更を検知* しました。'
      AlarmName: !Sub 'Warning-${AWS::StackName}-CloudTrail-ConfigConfiguration-Changes'
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      MetricName: ConfigConfigurationChanges
      Namespace: LogMetrics
      OKActions:
        - !Ref SnsTopicARN
      Period: 60
      Statistic: Maximum
      Threshold: 1
      TreatMissingData: notBreaching
  # CIS 3.10 a log metric filter and alarm exist for changes to Security Group 
  CloudWatchLogsMetricFilterCloudTrailSecurityGroupChanges:
    Type: 'AWS::Logs::MetricFilter'
    Properties: 
      FilterPattern: '{($.eventName=AuthorizeSecurityGroupIngress) || ($.eventName=AuthorizeSecurityGroupEgress) || ($.eventName=RevokeSecurityGroupIngress) || ($.eventName=RevokeSecurityGroupEgress) || ($.eventName=CreateSecurityGroup) || ($.eventName=DeleteSecurityGroup)}'
      LogGroupName: !Ref CloudWatchLogsGroupForCloudTrail
      MetricTransformations:
        - MetricName: SecurityGroupChanges 
          MetricNamespace: LogMetrics
          MetricValue: '1'
  CloudWatchAlarmMetricFilterCloudTrailSecurityGroupChanges:
    Type: 'AWS::CloudWatch::Alarm'
    Properties:
      ActionsEnabled: true
      AlarmActions:
        - !Ref SnsTopicARN
      AlarmDescription: '*CloudTrail* が *セキュリティグループの変更を検知* しました。'
      AlarmName: !Sub 'Warning-${AWS::StackName}-CloudTrail-SecurityGroup-Changes'
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      MetricName: SecurityGroupChanges
      Namespace: LogMetrics
      OKActions:
        - !Ref SnsTopicARN
      Period: 60
      Statistic: Maximum
      Threshold: 1
      TreatMissingData: notBreaching
  # CIS 3.11 a log metric filter and alarm exist for changes to Network Access Control Lists (NACL) 
  CloudWatchLogsMetricFilterCloudTrailNACLChanges:
    Type: 'AWS::Logs::MetricFilter'
    Properties: 
      FilterPattern: '{($.eventName=CreateNetworkAcl) || ($.eventName=CreateNetworkAclEntry) || ($.eventName=DeleteNetworkAcl) || ($.eventName=DeleteNetworkAclEntry) || ($.eventName=ReplaceNetworkAclEntry) || ($.eventName=ReplaceNetworkAclAssociation)}'
      LogGroupName: !Ref CloudWatchLogsGroupForCloudTrail
      MetricTransformations:
        - MetricName: NACLChanges 
          MetricNamespace: LogMetrics
          MetricValue: '1'
  CloudWatchAlarmMetricFilterCloudTrailNACLChanges:
    Type: 'AWS::CloudWatch::Alarm'
    Properties:
      ActionsEnabled: true
      AlarmActions:
        - !Ref SnsTopicARN
      AlarmDescription: '*CloudTrail* が *NACLの変更を検知* しました。'
      AlarmName: !Sub 'Notice-${AWS::StackName}-CloudTrail-NACL-Changes'
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      MetricName: NACLChanges
      Namespace: LogMetrics
      OKActions:
        - !Ref SnsTopicARN
      Period: 60
      Statistic: Maximum
      Threshold: 1
      TreatMissingData: notBreaching
  # CIS 3.12 a log metric filter and alarm exist for changes to network gateways
  CloudWatchLogsMetricFilterCloudTrailNetworkGatewayChanges:
    Type: 'AWS::Logs::MetricFilter'
    Properties: 
      FilterPattern: '{($.eventName=CreateCustomerGateway) || ($.eventName=DeleteCustomerGateway) || ($.eventName=AttachInternetGateway) || ($.eventName=CreateInternetGateway) || ($.eventName=DeleteInternetGateway) || ($.eventName=DetachInternetGateway)}'
      LogGroupName: !Ref CloudWatchLogsGroupForCloudTrail
      MetricTransformations:
        - MetricName: NetworkGatewayChanges 
          MetricNamespace: LogMetrics
          MetricValue: '1'
  CloudWatchAlarmMetricFilterCloudTrailNetworkGatewayChanges:
    Type: 'AWS::CloudWatch::Alarm'
    Properties:
      ActionsEnabled: true
      AlarmActions:
        - !Ref SnsTopicARN
      AlarmDescription: '*CloudTrail* が *ネットワークゲートウェイの変更を検知* しました。'
      AlarmName: !Sub 'Notice-${AWS::StackName}-CloudTrail-NetworkGateway-Changes'
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      MetricName: NetworkGatewayChanges
      Namespace: LogMetrics
      OKActions:
        - !Ref SnsTopicARN
      Period: 60
      Statistic: Maximum
      Threshold: 1
      TreatMissingData: notBreaching
  # CIS 3.13 a log metric filter and alarm exist for route table changes
  CloudWatchLogsMetricFilterCloudTrailRouteTableChanges:
    Type: 'AWS::Logs::MetricFilter'
    Properties: 
      FilterPattern: '{($.eventName=CreateRoute) || ($.eventName=CreateRouteTable) || ($.eventName=ReplaceRoute) || ($.eventName=ReplaceRouteTableAssociation) || ($.eventName=DeleteRouteTable) || ($.eventName=DeleteRoute) || ($.eventName=DisassociateRouteTable)}'
      LogGroupName: !Ref CloudWatchLogsGroupForCloudTrail
      MetricTransformations:
        - MetricName: RouteTableChanges 
          MetricNamespace: LogMetrics
          MetricValue: '1'
  CloudWatchAlarmMetricFilterCloudTrailRouteTableChanges:
    Type: 'AWS::CloudWatch::Alarm'
    Properties:
      ActionsEnabled: true
      AlarmActions:
        - !Ref SnsTopicARN
      AlarmDescription: '*CloudTrail* が *ルートテーブルの変更を検知* しました。'
      AlarmName: !Sub 'Notice-${AWS::StackName}-CloudTrail-RouteTable-Changes'
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      MetricName: RouteTableChanges
      Namespace: LogMetrics
      OKActions:
        - !Ref SnsTopicARN
      Period: 60
      Statistic: Maximum
      Threshold: 1
      TreatMissingData: notBreaching
  # CIS 3.14 a log metric filter and alarm exist for VPC changes 
  CloudWatchLogsMetricFilterCloudTrailVpcChanges:
    Type: 'AWS::Logs::MetricFilter'
    Properties: 
      FilterPattern: '{($.eventName=CreateVpc) || ($.eventName=DeleteVpc) || ($.eventName=ModifyVpcAttribute) || ($.eventName=AcceptVpcPeeringConnection) || ($.eventName=CreateVpcPeeringConnection) || ($.eventName=DeleteVpcPeeringConnection) || ($.eventName=RejectVpcPeeringConnection) || ($.eventName=AttachClassicLinkVpc) || ($.eventName=DetachClassicLinkVpc) || ($.eventName=DisableVpcClassicLink) || ($.eventName=EnableVpcClassicLink)}'
      LogGroupName: !Ref CloudWatchLogsGroupForCloudTrail
      MetricTransformations:
        - MetricName: VpcChanges 
          MetricNamespace: LogMetrics
          MetricValue: '1'
  CloudWatchAlarmMetricFilterCloudTrailVpcChanges:
    Type: 'AWS::CloudWatch::Alarm'
    Properties:
      ActionsEnabled: true
      AlarmActions:
        - !Ref SnsTopicARN
      AlarmDescription: '*CloudTrail* が *VPCの変更を検知* しました。'
      AlarmName: !Sub 'Notice-${AWS::StackName}-CloudTrail-Vpc-Changes'
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods: 1
      MetricName: VpcChanges
      Namespace: LogMetrics
      OKActions:
        - !Ref SnsTopicARN
      Period: 60
      Statistic: Maximum
      Threshold: 1
      TreatMissingData: notBreaching

また、これらのメトリクスのアラーム通知先となる、Amazon SNS と Topic Policy を作成する。

Resources:
  SNSAlert:
    Type: 'AWS::SNS::Topic'
    Properties:
      DisplayName: !Sub 'System Notifications from ${AWS::StackName}'
      TopicName: !Sub '${AWS::StackName}-system-notifications'
  SNSAlertTopicPolicy:
    Type: 'AWS::SNS::TopicPolicy'
    Properties:
      Topics: 
        - !Ref SNSAlert
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            # All AWS Services
            Principal:
              AWS: '*'
            Resource: '*'
            Action: 'SNS:Publish'

AWS CloudTrail(1)CloudTrailの概要

CloudTrailとは

CloudTrailは、AWS アカウントの ガバナンス、コンプライアンス、運用監査、リスク監査を行うためのサービス。AWSマネージメントコンソールやAWS CLI、SDK、APIで実行されたアクションを、ログに記録する。API アクティビティに特化した機能であり、パフォーマンスのモニタリングとシステムの正常性をモニタリングできるCloudWatchとは用途が異なる。

CloudTrailは、AWSアカウントを作成した際に自動的に有効になる。イベントとして記録された証跡と呼ばれるこれらのデータは、過去90日分を表示、検索、ダウンロード、アーカイブ、分析等を行うことができる。

証跡はサーバサイド暗号化を使用してS3バケットへ配信、およびAmazon CloudWatch Logs 、Amazon CloudWatch Eventsなどを用いて配信や分析を行うことができる。ログは、 1時間に複数回、約5分ごとに発行され、イベント発生から15分以内に配信される。CloudTrailには、設定変更などを含む管理イベントと情報の取得などを含むデータイベントの2つの種類のイベントが保存できるが、標準では管理イベントのみが保存される。設定により、S3およびLambdaのデータイベントを証跡に含ませることができる。なお、CloudTrailは全てのリージョンに適用可能であるが、イベントをログに記録するリージョンのみで表示することができる。

セキュリティ

以下の手段を用いることで、CloudTrailが生成した証跡を適切に保全し、インシデント発生時のフォレンジックを正常に遂行することができる。

データ保護

  • 診断ログに機密情報が残ることを避けるため、名前フィールドなどに機密情報を載せることはしない。

検出

  • 全てのリージョンで証跡を有効化する
  • 証跡を保存するS3バケットは、サーバサード暗号化を行う
  • CloudWatch Logsとの統合を行い、イベントの監視を行う

予防

  • 証跡を保存するS3バケットは、専用のAWSアカウントに保存する
  • 証跡を保存するS3バケットには、最低限のアクセス権限のみ付与する。
  • 証跡を保存するS3バケットに対して、MFA Deleteを有効化する

AWS CloudTrail のセットアップ

CloudRrailはCloudFormationに対応しているため、CloudFormation経由でCloudTrailをセットアップすることができる。

IAM Roleの有効化

CloudTrailにCloudWatch Logsへのアクセス権限を付与する。

Resources:
  IAMRoleForCloudTrail:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: cloudtrail.amazonaws.com
            Action: 'sts:AssumeRole'
      Description: A role required for CloudTrail to access CloudWatch Logs.
      Policies:
        - PolicyName: !Sub '${AWS::StackName}-AWSCloudTrailCloudWatchLogsPolicy-${AWS::Region}'
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 'logs:PutLogEvents'
                  - 'logs:CreateLogStream'
                Resource:
                  - !GetAtt CloudWatchLogsGroupForCloudTrail.Arn
      RoleName: !Sub '${AWS::StackName}-CloudTrail-${AWS::Region}'

S3バケットの作成

証跡を保存するS3バケットと、CloudTrailからS3バケットへのアクセスを許可する、バケットポリシーを作成する。適切なバケットポリシーを設定しないと、 InsufficientS3BucketPolicyException エラーが発生する。

Resouces:
  S3ForCloudTrail:
    Type: 'AWS::S3::Bucket'
    DeletionPolicy: Retain
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration: 
          - ServerSideEncryptionByDefault: 
              SSEAlgorithm: aws:kms
      BucketName: !Sub defaultsecuritysettings-cloudtrail-${AWS::Region}-${AWS::AccountId}
      LifecycleConfiguration:
        Rules:
          - Id: ExpirationInDays
            ExpirationInDays: 60
            Status: Enabled
      LoggingConfiguration:
        DestinationBucketName: !Ref LogBacketName
        LogFilePrefix: CloudTrail/
      PublicAccessBlockConfiguration: 
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
  S3BucketPolicyForCloudTrail:
    Type: 'AWS::S3::BucketPolicy'
    Properties: 
      Bucket: !Ref S3ForCloudTrail
      PolicyDocument:
        Version: 2012-10-17
        Id: !Ref S3ForCloudTrail
        Statement:
          - Effect: Allow
            Principal:
              Service: cloudtrail.amazonaws.com
            Action:
              - 's3:GetBucketAcl'
            Resource:
              - !GetAtt S3ForCloudTrail.Arn
          - Effect: Allow
            Principal:
              Service: cloudtrail.amazonaws.com
            Action:
              - 's3:PutObject'
            Resource:
              - !Join
                - ''
                - - !GetAtt S3ForCloudTrail.Arn
                  - /AWSLogs/
                  - !Sub ${AWS::AccountId}
                  - /*
            Condition:
              StringEquals:
                s3:x-amz-acl: bucket-owner-full-control

CloudWatch Logsの作成

CloudWatch Logs の Log Group を作成する。

Resources:
  CloudWatchLogsGroupForCloudTrail:
    Type: 'AWS::Logs::LogGroup'
    Properties:
      LogGroupName: !Sub '/aws/cloudtrail/${AWS::StackName}'
      RetentionInDays: 365

CloudTrailの有効化

CloudTrail を有効化する。先ほど作成したS3バケットとCloudWatch Logsを証跡の出力先に指定する。通常は、データイベントの保存は行わないが、以下のテンプレートでは、S3とLambdaのデータイベントも保存している。

Resources:
  CloudTrail:
    DependsOn:
      - S3BucketPolicyForCloudTrail
    Condition: CreateCentralizedResources
    Type: 'AWS::CloudTrail::Trail'
    Properties:
      CloudWatchLogsLogGroupArn: !GetAtt CloudWatchLogsGroupForCloudTrail.Arn
      CloudWatchLogsRoleArn: !GetAtt IAMRoleForCloudTrail.Arn
      EnableLogFileValidation: true
      EventSelectors:
        - DataResources: 
            # All S3 buckets
            - Type: AWS::S3::Object
              Values: 
                - arn:aws:s3
            # All Lambda functions
            - Type: AWS::Lambda::Function
              Values: 
                - arn:aws:lambda
      IncludeGlobalServiceEvents: true
      IsLogging: true
      IsMultiRegionTrail: true
      S3BucketName: !Ref S3ForCloudTrail
      SnsTopicName: !Ref SnsTopicName
      TrailName: !Sub ${AWS::StackName}

CloudFormation Launch Stack URL

以下のボタンから上のCloudFormationテンプレートを実行することが可能である。ソースコードは、aws-cloudformation-templates/security – GitHub にて公開。

作成されるAWSサービス CloudFormationテンプレート
セキュリティサービス全般 cloudformation-launch-stack
CloudTrailのみ cloudformation-launch-stack

AWS Config(6)デフォルトセキュリティグループの無効化

デフォルトセキュリティグループ

CIS AWS Foundations Benchmark というセキュリティガイドラインが公開されており、このガイドラインは、AWSアカウントをセキュアに保つために必要なAWSのセキュリティ設定を集めたベストプラクティス集として活用できる。

この CIS AWS Foundations Benchmarkでは、デフォルトセキュリティグループについて、以下の設定とすることが奨励されている。

  • 4.3 IAM すべての VPC のデフォルトセキュリティグループがすべてのトラフィックを制限するようにします

この CIS AWS Foundations Benchmark に 準拠していないデフォルトセキュリティグループ を SSM Automation を用いて 自動修復 するために以下の設定を行う。

  1. 上記のポリシーに準拠しているか AWS Config を用いて定期的にチェックを行う
  2. 非準拠であった場合には、AWS Config が SSM Automationを自動起動する
  3. SSM Automation が非準拠のデフォルトセキュリティグループの設定を自動修復する

1. AWS Configの有効化

AWS Configを有効化する手順については、こちら。

2. AWS Configを用いた定期チェック

デフォルトセキュリティグループ設定のチェックには、あらかじめAWS Configに用意されている vpc-default-security-group-closed マネージドルールを使用する。デフォルトセキュリティグループがこの条件を満たしていない場合、このリソースはルールに 非準拠(NON_COMPLIANT) であると判定される。

なお、この Config Rule を設定する前に ConfigurationRecorder を生成しておく必要がある。そこで、DependsOn 属性に ConfigurationRecorder リソースを設定している。

Resources:
  ConfigVpcDefaultSecurityGroupClosed:
    DependsOn:
      - ConfigConfigurationRecorder
    Type: 'AWS::Config::ConfigRule'
    Properties:
      ConfigRuleName: vpc-default-security-group-closed
      Description: いずれの Amazon Virtual Private Cloud (VPC) のデフォルトのセキュリティグループでもインバウンドとアウトバウンドのいずれのトラフィックも許可しないことを確認します。
      Source:
        Owner: AWS
        SourceIdentifier: VPC_DEFAULT_SECURITY_GROUP_CLOSED 

3. SSM Automation を用いた自動修復

Systems Manager Automation は、AWS Config で 直接指定できる、現時点で唯一の自動修復手段 となっている。そこで、デフォルトのセキュリティグループを修復する Systems Manager Automation ドキュメント を作成し、AWS Config との紐付けを行う。

下のSystems Manager Automation ドキュメントは、EC2 の RevokeSecurityGroupIngress, RevokeSecurityGroupEgress, DescribeSecurityGroups を実行して、デフォルトセキュリティグループの設定を自動修復する。

Resources:
  SSMAutomationRevokeDefaultSecurityGroup:
    Type: 'AWS::SSM::Document'
    Properties: 
      Content:
        schemaVersion: "0.3"
        assumeRole: "{{ AutomationAssumeRole }}"
        description: Revoke Default Security Group.
        mainSteps:
          - name: DescribeSecurityGroups
            action: aws:executeAwsApi
            onFailure: Abort
            inputs:
              Service: ec2
              Api: DescribeSecurityGroups
              GroupIds: ["{{ GroupId }}"]
            outputs:
              - Name: IpPermissionsIngress
                Selector: $.SecurityGroups[0].IpPermissions
                Type: MapList
              - Name: IpPermissionsEgress
                Selector: $.SecurityGroups[0].IpPermissionsEgress
                Type: MapList
          - name: RevokeSecurityGroupIngress
            action: aws:executeAwsApi
            onFailure: Continue
            inputs:
              Service: ec2
              Api: RevokeSecurityGroupIngress
              GroupId: "{{ GroupId }}"
              IpPermissions: "{{ DescribeSecurityGroups.IpPermissionsIngress }}"
          - name: RevokeSecurityGroupEgress
            action: aws:executeAwsApi
            onFailure: Continue
            inputs:
              Service: ec2
              Api: RevokeSecurityGroupEgress
              GroupId: "{{ GroupId }}"
              IpPermissions: "{{ DescribeSecurityGroups.IpPermissionsEgress }}"
        parameters:
          AutomationAssumeRole:
            type: String
            description: Automation Assume Role Arn
          GroupId:
            type: String
            description: Group Id
      DocumentType: Automation

4. AWS Config と SSM Automation の紐付け

Systems Manager Automation ドキュメントは、上述の通り IAM の RevokeSecurityGroupIngress などを実行する必要があるため、このAWS API アクションを Systems Manager Automation から呼び出すことを可能とする IAM Role を作成する。

Resources:
  IAMRoleForSSM:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: ssm.amazonaws.com
            Action: 'sts:AssumeRole'
      Description: A role required for SSM to access IAM.
      Policies:
        - PolicyName: !Sub '${PrefixOfLogicalName}-AWSSystemManagerIAMRole-${AWS::Region}'
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 'ec2:RevokeSecurityGroupIngress'
                  - 'ec2:RevokeSecurityGroupEgress'
                  - 'ec2:DescribeSecurityGroups'
                Resource:
                  - '*'
      RoleName: !Sub '${AWS::StackName}-SSM-${AWS::Region}'

このIAM RoleのARNは、AWS Config から Systems Manager Automation へ渡されるパラメータの1つとして規定される。AWS::Config::RemediationConfiguration は、非準拠(NON_COMPLIANT)と判定された場合の自動修復方法を規定し、Config Rule と Systems Manager Automation との紐付けや、受け渡されるパラメータの規定を行う。自動修復を行う場合は、AutomationAssumeRole, MaximumAutomaticAttempts, RetryAttemptSeconds の各パラメータの入力が必須である。

Resources:
  ConfigVpcDefaultSecurityGroupClosedRemediationConfiguration:
    Condition: CreateRemediationResources
    Type: 'AWS::Config::RemediationConfiguration'
    Properties:
      Automatic: true
      ConfigRuleName: !Ref ConfigVpcDefaultSecurityGroupClosed
      MaximumAutomaticAttempts: 1
      Parameters:
        AutomationAssumeRole:
          StaticValue:
            Values:
              - !GetAtt IAMRoleForSSM.Arn
        GroupId:
          ResourceValue:
            Value: RESOURCE_ID
      RetryAttemptSeconds: 30
      TargetId: !Ref SSMAutomationRevokeDefaultSecurityGroup
      TargetType: SSM_DOCUMENT

以上で、デフォルトセキュリティグループからインバウンドおよびアウトバンドの許可ルールを削除することができた。

AWS Config(5)外部からのSSHおよびRDPアクセスを制限する

セキュリティグループの自動修復

CIS AWS Foundations Benchmark というセキュリティガイドラインが公開されており、このガイドラインは、AWSアカウントをセキュアに保つために必要なAWSのセキュリティ設定を集めたベストプラクティス集として活用できる。

この CIS AWS Foundations Benchmarkでは、EC2/VPCのセキュリティグループについて、以下の設定とすることが奨励されている。

  • 4.1 どのセキュリティグループでも 0.0.0.0/0 からポート 22 への入力を許可しないようにします
  • 4.2 どのセキュリティグループでも 0.0.0.0/0 からポート 3389 への入力を許可しないようにします

この CIS AWS Foundations Benchmark に 準拠していないセキュリティグループ を SSM Automation を用いて 自動修復 するために以下の設定を行う。

  1. 上記のポリシーに準拠しているか AWS Config を用いて定期的にチェックを行う
  2. 非準拠であった場合には、AWS Config が SSM Automationを自動起動する
  3. SSM Automation が奨励されたセキュリティグループ設定となるように設定を自動修復する

1. AWS Configの有効化

AWS Configを有効化する手順については、こちら。

2. AWS Configを用いた定期チェック

セキュリティグループの設定のチェックには、あらかじめAWS Configに用意されている vpc-default-security-group-closed マネージドルールを使用する。セキュリティグループがこの条件を満たしていない場合、このリソースはルールに 非準拠(NON_COMPLIANT) であると判定される。

なお、この Config Rule を設定する前に ConfigurationRecorder を生成しておく必要がある。そこで、DependsOn 属性に ConfigurationRecorder リソースを設定している。

Resources:
  ConfigSVpcSgOpenOnlyToAuthorizedPorts:
    DependsOn:
      - ConfigConfigurationRecorder
    Type: 'AWS::Config::ConfigRule'
    Properties:
      ConfigRuleName: vpc-sg-open-only-to-authorized-ports
      Description: いずれかの 0.0.0.0/0 Amazon Virtual Private Cloud (Amazon VPC) を持つセキュリティグループで、特定のインバウンド TCP または UDP トラフィックのみが許可されるかどうかを確認します。
      InputParameters:
        authorizedTcpPorts: 1-21,23-3388,3390-65535
        authorizedUdpPorts: 1-21,23-3388,3390-65535
      Source:
        Owner: AWS
        SourceIdentifier: VPC_SG_OPEN_ONLY_TO_AUTHORIZED_PORTS 

3. SSM Automation を用いた自動修復

Systems Manager Automation は、AWS Config で 直接指定できる、現時点で唯一の自動修復手段 となっている。そこで、セキュリティグループ を修復する Systems Manager Automation ドキュメント と AWS Config との紐付けを行う。

修復には、あらかじめ用意されている AWS-DisablePublicAccessForSecurityGroup ドキュメントを使用する。このドキュメントは、すべての IP アドレスに対して開かれているデフォルトの SSH および RDP ポートを無効にする。

Resources:
  ConfigVpcSgOpenOnlyToAuthorizedPortsRemediationConfiguration:
    Condition: CreateRemediationResources
    Type: 'AWS::Config::RemediationConfiguration'
    Properties:
      # NOTE: AutomationAssumeRole, MaximumAutomaticAttempts and RetryAttemptSeconds are Required if Automatic is true.
      Automatic: true
      ConfigRuleName: !Ref ConfigSVpcSgOpenOnlyToAuthorizedPorts
      MaximumAutomaticAttempts: 1
      Parameters:
        AutomationAssumeRole:
          StaticValue:
            Values:
              - !GetAtt IAMRoleForSSM.Arn
        GroupId:
          ResourceValue:
            Value: RESOURCE_ID
      RetryAttemptSeconds: 30
      TargetId: AWS-DisablePublicAccessForSecurityGroup
      TargetType: SSM_DOCUMENT

以上で、すべての IP アドレスに対して開かれているデフォルトの SSH および RDP ポートを無効にすることができた。

AWS Config(4)VPCフローログの自動生成

VPCフローログ

CIS AWS Foundations Benchmark というセキュリティガイドラインが公開されており、このガイドラインは、AWSアカウントをセキュアに保つために必要なAWSのセキュリティ設定を集めたベストプラクティス集として活用できる。

この CIS AWS Foundations Benchmarkでは、VPCフローログについて以下のように定められている。

  • 2.9 すべての VPC で VPC フローログ記録が有効になっていることを確認します

この CIS AWS Foundations Benchmark に 準拠していないVPCが存在した場合 に SSM Automation を用いて VPCフローログを自動作成 するために以下の設定を行う。

  1. 上記のポリシーに準拠しているか AWS Config を用いて定期的にチェックを行う
  2. 非準拠であった場合には、AWS Config が SSM Automationを自動起動する
  3. SSM Automation がVPCフローログを自動作成する

1. AWS Configの有効化

AWS Configを有効化する手順については、こちら。

2. AWS Configを用いた定期チェック

VPCフローログの存在確認には、あらかじめAWS Configに用意されている vpc-flow-logs-enabled マネージドルールを使用する。VPCフローログが設定されていない場合、このVPCはルールに 非準拠(NON_COMPLIANT) であると判定される。

なお、この Config Rule を設定する前に ConfigurationRecorder を生成しておく必要がある。そこで、DependsOn 属性に ConfigurationRecorder リソースを設定している。

Resources:
  ConfigVpcFlowLogsEnabled:
    DependsOn:
      - ConfigConfigurationRecorder
    Type: 'AWS::Config::ConfigRule'
    Properties:
      ConfigRuleName: vpc-flow-logs-enabled
      Description: Amazon Virtual Private Cloud フローログが見つかり、Amazon VPC に対して有効になっているかどうかを確認します。
      Source:
        Owner: AWS
        SourceIdentifier: VPC_FLOW_LOGS_ENABLED 

3. SSM Automation を用いた自動修復

Systems Manager Automation は、AWS Config で 直接指定できる、現時点で唯一の自動修復手段 となっている。そこで、VPCフローログ を生成する Systems Manager Automation ドキュメント を作成し、AWS Config との紐付けを行う。

下のSystems Manager Automation ドキュメントは、EC2 の CreateFlowLogs を実行して、IAM パスワードポリシーの設定を自動修復する。

Resources:
  SSMAutomationCreateFlowLogs:
    Type: 'AWS::SSM::Document'
    Properties: 
      Content:
        schemaVersion: "0.3"
        assumeRole: "{{ AutomationAssumeRole }}"
        description: Create VPC flow logs.
        mainSteps:
          - name: CreateLogGroup
            action: aws:executeAwsApi
            inputs:
              Service: cloudwatch
              Api: CreateLogGroup
              logGroupName: "/aws/vpc/{{ ResourceId }}"
          - name: PutRetentionPolicy
            action: aws:executeAwsApi
            inputs:
              Service: cloudwatch
              Api: PutRetentionPolicy
              logGroupName: "/aws/vpc/{{ ResourceId }}"
              retentionInDays: 60
          - name: CreateFlowLogs
            action: aws:executeAwsApi
            inputs:
              Service: ec2
              Api: CreateFlowLogs
              DeliverLogsPermissionArn: "{{ DeliverLogsPermissionArn }}"
              LogDestinationType: cloud-watch-logs
              LogGroupName: "/aws/vpc/{{ ResourceId }}"
              ResourceId.N: "{{ ResourceId }}"
              ResourceType: VPC
              TrafficType: ALL
        parameters:
          AutomationAssumeRole:
            type: String
            description: Automation Assume Role Arn
          DeliverLogsPermissionArn:
            type: String
            description: Deliver Logs Permission Arn
          ResourceId:
            type: String
            description: Resource ID
      DocumentType: Automation
      Tags:
        - Key: !Ref TagKey
          Value: !Ref TagValue

4. AWS Config と SSM Automation の紐付け

Systems Manager Automation ドキュメントは、上述の通り EC2 の CreateFlowLogs などを実行する必要があるため、このAWS API アクションを Systems Manager Automation から呼び出すことを可能とする IAM Role を作成する。

また、VPCからCloudWatch Logsにフローログを書き込むことのできる権限を持つIAM Roleも別途作成しておく。

Resources:
  IAMRoleForSSM:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: ssm.amazonaws.com
            Action: 'sts:AssumeRole'
      Description: A role required for SSM to access IAM.
      Policies:
        - PolicyName: !Sub '${PrefixOfLogicalName}-AWSSystemManagerIAMRole-${AWS::Region}'
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 'logs:CreateLogGroup'
                  - 'logs:PutRetentionPolicy'
                  - 'ec2:CreateFlowLogs'
                Resource:
                  - '*'
      RoleName: !Sub '${AWS::StackName}-SSM-${AWS::Region}'
  IAMRoleForVPCFlowLog:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: vpc-flow-logs.amazonaws.com
            Action: 'sts:AssumeRole'
      Description: A role required for VPC flow logs to access CloudWatch Logs.
      Policies:
        - PolicyName: !Sub '${AWS::StackName}-AWSVPCFlowLogCloudWatchLogsPolicy-${AWS::Region}'
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 'logs:CreateLogGroup'
                  - 'logs:CreateLogStream'
                  - 'logs:PutLogEvents'
                  - 'logs:DescribeLogGroups'
                  - 'logs:DescribeLogStreams'
                Resource: '*'
      RoleName: !Sub '${AWS::StackName}-AWSVPCFlowLogRole-${AWS::Region}'

このIAM RoleのARNは、AWS Config から Systems Manager Automation へ渡されるパラメータの1つとして規定される。AWS::Config::RemediationConfiguration は、非準拠(NON_COMPLIANT)と判定された場合の自動修復方法を規定し、Config Rule と Systems Manager Automation との紐付けや、受け渡されるパラメータの規定を行う。自動修復を行う場合は、AutomationAssumeRole, MaximumAutomaticAttempts, RetryAttemptSeconds の各パラメータの入力が必須である。

Resources:
  ConfigVpcFlowLogsEnabledRemediationConfiguration:
    Condition: CreateRemediationResources
    Type: 'AWS::Config::RemediationConfiguration'
    Properties:
      Automatic: true
      ConfigRuleName: !Ref ConfigVpcFlowLogsEnabled
      MaximumAutomaticAttempts: 1
      Parameters:
        AutomationAssumeRole:
          StaticValue:
            Values:
              - !GetAtt IAMRoleForSSM.Arn
        DeliverLogsPermissionArn:
          StaticValue:
            Values:
              - !GetAtt IAMRoleForVPCFlowLog.Arn
        ResourceId:
          ResourceValue:
            Value: RESOURCE_ID
      RetryAttemptSeconds: 30
      TargetId: !Ref SSMAutomationCreateFlowLogs
      TargetType: SSM_DOCUMENT

以上で、CIS AWS Foundations Benchmark に準拠していないVPCに対して、VPCフローログを自動生成することができた。

AWS Config(3)アクセスキーの管理と自動削除

アクセスキーの適切な管理

CIS AWS Foundations Benchmark というセキュリティガイドラインが公開されており、このガイドラインは、AWSアカウントをセキュアに保つために必要なAWSのセキュリティ設定を集めたベストプラクティス集として活用できる。

この CIS AWS Foundations Benchmarkでは、アクセスキーの取り扱いについて以下のように定めている。

  • 1.3 90 日間以上使用されていない認証情報は無効にします
  • 1.4 アクセスキーは 90 日ごとに更新します

この CIS AWS Foundations Benchmark に 準拠していないアクセスキー を CloudWatch Events と Lambda を用いて 自動削除 するために以下の設定を行う。

  1. 上記のポリシーに準拠しているか AWS Config を用いて定期的にチェックを行う
  2. 非準拠であった場合には、CloudWatch Events が Lambdaを自動起動する
  3. Lambda が非準拠のアクセスキーを削除する

1. AWS Configの有効化

AWS Configを有効化する手順については、こちら。

2. AWS Configを用いた定期チェック

アクセスキーのチェックには、あらかじめAWS Configに用意されている access-keys-rotated マネージドルールを使用する。CIS AWS Foundations Benchmark で奨励されているアクセスキーの有効期限は、マネージドルール内の InputParameters で設定する。アクセスキーがこの条件を満たしていない場合、このリソースはルールに 非準拠(NON_COMPLIANT) であると判定される。

なお、この Config Rule を設定する前に ConfigurationRecorder を生成しておく必要がある。そこで、DependsOn 属性に ConfigurationRecorder リソースを設定している。

Resources:
  ConfigIamAccessKeysRotated:
    DependsOn:
      - ConfigConfigurationRecorder
    Type: 'AWS::Config::ConfigRule'
    Properties:
      ConfigRuleName: access-keys-rotated
      Description: アクティブなアクセスキーが、maxAccessKeyAge で指定された日数内にローテーションされるかどうかを確認します。
      InputParameters:
        maxAccessKeyAge: 90
      Source:
        Owner: AWS
        SourceIdentifier: ACCESS_KEYS_ROTATED

3. CloudWatch Events を用いた Lambda の自動実行

Configルールに 非準拠(NON_COMPLIANT)となった場合に、これを修復するLambdaを発火させるためのトリガとして、CloudWatch Events を設定する。

Resources:
  CloudWatchEventsForConfigIamAccessKeysRotated:
    Type: 'AWS::Events::Rule'
    Properties: 
      Description: CloudWatch Events about Config IAM Access Keys Rotated.
      EventPattern:
        source:
          - aws.config
        detail-type: 
          - Config Rules Compliance Change
        detail:
          messageType:
            - ComplianceChangeNotification
          newEvaluationResult:
            complianceType:
              - NON_COMPLIANT
      Name: AWS_Config
      State: ENABLED
      Targets:
        - Arn: !GetAtt LambdaDeleteExpiredAccessKeys.Arn
          Id: lambda

4. Lambda を用いた アクセスキー の削除

Lambdaが受け取るConfigから通知されたメッセージには、期限切れのアクセスキーを持つIAMユーザの情報が含まれる。そこでLambdaは、このユーザが持つアクセスキーのうち、有効期限を超過したアクセスキーのIDを探し当て、DeleteAccessKey API を用いてこれを削除する。

また、AWS::Lambda::Permission リソースタイプを用いて、前述のCloudWatch Eventsが、このLambdaを実行可能とする権限を設定する。

Resources:
  LambdaDeleteExpiredAccessKeys:
    Type: 'AWS::Lambda::Function'
    Properties:
      Code:
        ZipFile: |
          import boto3
          import datetime
          import time
          import logging

          logger = logging.getLogger()
          logger.setLevel(logging.INFO)

          def lambda_handler(event, context):
              logger.info(str(event))
            
              if 'detail' in event:
                  detail = event['detail']
                  if 'configRuleName' in detail:
                      # access-keys-rotated
                      if detail['configRuleName'] == 'access-keys-rotated':
                          iam = boto3.client('iam')
                          users = iam.list_users()
                          for user in users['Users']:
                              if detail['resourceId'] == user['UserId']:
                                  access_keys = iam.list_access_keys(
                                      UserName=user['UserName']
                                      )
                                  for access_key in access_keys['AccessKeyMetadata']:
                                      create_date = access_key['CreateDate'].timestamp()
                                      now = time.time()
                                      if now - create_date > 60*60*24*90:
                                          response = iam.delete_access_key(
                                              UserName=user['UserName'],
                                              AccessKeyId=access_key['AccessKeyId']
                                          )
      Description: 有効期限が過ぎたアクセスキーを削除します
      FunctionName: deleteExpiredAccessKeys
      Handler: index.lambda_handler
      MemorySize: 128
      Role: !GetAtt IAMRoleForLambda.Arn
      Runtime: python3.7
      Tags:
        - Key: !Ref TagKey
          Value: !Ref TagValue
      Timeout: 3
      TracingConfig:
        Mode: Active
  LambdaDeleteExpiredAccessKeysPermission:
    Type: 'AWS::Lambda::Permission'
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !Ref LambdaDeleteExpiredAccessKeys
      Principal: events.amazonaws.com
      # DO NOT write 'SourceAccount' option.
      SourceArn: !GetAtt CloudWatchEventsForConfigIamAccessKeysRotated.Arn

以上で、CIS AWS Foundations Benchmark に非準拠のアクセスキーを削除することができた。

AWS Config(2)パスワードポリシーの自動修復

IAMパスワードポリシーの自動修復

CIS AWS Foundations Benchmark というセキュリティガイドラインが公開されており、このガイドラインは、AWSアカウントをセキュアに保つために必要なAWSのセキュリティ設定を集めたベストプラクティス集として活用できる。

この CIS AWS Foundations Benchmarkでは、IAMユーザ用のパスワードポリシーについて、以下の設定とすることが奨励されている。

  • 1.5 IAM パスワードポリシーには少なくとも 1 つの大文字が必要です
  • 1.6 IAM パスワードポリシーには少なくとも 1 つの小文字が必要です
  • 1.7 IAM パスワードポリシーには少なくとも 1 つの記号が必要です
  • 1.8 IAM パスワードポリシーには少なくとも 1 つの数字が必要です
  • 1.9 IAM パスワードポリシーは 14 文字以上の長さが必要です
  • 1.10 IAM パスワードポリシーはパスワードの再使用を禁止しています

この CIS AWS Foundations Benchmark に 準拠していないIAMパスワードポリシー を SSM Automation を用いて 自動修復 するために以下の設定を行う。

  1. 上記のポリシーに準拠しているか AWS Config を用いて定期的にチェックを行う
  2. 非準拠であった場合には、AWS Config が SSM Automationを自動起動する
  3. SSM Automation が正しいIAMパスワードポリシーとなるように設定を自動修復する

1. AWS Configの有効化

AWS Configを有効化する手順については、こちら。

2. AWS Configを用いた定期チェック

IAM パスワードポリシー設定のチェックには、あらかじめAWS Configに用意されている iam-password-policy マネージドルールを使用する。CIS AWS Foundations Benchmark で奨励されているパスワードポリシーの値は、マネージドルール内の InputParameters で設定する。IAM パスワードポリシーがこの条件を満たしていなかった場合、このリソースはルールに 非準拠(NON_COMPLIANT) であると判定される。

なお、この Config Rule を設定する前に ConfigurationRecorder を生成しておく必要がある。そこで、DependsOn 属性に ConfigurationRecorder リソースを設定している。

Resources:
  ConfigIamPasswordPolicy:
    DependsOn:
      - ConfigConfigurationRecorder
    Type: AWS::Config::ConfigRule
    Properties:
      ConfigRuleName: iam-password-policy
      Description: IAM ユーザーのアカウントパスワードポリシーが、指定した要件を満たしているかどうかを確認します。
      InputParameters:
        RequireUppercaseCharacters: true
        RequireLowercaseCharacters: true
        RequireSymbols: true
        RequireNumbers: true
        MinimumPasswordLength: 14
        PasswordReusePrevention: 24
      Source:
        Owner: AWS
        SourceIdentifier: IAM_PASSWORD_POLICY

3. SSM Automation を用いた自動修復

Systems Manager Automation は、AWS Config で 直接指定できる、現時点で唯一の自動修復手段 となっている。そこで、IAM パスワードポリシー を修復する Systems Manager Automation ドキュメント を作成し、AWS Config との紐付けを行う。

下のSystems Manager Automation ドキュメントは、IAM の UpdateAccountPasswordPolicy を実行して、IAM パスワードポリシーの設定を自動修復する。

Resources:
  SSMAutomationUpdateAccountPasswordPolicy:
    Type: AWS::SSM::Document
    Properties: 
      Content:
        schemaVersion: "0.3"
        assumeRole: "{{ AutomationAssumeRole }}"
        description: Update account password policy.
        mainSteps:
          - name: UpdateAccountPasswordPolicy
            action: aws:executeAwsApi
            inputs:
              Service: iam
              Api: UpdateAccountPasswordPolicy
              AllowUsersToChangePassword: true
              HardExpiry: false
              MinimumPasswordLength: 14
              PasswordReusePrevention: 24
              RequireLowercaseCharacters: true
              RequireNumbers: true
              RequireSymbols: true
              RequireUppercaseCharacters: true
        parameters:
          AutomationAssumeRole:
            type: String
            description: Automation Assume Role Arn
      DocumentType: Automation

4. AWS Config と SSM Automation の紐付け

Systems Manager Automation ドキュメントは、上述の通り IAM の UpdateAccountPasswordPolicy を実行する必要があるため、このAWS API アクションを Systems Manager Automation から呼び出すことを可能とする IAM Role を作成する。

Resources:
  IAMRoleForSSM:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: ssm.amazonaws.com
            Action: 'sts:AssumeRole'
      Description: A role required for SSM to access IAM.
      Policies:
        - PolicyName: !Sub 'DefaultSecuritySettings-AWSSystemManagerIAMRole-${AWS::Region}'
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 'iam:UpdateAccountPasswordPolicy'
                Resource:
                  - '*'
      RoleName: !Sub 'DefaultSecuritySettings-SSM-${AWS::Region}'

このIAM RoleのARNは、AWS Config から Systems Manager Automation へ渡されるパラメータの1つとして規定される。AWS::Config::RemediationConfiguration は、非準拠(NON_COMPLIANT)と判定された場合の自動修復方法を規定し、Config Rule と Systems Manager Automation との紐付けや、受け渡されるパラメータの規定を行う。自動修復を行う場合は、AutomationAssumeRole, MaximumAutomaticAttempts, RetryAttemptSeconds の各パラメータの入力が必須である。

Resources:
  ConfigIamPasswordPolicyRemediationConfiguration:
    Type: AWS::Config::RemediationConfiguration
    Properties:
      Automatic: true
      ConfigRuleName: !Ref ConfigIamPasswordPolicy
      MaximumAutomaticAttempts: 1
      Parameters:
        AutomationAssumeRole:
          StaticValue:
            Values:
              - !GetAtt IAMRoleForSSM.Arn
      RetryAttemptSeconds: 60
      TargetId: !Ref SSMAutomationUpdateAccountPasswordPolicy
      TargetType: SSM_DOCUMENT

以上で、CIS AWS Foundations Benchmark に準拠したIAM パスワードポリシーへと、自動で修復することができた。