🐈

[備忘録]個人用AWSアカウントを作成したときにやったこと

に公開

大半はこちらこちらを参考にさせてもらった。
その上で、CFnで行ったことや他に取り入れたことについて残す。

Amazon SNSのトピック作成

AWS BudgetsとAmazon GuardDutyが通知に使用するSNSトピックを作成した。

  Topic:
    Type: AWS::SNS::Topic
    Properties:
      DisplayName: aws-notification
      Subscription:
        - Endpoint: <YOUR_MAIL_ADDRESS>
          Protocol: email
      TopicName: <YOUR_TOPIC_NAME>

  TopicPolicy:
    Type: AWS::SNS::TopicPolicy
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Sid: "budgets"
            Effect: "Allow"
            Principal:
              Service: !Sub "budgets.${AWS::URLSuffix}"
            Action:
              - "sns:Publish"
            Resource:
              - !Ref Topic
          - Sid: "events"
            Effect: "Allow"
            Principal:
              Service: !Sub "events.${AWS::URLSuffix}"
            Action:
              - "sns:Publish"
            Resource:
              - !Ref Topic
      Topics:
        - !Ref Topic

AWS Budgetsの設定

予算(BudgetLimit)に対して、

  • 実際の支出(NotificationType: ACTUAL)が70%が超えた時
  • 実際の支出(NotificationType: ACTUAL)が100%が超えた時
  • 支出の予想(NotificationType: FORECASTED)が100%が超えた時

上記3パターンで通知を設定した。

  Budget:
    Type: AWS::Budgets::Budget
    Properties:
      Budget:
        BudgetLimit:
          Unit: USD
          Amount: <YOUR_MAXIMUM_BUDGET>
        BudgetName: monthly
        BudgetType: COST
        TimeUnit: MONTHLY
      NotificationsWithSubscribers:
        - Notification:
            ComparisonOperator: GREATER_THAN
            NotificationType: ACTUAL
            Threshold: 70
            ThresholdType: PERCENTAGE
          Subscribers:
            - Address: !Ref Topic
              SubscriptionType: SNS
        - Notification:
            ComparisonOperator: GREATER_THAN
            NotificationType: ACTUAL
            Threshold: 100
            ThresholdType: PERCENTAGE
          Subscribers:
            - Address: !Ref Topic
              SubscriptionType: SNS
        - Notification:
            ComparisonOperator: GREATER_THAN
            NotificationType: FORECASTED
            Threshold: 100
            ThresholdType: PERCENTAGE
          Subscribers:
            - Address: !Ref Topic
              SubscriptionType: SNS

Amazon GuardDutyの設定

脅威が検知された(GuardDuty Finding)時にSNSで通知するよう設定した。
EKSは現状使用するつもりが無いため、丸っと無効化した。

  GuardDuty:
    Type: AWS::GuardDuty::Detector
    Properties:
      Enable: true
      Features:
        - Name: EBS_MALWARE_PROTECTION
          Status: ENABLED
        - Name: EKS_AUDIT_LOGS
          Status: DISABLED
        - Name: LAMBDA_NETWORK_LOGS
          Status: ENABLED
        - Name: RDS_LOGIN_EVENTS
          Status: DISABLED
        - Name: RUNTIME_MONITORING
          Status: DISABLED
        - Name: S3_DATA_EVENTS
          Status: ENABLED
      FindingPublishingFrequency: SIX_HOURS

  GuardDutyFindingEventRule:
    Type: AWS::Events::Rule
    Properties:
      EventPattern:
        source:
          - aws.guardduty
        detail-type:
          - GuardDuty Finding
      Name: GuardDuty-Finding-Event
      Targets:
        - Arn: !Ref Topic
          Id: SNS

AWS CloudTrailの設定

保存用のS3バケットは特に何も考えずにサーバーサイド暗号化(ServerSideEncryptionConfiguration)して、保存期間は1年(ExpirationInDays: 365)にしておいた。
※保存にかかる費用があまりかからないようであれば見直す。
また、証跡になるものなので、オブジェクトロックを有効化(ObjectLockEnabled: true)しておいた。

