TECHSTEP

ITインフラ関連の記事を公開してます。

【メモ】AWS CodeCommitのブランチへの操作制限と承認ルールテンプレートの作り方

ソースコードを管理するリポジトリを本格的に運用しようと思うと、リポジトリへの操作に対して制限をかける必要が出てきます。AWS CodeCommitはデフォルトに提供する機能に限りがあり、リポジトリに対する制限をかけるためにいろいろと設定が必要になります。

今回はAWS CodeCommitに対する操作制限をかける方法について調査しました。

ブランチへの操作制限

ブランチへの操作制限は、専用のIAMポリシーを作成し、それをIAMユーザー・グループに紐づけることで実現できます。制限できる内容としては、特定ブランチに対する git push や削除、マージの手法などです。

※参考

今回はシンプルに main ブランチへの直接Pushが実行できないようIAMポリシーを作成し、それを testgroup というIAMグループに付与することで、グループ内のIAMユーザーに操作制限を加えました。

IAMポリシーはjsonで書かれた例が多く見つかりましたが、今回はCloudFormationからYaml形式で書いた例を載せておきます。以下のようなYamlファイルを使ってリソースを作成しました。

Resources:
  CodeCommit:
    Type: AWS::CodeCommit::Repository
    Properties:
      RepositoryName: !Ref RepoName
      RepositoryDescription: test repo
  IAMPolicy:
    Type: 'AWS::IAM::ManagedPolicy'
    Properties: 
      Description: prohibit direct push to codecommit
      Path: /
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Deny
            Action: 'codecommit:GitPush'
            Resource: '*'
            Condition:
              "StringEqualsIfExists":
                "codecommit:References": 
                  - "refs/heads/main"
              "Null":
                "codecommit:References": 
                  - "false"
  IAMUser:
    Type: AWS::IAM::User
    Properties:
      Groups:
        - !Ref IAMGroup
      UserName: !Ref UserName
      LoginProfile:
        Password: !Ref UserPassword
        PasswordResetRequired: "false"
  IAMGroup:
    Type: AWS::IAM::Group
    Properties:
      GroupName: !Ref GroupName
      ManagedPolicyArns:
        - !Ref IAMPolicy
        - "arn:aws:iam::aws:policy/AWSCodeCommitPowerUser"

※参考

承認ルールテンプレート

承認ルールテンプレートはCodeCommitの機能として備わっており、マネジメントコンソールから作成・管理することができます。

また承認ルールテンプレートの作成は、公式には提供されていないものの、CloudFormationの拡張機能を利用し、コミュニティの公開するパッケージを利用することで、CloudFormationテンプレートからの作成が可能になります。

今回はあまり紹介する例の見当たらなかった、CloudFormationのほうで作ってみました。登録するリソースタイプは Community::CodeCommit::ApprovalRuleTemplate Community::CodeCommit::RepositoryAssociation の2つです。

まずCloudFormationを利用する準備をするため、実行用のロールの作成とCloudFormationタイプの登録を行います。

# IAMロールの作成
$ aws cloudformation create-stack \
   --template-url https://community-resource-provider-catalog.s3.amazonaws.com/community-codecommit-approvalruletemplate-resource-role-0.1.0.yml \
   --stack-name community-codecommit-approvalruletemplate-resource-role \
   --capabilities CAPABILITY_IAM

$ aws cloudformation create-stack \
   --template-url https://community-resource-provider-catalog.s3.amazonaws.com/community-codecommit-repositoryassociation-resource-role-0.1.0.yml \
  --stack-name community-codecommit-repositoryassociation-resource-role \
   --capabilities CAPABILITY_IAM



# CloudFormationタイプの登録
$ aws cloudformation register-type \
   --region "ap-northeast-1" \
   --type-name "Community::CodeCommit::ApprovalRuleTemplate" \
   --schema-handler-package "s3://community-resource-provider-catalog/community-codecommit-approvalruletemplate-0.1.0.zip" \
   --type RESOURCE \
   --execution-role-arn <ApprovalRuleTemplate用のロールARNを指定>

$ aws cloudformation register-type \
   --region "ap-northeast-1" \
   --type-name "Community::CodeCommit::RepositoryAssociation" \
   --schema-handler-package "s3://community-resource-provider-catalog/community-codecommit-repositoryassociation-0.1.0.zip" \
   --type RESOURCE \
   --execution-role-arn <RepositoryAssociation用のロールARNを指定>

