CodePipeLine × 別アカウント CodeCommit の超セキュア連携!AWS Codeシリーズの高度な設定方法を AWS サーバーレスエンジニアが解説😎

CodePipeLine × 別アカウント CodeCommit の超セキュア連携!AWS Codeシリーズの高度な設定方法を AWS サーバーレスエンジニアが解説😎

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは!

CodePipeLine を使用して CI.CD 構築を行う時、ソースコードのリポジトリにはいくつかの選択肢がありますが、 AWS 内に閉じて CodePipeLine を実行したい!なんてニーズが多いのではないと考えます。プロジェクトによっては GitHub や BitBucket 等々のサードパーティの使用に稟議が必要で、面倒くさいから CodeCommit で済ませたいなんてケースも有るかと思います。

ただ、AWS ベストプラクティスのマルチアカウント環境でこれを実現しようとすると、CodeCommit が別のアカウントに存在し、設定が難しいなんて声を多く聞きます。

本記事では、ちょっと複雑で実装の難しい 別アカウントの CodeCommit と CodePipeLine の連携方法を、ソースコード付きで AWS サーバーレスエンジニアが解説します!

想定する読者

  • CodePipeLine で別アカウントの CodeCommit と連携したヒト
  • CodePipeLine の動作概略を改めて学びたいヒト
  • CodePipeLine の一歩進んだ使い方を学びたいヒト

はじめに

本記事では、CodePipeLine の Source ステージのみの解説とし、CodeBuild や CodeDeploy の設定方法については触れません。

別アカウント CodeCommit 連携の動作概略

動作概略

上記の構成で、以下の点が重要です。

  • CodePipeLine の Source ステージの実行するロールに CodeCommit 側アカウント の IAM Role ARN を指定
  • CodeCommit 側アカウントへ CodePipeLine アカウント側 S3 Bucket ( Artifacts 用 ) と KMS ( Artifacts暗号化用 ) の操作をクロスアカウント認可

各要点に触れながら後述で解説いたしますが、クロスアカウントの連携の際には上記のような IAM Role のクロスアカウント連携が重要となります。

IAM Role は AssumeRole によって一時的な認可トークン ( STS トークン ) を発行可能で、AssumeRole を行う実行者を Principal と呼ばれるポリシーで制御できます。本件では、Principal に別アカウントの IAM Role を追加し、別アカウントからAssumeRole を可能にすることで、別アカウントのリソースへアクセス ( クロスアカウント制御 )を実現にします。

それでは連携方法をソースコード付きで解説していきます。

Artifacts 用の S3 Bucket と S3 Bucket Policy を作成

CodePipeLine 実行時に各ステージの Artifacts 格納用の S3 Bucket と S3 Bucket Policy 作成します。CodeCommit 側 AWS アカウントによる S3 バケットへの Artifacts の Put とGet/List を認可しています。

CodePipeLineArtifactsBucket:
  Type: 'AWS::S3::Bucket'
  Properties:
    BucketName: 'codepipeline-artifacts-bucket'
    AccessControl: Private
    VersioningConfiguration:
      Status: Enabled
    PublicAccessBlockConfiguration:
      BlockPublicAcls: true
      BlockPublicPolicy: true
      IgnorePublicAcls: true
      RestrictPublicBuckets: true