バケットポリシーに関してはこちらを参考にした。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AWSCloudTrailAclCheck20150319",
            "Effect": "Allow",
            "Principal": {"Service": "cloudtrail.amazonaws.com"},
            "Action": "s3:GetBucketAcl",
            "Resource": "arn:aws:s3:::amzn-s3-demo-bucket",
            "Condition": {
                "StringEquals": {
                    "aws:SourceArn": "arn:aws:cloudtrail:region:myAccountID:trail/trailName"
                }
            }
        },
        {
            "Sid": "AWSCloudTrailWrite20150319",
            "Effect": "Allow",
            "Principal": {"Service": "cloudtrail.amazonaws.com"},
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::amzn-s3-demo-bucket/[optionalPrefix]/AWSLogs/myAccountID/*",
            "Condition": {
                "StringEquals": {
                    "s3:x-amz-acl": "bucket-owner-full-control",
                    "aws:SourceArn": "arn:aws:cloudtrail:region:myAccountID:trail/trailName"
                }
            }
        }
    ]
}

念のためオブジェクトや設定の変更権限はDenyしておいたが、オブジェクトロックはコンプライアンスモードなので不要だったかもしれない。

また、aws:SourceArnを決め打ちにしたかったが、

  • 先にAWS::CloudTrail::Trailを作成する→バケットポリシーで許可が無いため作成に失敗する。
  • 先にAWS::S3::BucketPolicyを作成する→AWS::CloudTrail::Trailの作成がまだ終わっていないため、参照出来ない。

というジレンマがあり、妥協してStringLikeとワイルカードの併用に落ち着いた。

  TrailStorageBucket:
    DeletionPolicy: Retain
    UpdateReplacePolicy: Retain
    Type: AWS::S3::Bucket
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      BucketName: <YOUR_BUCKET_NAME>
      LifecycleConfiguration:
        Rules:
          - ExpirationInDays: 365
            NoncurrentVersionExpiration:
              NoncurrentDays: 365
            Status: Enabled
      ObjectLockConfiguration:
        ObjectLockEnabled: Enabled
        Rule:
          DefaultRetention:
            Days: 365
            Mode: COMPLIANCE
      ObjectLockEnabled: true
      VersioningConfiguration:
        Status: Enabled

  BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref TrailStorageBucket
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Deny"
            Principal:
              AWS: "*"
            Action:
              - "s3:DeleteBucket"
              - "s3:DeleteBucketPolicy"
              - "s3:PutBucketObjectLockConfiguration"
              - "s3:PutBucketPolicy"
              - "s3:PutEncryptionConfiguration"
              - "s3:PutLifecycleConfiguration"
            Resource:
              - !GetAtt TrailStorageBucket.Arn
          - Effect: "Deny"
            Principal:
              AWS: "*"
            Action:
              - "s3:DeleteObject"
              - "s3:DeleteObjectVersion"
            Resource:
              - !Sub "${TrailStorageBucket.Arn}/AWSLogs/${AWS::AccountId}/*"
          - Effect: "Allow"
            Principal:
              Service: !Sub "cloudtrail.${AWS::URLSuffix}"
            Action:
              - "s3:GetBucketAcl"
            Resource:
              - !GetAtt TrailStorageBucket.Arn
            Condition:
              StringLike:
                aws:SourceArn: !Sub "arn:${AWS::Partition}:cloudtrail:${AWS::Region}:${AWS::AccountId}:trail/*"
          - Effect: "Allow"
            Principal:
              Service: !Sub "cloudtrail.${AWS::URLSuffix}"
            Action:
              - "s3:PutObject"
            Resource:
              - !Sub "${TrailStorageBucket.Arn}/AWSLogs/${AWS::AccountId}/*"
            Condition:
              StringLike:
                s3:x-amz-acl: "bucket-owner-full-control"
                aws:SourceArn: !Sub "arn:${AWS::Partition}:cloudtrail:${AWS::Region}:${AWS::AccountId}:trail/*"

  CloudTrail:
    Type: AWS::CloudTrail::Trail
    Properties:
      IncludeGlobalServiceEvents: true
      IsLogging: true
      IsMultiRegionTrail: true
      S3BucketName: !Ref TrailStorageBucket
      TrailName: default
    DependsOn: BucketPolicy

Permissions Boundary用のIAMポリシーの作成

これまでに作成したAWSアカウントを保護するためのリソースに対して、一切の変更・削除を出来ないようにする。
また、他のIAMユーザーやIAMロールを使用して自身の権限をエスカレーション出来ないようにもする。
sts:AssumeRoleについても制限が必要そうなので調査中

権限の許可は通常のIAMポリシーとPermissions Boundary用ポリシーの両方で許可されている必要があるため、本当に拒否したい事以外は許可するようにした。
※その他の細かい制御は通常のIAMポリシーで行う

  AccountProtectionPermissionsBoundaryPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: AccountProtectionPermissionsBoundary
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          # ----- これまで作成したリソースの変更を禁止 -----
          - Effect: "Deny"
            Action:
              - "sns:Unsubscribe"
            Resource: "*"
            Condition:
              StringEquals:
                aws:ResourceTag/aws:cloudformation:stack-id: !Ref AWS::StackId
          - Effect: "Deny"
            Action:
              - "sns:DeleteTopic"
              - "sns:RemovePermission"
              - "sns:SetTopicAttributes"
            Resource:
              - !Ref Topic
          - Effect: "Deny"
            Action:
              - "guardduty:DeleteDetector"
              - "guardduty:UpdateDetector"
            Resource:
              - !Sub "arn:${AWS::Partition}:guardduty:${AWS::Region}:${AWS::AccountId}:detector/${GuardDuty}"
          - Effect: "Deny"
            Action:
              - "events:DeleteRule"
              - "events:DisableRule"
              - "events:PutRule"
              - "events:RemoveTargets"
            Resource:
              - !GetAtt GuardDutyFindingEventRule.Arn
          - Effect: "Deny"
            Action:
              - "cloudtrail:DeleteTrail"
              - "cloudtrail:StopLogging"
              - "cloudtrail:UpdateTrail"
            Resource:
              - !GetAtt CloudTrail.Arn
          - Effect: "Deny"
            Action:
              - "iam:CreatePolicyVersion"
              - "iam:DeletePolicy"
              - "iam:DeletePolicyVersion"
              - "iam:SetDefaultPolicyVersion"
            Resource:
              - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/AccountProtectionPermissionsBoundary"
          # ----- このPermissions Boundary用ポリシーの強制 -----
          - Effect: "Deny"
            Action:
              - "iam:CreateUser"
              - "iam:DeleteUserPolicy"
              - "iam:PutUserPermissionsBoundary"
              - "iam:PutUserPolicy"
              - "iam:UpdateUser"
            Resource: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:user/*"
            Condition:
              ArnNotEquals:
                iam:PermissionsBoundary: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/AccountProtectionPermissionsBoundary"
          - Effect: "Deny"
            Action:
              - "iam:DeleteUserPermissionsBoundary"
            Resource: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:user/*"
            Condition:
              ArnEquals:
                iam:PermissionsBoundary: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/AccountProtectionPermissionsBoundary"
          - Effect: "Deny"
            Action:
              - "iam:CreateRole"
              - "iam:DeleteRolePolicy"
              - "iam:PutRolePermissionsBoundary"
              - "iam:PutRolePolicy"
              - "iam:UpdateRole"
            Resource: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/*"
            Condition:
              ArnNotEquals:
                iam:PermissionsBoundary: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/AccountProtectionPermissionsBoundary"
          - Effect: "Deny"
            Action:
              - "iam:DeleteRolePermissionsBoundary"
            Resource: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/*"
            Condition:
              ArnEquals:
                iam:PermissionsBoundary: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/AccountProtectionPermissionsBoundary"
          # ----- このスタックへの操作を禁止 -----
          - Effect: "Deny"
            Action:
              - "cloudformation:DeleteStack"
              - "cloudformation:UpdateStack"
            Resource:
              - !Ref AWS::StackId
          # ----- 本当に禁止にしたいこと以外は許可 -----
          - Effect: "Allow"
            Action: "*"
            Resource: "*"

その他

MFA強制用のIAMポリシー

こちらを参考に設定した。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowListActions",
            "Effect": "Allow",
            "Action": [
                "iam:ListUsers",
                "iam:ListVirtualMFADevices"
            ],
            "Resource": "*"
        },
        {
            "Sid": "AllowUserToCreateVirtualMFADevice",
            "Effect": "Allow",
            "Action": [
                "iam:CreateVirtualMFADevice"
            ],
            "Resource": "arn:aws:iam::*:mfa/*"
        },
        {
            "Sid": "AllowUserToManageTheirOwnMFA",
            "Effect": "Allow",
            "Action": [
                "iam:EnableMFADevice",
                "iam:GetMFADevice",
                "iam:ListMFADevices",
                "iam:ResyncMFADevice"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Sid": "AllowUserToDeactivateTheirOwnMFAOnlyWhenUsingMFA",
            "Effect": "Allow",
            "Action": [
                "iam:DeactivateMFADevice"
            ],
            "Resource": [
                "arn:aws:iam::*:user/${aws:username}"
            ],
            "Condition": {
                "Bool": {
                    "aws:MultiFactorAuthPresent": "true"
                }
            }
        },
        {
            "Sid": "BlockMostAccessUnlessSignedInWithMFA",
            "Effect": "Deny",
            "NotAction": [
                "iam:CreateVirtualMFADevice",
                "iam:EnableMFADevice",
                "iam:ListMFADevices",
                "iam:ListUsers",
                "iam:ListVirtualMFADevices",
                "iam:ResyncMFADevice"
            ],
            "Resource": "*",
            "Condition": {
                "BoolIfExists": {
                    "aws:MultiFactorAuthPresent": "false"
                }
            }
        }
    ]
}

付け加えた点として、MFAが有効でないときの許可(NotActionで設定している部分)にiam:ChangePasswordiam:GetAccountPasswordPolicyを追加した。
そうしないと、初回ログイン時のパスワード変更が失敗してしまう。

  MultiFactorAuthEnforcementPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: MultiFactorAuthEnforcementPolicy
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Deny"
            NotAction:
              - "iam:ChangePassword"
              - "iam:CreateVirtualMFADevice"
              - "iam:EnableMFADevice"
              - "iam:GetAccountPasswordPolicy"
              - "iam:ListMFADevices"
              - "iam:ListUsers"
              - "iam:ListVirtualMFADevices"
              - "iam:ResyncMFADevice"
            Resource: "*"
            Condition:
              BoolIfExists:
                aws:MultiFactorAuthPresent: "false"
          - Effect: "Allow"
            Action:
              - "iam:ListUsers"
              - "iam:ListVirtualMFADevices"
            Resource: "*"
          - Effect: "Allow"
            Action:
              - "iam:CreateVirtualMFADevice"
            Resource: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:mfa/*"
          - Effect: "Allow"
            Action:
              - "iam:EnableMFADevice"
              - "iam:GetMFADevice"
              - "iam:ListMFADevices"
              - "iam:ResyncMFADevice"
            Resource: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:user/${!aws:username}"
          - Effect: "Allow"
            Action:
              - "iam:DeactivateMFADevice"
            Resource: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:user/${!aws:username}"
            Condition:
              Bool:
                aws:MultiFactorAuthPresent: "true"

パスワード変更及びアクセスキー発行用のポリシー

こちらこちらを参考に、自身のパスワードやアクセスキーなどの認証情報を自由にさわれるようにした。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iam:GetAccountPasswordPolicy",
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": "iam:ChangePassword",
      "Resource": "arn:aws:iam::*:user/${aws:username}"
    }
  ]
}
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "CreateOwnAccessKeys",
            "Effect": "Allow",
            "Action": [
                "iam:CreateAccessKey",
                "iam:GetUser",
                "iam:ListAccessKeys",
                "iam:TagUser"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        }
    ]
}
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "ManageOwnAccessKeys",
            "Effect": "Allow",
            "Action": [
                "iam:CreateAccessKey",
                "iam:DeleteAccessKey",
                "iam:GetAccessKeyLastUsed",
                "iam:GetUser",
                "iam:ListAccessKeys",
                "iam:UpdateAccessKey",
                "iam:TagUser"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        }
    ]
}
  OwnCredentialsAccessPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: OwnCredentialsAccessPolicy
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Action: "iam:GetAccountPasswordPolicy"
            Resource: "*"
          - Effect: "Allow"
            Action: "iam:ChangePassword"
            Resource: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:user/${!aws:username}"
          - Effect: "Allow"
            Action:
              - "iam:CreateAccessKey"
              - "iam:DeleteAccessKey"
              - "iam:GetAccessKeyLastUsed"
              - "iam:GetUser"
              - "iam:ListAccessKeys"
              - "iam:UpdateAccessKey"
              - "iam:TagUser"
            Resource: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:user/${!aws:username}"

AWSアカウントのエイリアス設定

  AccountAlias:
    Type: AWS::SupportApp::AccountAlias
    Properties:
      AccountAlias: <YOUR_ACCOUNT_ALIAS>

Discussion