タイプ登録後は以下のようにコマンドを実行し、登録の進行状況を確認できます。 aws cloudformation register-type コマンド実行後に RegistrationTokenという出力があるので、そこに表示されたトークンを指定します。

$ aws cloudformation describe-type-registration --registration-token <RegistrationToken>
{
    "ProgressStatus": "IN_PROGRESS",
    "Description": "Deployment is currently in DEPLOY_STAGE of status IN_PROGRESS; ",
    "TypeArn": "arn:aws:cloudformation:ap-northeast-1:111111111111:type/resource/Community-CodeCommit-ApprovalRuleTemplate",
    "TypeVersionArn": "arn:aws:cloudformation:ap-northeast-1:111111111111:type/resource/Community-CodeCommit-ApprovalRuleTemplate/00000005"
}
$ aws cloudformation describe-type-registration --registration-token <RegistrationToken>
{
    "ProgressStatus": "COMPLETE",
    "Description": "Deployment is currently in DEPLOY_STAGE of status COMPLETED; ",
    "TypeArn": "arn:aws:cloudformation:ap-northeast-1:111111111111:type/resource/Community-CodeCommit-ApprovalRuleTemplate",
    "TypeVersionArn": "arn:aws:cloudformation:ap-northeast-1:111111111111:type/resource/Community-CodeCommit-ApprovalRuleTemplate/00000005"
}

なお、CloudFormationタイプはバージョン情報を含んでおり、何度か登録を繰り返すとバージョンが更新されます。ただしデフォルトで利用するタイプを明示的に変更しないと、CloudFormationからリソースを作成する際に使われるバージョンが古いものになるため、バージョンの変更が必要な場合は、以下のようにバージョンの確認と更新を行います。

# バージョン一覧の取得
$ aws cloudformation list-type-versions --type RESOURCE --type-name "Community::CodeCommit::RepositoryAssociation"
{
    "TypeVersionSummaries": [
        {
            "Type": "RESOURCE",
            "TypeName": "Community::CodeCommit::RepositoryAssociation",
            "VersionId": "00000001",
            "IsDefaultVersion": true,
            "Arn": "arn:aws:cloudformation:ap-northeast-1:111111111111:type/resource/Community-CodeCommit-RepositoryAssociation/00000001",
            "TimeCreated": "2022-01-06T15:02:17.280000+00:00",
            "Description": "Resource that allows for the association of a particular approval rule template to CodeCommit repositories."
        },

(中略)

        {
            "Type": "RESOURCE",
            "TypeName": "Community::CodeCommit::RepositoryAssociation",
            "VersionId": "00000005",
            "IsDefaultVersion": false,
            "Arn": "arn:aws:cloudformation:ap-northeast-1:111111111111:type/resource/Community-CodeCommit-RepositoryAssociation/00000005",
            "TimeCreated": "2022-01-07T13:27:06.992000+00:00",
            "Description": "Resource that allows for the association of a particular approval rule template to CodeCommit repositories."
        }
    ]
}

# デフォルトバージョンの指定
$ aws cloudformation set-type-default-version \
   --version-id 00000005 \
   --type-name Community::CodeCommit::RepositoryAssociation \
   --type RESOURCE

これで承認ルールテンプレートをCloudFormationから作成できるようになったので、以下のようなファイルを使って作成します。

  RuleTemplate:
    Type: Community::CodeCommit::ApprovalRuleTemplate
    Properties:
      Name: !Ref RuleTemplateName
      Description: test rule
      Content:
        Version: "2018-11-08"
        DestinationReferences:
          - "refs/heads/main"
        Statements:
          - Type: "Approvers"
            NumberOfApprovalsNeeded: 1
            ApprovalPoolMembers:
              - "*"
  RepoAssociation:
    Type: Community::CodeCommit::RepositoryAssociation
    Properties:
      ApprovalRuleTemplateArn: !Ref RuleTemplate
      RepositoryNames: 
        - !GetAtt CodeCommit.Name

※参考

実際の利用

ここまでで必要なリソースの作成が完了したので、作成した testuser というIAMユーザーからCodeCommitへの操作を行います。

※リソースの作成に利用したYamlファイルはこちら。

sample-file.yaml

AWSTemplateFormatVersion: '2010-09-09'
Description: codecommit