CodePipeLineArtifactsBucketPolicy:
  Type: 'AWS::S3::BucketPolicy'
  DependsOn: CodePipeLineArtifactsBucket
  Properties:
    Bucket: !Ref CodePipeLineArtifactsBucket
    PolicyDocument:
      Version: '2012-10-17'
      Statement:
        - Action:
            - s3:PutObject
          Effect: Deny
          Principal: '*'
          Resource:
            - 'arn:aws:s3:::codepipeline-artifacts-bucket/*'
            - 'arn:aws:s3:::codepipeline-artifacts-bucket'
          Condition:
            StringNotEquals:
              s3:x-amz-server-side-encryption: aws:kms
        - Action:
            - s3:*
          Effect: Deny
          Principal: '*'
          Resource:
            - 'arn:aws:s3:::codepipeline-artifacts-bucket/*'
            - 'arn:aws:s3:::codepipeline-artifacts-bucket'
          Condition:
            Bool:
              aws:SecureTransport: false
        - Action:
            - s3:Get*
            - s3:Put*
          Effect: Allow
          Principal:
            AWS:
              - arn:aws:iam::${CodeCommit側のAWSアカウントID}:root
              - ${CodeCommit側AWSアカウントのIAMRoleARN}
          Resource:
            - 'arn:aws:s3:::codepipeline-artifacts-bucket/*'
            - 'arn:aws:s3:::codepipeline-artifacts-bucket'
        - Action:
            - s3:ListBucket
          Effect: Allow
          Principal:
            AWS:
              - arn:aws:iam::${CodeCommit側のAWSアカウントID}:root
              - ${CodeCommit側AWSアカウントのIAMRoleARN}
          Resource:
            - 'arn:aws:s3:::codepipeline-artifacts-bucket/*'
            - 'arn:aws:s3:::codepipeline-artifacts-bucket'

Artifacts 暗号化用の KMS の作成

別アカウント CodeCommit から 自身のアカウントの S3 Bucket へ Artifacts を格納するため、暗号化は必須と言えます。暗号化しなくても実現自体は可能ですが、セキュリティを考えれば必須事項です。

CodePipelineProjectKms:
  Type: 'AWS::KMS::Key'
  Properties:
    Description: CMK for  codepipeline s3 artifacts
    KeyPolicy:
      Version: '2012-10-17'
      Id: key-default-
      Statement:
        - Sid: Enable IAM User Permissions
          Effect: Allow
          Principal:
            AWS:
              - !Sub arn:aws:iam::${AWS::AccountId}:root
          Action: kms:*
          Resource: '*'
        - Sid: Allow access for Key Administrators
          Effect: Allow
          Principal:
            AWS: 
              - ${KMSを管理する保守運用者のIAMユーザーARN}
          Action:
            - 'kms:Create*'
            - 'kms:Describe*'
            - 'kms:Enable*'
            - 'kms:List*'
            - 'kms:Put*'
            - 'kms:Update*'
            - 'kms:Revoke*'
            - 'kms:Disable*'
            - 'kms:Get*'
            - 'kms:Delete*'
            - 'kms:ScheduleKeyDeletion'
            - 'kms:CancelKeyDeletion'
          Resource: '*'
        - Sid: Allow use of the key
          Effect: Allow
          Principal:
            AWS:
              - !Sub arn:aws:iam::${CodeCommit側のAWSアカウントID}:root
              - !GetAtt CodeBuildServiceRole.Arn
              - !GetAtt CodePipelineServiceRole.Arn
              - ${CodeCommit側AWSアカウントのIAMRoleARN}
          Action:
            - kms:DescribeKey
            - kms:Encrypt
            - kms:Decrypt
            - kms:ReEncrypt*
            - kms:GenerateDataKey
            - kms:GenerateDataKeyWithoutPlaintext
          Resource: '*'
        - Sid: Allow attachment of persistent resources
          Effect: Allow
          Principal:
            AWS:
              - !Sub arn:aws:iam::${CodeCommit側のAWSアカウントID}:root
              - !GetAtt CodeBuildServiceRole.Arn
              - !GetAtt CodePipelineServiceRole.Arn
              - ${CodeCommit側AWSアカウントのIAMRoleARN}
          Action:
            - kms:CreateGrant
            - kms:ListGrants
            - kms:RevokeGrant
          Resource: '*'
          Condition:
            Bool:
              kms:GrantIsForAWSResource: true

CodePipelineProjectKmsAlias:
  Type: AWS::KMS::Alias
  Properties:
    AliasName: alias/CodePipelineArtifact
    TargetKeyId: !Ref CodePipelineProjectKms

CodePipeLine の作成