Parameters:
  RepoName:
    Type: String
    Default: "testrepo"
  UserName:
    Type: String
    Default: "testuser"
  UserPassword:
    Type: String
    Default: "testuser@1234"
  GroupName:
    Type: String
    Default: "testgroup"
  RuleTemplateName:
    Type: String
    Default: "testrule"
  
Resources:
  CodeCommit:
    Type: AWS::CodeCommit::Repository
    Properties:
      RepositoryName: !Ref RepoName
      RepositoryDescription: test repo
  IAMPolicy:
    Type: 'AWS::IAM::ManagedPolicy'
    Properties: 
      Description: prohibit direct push to codecommit
      Path: /
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Deny
            Action: 'codecommit:GitPush'
            Resource: '*'
            Condition:
              "StringEqualsIfExists":
                "codecommit:References": 
                  - "refs/heads/main"
              "Null":
                "codecommit:References": 
                  - "false"
  IAMUser:
    Type: AWS::IAM::User
    Properties:
      Groups:
        - !Ref IAMGroup
      UserName: !Ref UserName
      LoginProfile:
        Password: !Ref UserPassword
        PasswordResetRequired: "false"
  IAMGroup:
    Type: AWS::IAM::Group
    Properties:
      GroupName: !Ref GroupName
      ManagedPolicyArns:
        - !Ref IAMPolicy
        - "arn:aws:iam::aws:policy/AWSCodeCommitPowerUser"
  RuleTemplate:
    Type: Community::CodeCommit::ApprovalRuleTemplate
    Properties:
      Name: !Ref RuleTemplateName
      Description: test rule
      Content:
        Version: "2018-11-08"
        DestinationReferences:
          - "refs/heads/main"
        Statements:
          - Type: "Approvers"
            NumberOfApprovalsNeeded: 1
            ApprovalPoolMembers:
              - "*"
  RepoAssociation:
    Type: Community::CodeCommit::RepositoryAssociation
    Properties:
      ApprovalRuleTemplateArn: !Ref RuleTemplate
      RepositoryNames: 
        - !GetAtt CodeCommit.Name

まずはIAMユーザー画面に移動し、CodeCommitへのアクセス情報を取得します。次にCodeCommitで利用するファイルを適当に用意します。

f:id:FY0323:20220109165300p:plain

ローカルの環境から testuser を使ってCodeCommitリポジトリの取得を行います。

$ git clone https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/testrepo
Cloning into 'testrepo'...
Username for 'https://git-codecommit.ap-northeast-1.amazonaws.com': testuser-at-111111111111
Password for 'https://testuser-at-111111111111@git-codecommit.ap-northeast-1.amazonaws.com':
remote: Counting objects: 3, done.
Unpacking objects: 100% (3/3), done.

$ cd testrepo/
$ git branch
* main

ここで README.md を適当に編集し、 main ブランチへ直接Pushをしてみます。するとエラーが発生し、 main への直接Pushができないことが確認できます。

$ vi README.md
$ cat README.md 
# testrepo

20220108

$ git add .
$ git commit -m "initial commit"
[main 9390f5b] initial commit
 1 file changed, 3 insertions(+), 1 deletion(-)
$ git push origin main
Username for 'https://git-codecommit.ap-northeast-1.amazonaws.com': testuser-at-111111111111
Password for 'https://testuser-at-111111111111@git-codecommit.ap-northeast-1.amazonaws.com': 
Counting objects: 3, done.
Writing objects: 100% (3/3), 262 bytes | 131.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/testrepo
 ! [remote rejected] main -> main (You don't have permission to push changes to this branch.)
error: failed to push some refs to 'https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/testrepo'

ここでは test-branch というブランチから、変更をコミットします。

$ git checkout -b test-branch
Switched to a new branch 'test-branch'
$ git push origin test-branch 
Username for 'https://git-codecommit.ap-northeast-1.amazonaws.com': testuser-at-111111111111
Password for 'https://testuser-at-111111111111@git-codecommit.ap-northeast-1.amazonaws.com': 
Counting objects: 3, done.
Writing objects: 100% (3/3), 262 bytes | 131.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/testrepo
 * [new branch]      test-branch -> test-branch

CodeCommitの画面に移動し、Pull Requestを作成します。すると承認ルールテンプレートに記載した内容の通り、1件の承認がないとマージができないよう表示されます。

f:id:FY0323:20220109165322p:plain

ここで別のユーザーで再ログインし、承認を行うことで、マージが可能になります。

f:id:FY0323:20220109165755p:plain

f:id:FY0323:20220109165807p:plain

※参考