ではいよいよ、 CodePipeLine を作成します。 CodePipeLine の Source ステージでは、クロスアカウントの IAM Role を指定します。そうすることで、CodePipeLine の IAM Role がクロスアカウントのAssumeRole を実行します。

尚、前述の通り S3 Bucket のオブジェクトは暗号化し双方のアカウントでアクセスするため、Artifacts には先程作成した KMS を指定します。これにより、 CodeCommit の暗号化されたソースコードを、 CodePipeLine の IAM Role が復号化して Source として使用することが可能となります。

CodePipelineProject:
  Type: AWS::CodePipeline::Pipeline
  Properties:
    Name: CodePipeline
    RoleArn: !GetAtt CodePipelineServiceRole.Arn
    Stages:
      - Name: Source
        Actions:
          - Name: SourceAction
            InputArtifacts: []
            OutputArtifacts:
              - Name: SourceArtifact
            Configuration:
              RepositoryName: ${CodeCommitのリポジトリ名}'
              BranchName: '${CodeCommitのブランチ名}'
              PollForSourceChanges: false
            ActionTypeId:
              Category: Source
              Owner: AWS
              Provider: CodeCommit
              Version: '1'
            RoleArn: ${CodeCommit側AWSアカウントのIAMRoleARN} 
            Namespace: SourceVariables
            RunOrder: 1
    ArtifactStore:
      Type: S3
      Location: !Ref CodePipeLineArtifactsBucket
      EncryptionKey:
        Id: !GetAtt CodePipelineProjectKms.Arn
        Type: KMS

CodePipelineServiceRole:
  Type: AWS::IAM::Role
  Properties:
    RoleName: 'CodePipelineServiceRole'
    Path: /
    AssumeRolePolicyDocument:
      Version: '2012-10-17'
      Statement:
        - Effect: Allow
          Principal:
            Service:
              - codepipeline.amazonaws.com
          Action:
            - sts:AssumeRole
    MaxSessionDuration: 3600
    Policies:
      - PolicyName: 'CodePipelineServiceRolePolicy'
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - sts:AssumeRole
              Resource: ${CodeCommit側AWSアカウントのIAMRoleARN}
      # Any Polices...

CodeCommit 側の IAM Role 及び CodeCommit 作成

CodePipeLine の AssumeRole を許可します。これにより、CodePipeLine 側のアカウントから CodeCommit が参照可能となります。

Resources:
  Repository:
    Type: AWS::CodeCommit::Repository
    Properties:
      RepositoryName: HogeFugaRepository
  Role:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              AWS:
              - arn:aws:iam::${CodePipeLine側のAWSアカウントID}:root
              - ${CodePipeLine側のCodePipelineServiceRole ARN}
            Action:
              - sts:AssumeRole
      Path: /
      Policies:
        - PolicyName: source
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - s3:PutObject
                  - s3:PutObjectAcl
                Resource:
                  - !Sub ${CodePipeLine側のArtifacts用S3BucketARN}/*
              - Effect: Allow
                Action:
                  - kms:DescribeKey
                  - kms:GenerateDataKey*
                  - kms:Encrypt
                  - kms:ReEncrypt*
                  - kms:Decrypt
                Resource:
                  - ${CodePipeLine側のKMS ARN}
              - Effect: Allow
                Action:
                  - codecommit:GetBranch
                  - codecommit:GetCommit
                  - codecommit:UploadArchive
                  - codecommit:GetUploadArchiveStatus
                  - codecommit:CancelUploadArchive
                Resource:
                  - !GetAtt Repository.Arn

まとめ

やりたいことは単純ですが、AWS 準拠のセキュリティに則って作ろうと意外と本記事の通り手順が多い印象です。ただ本記事の内容を実現することで、リポジトリの設営にサードパーティの用意が不要となり、面倒な社内稟議や、場合によってはサードパーティへの高いランニングコストが不要となります。

CodePipeLine を作っているヒトは、是非取り組んでみてください!

AWS サーバーレスな開発はお気軽にご相談ください。