CloudFormationを使用してECRリポジトリポリシー入門
ECRリポジトリはリソースベースのポリシーを設定することができます。
本ページでは、リポジトリポリシーを設定し、イメージのプッシュ/プルを制限することを目指します。
構築する環境

ECRを作成します。
ECRリポジトリポリシーを設定し、リポジトリに対するイメージのプッシュ/プルを制限します。
プライベートサブネット内にEC2インスタンスを3つ作成します。
インスタンスは最新版のAmazon Linux 2とします。
3台は同様の設定を行いますが、リポジトリポリシーによって、以下の動作を実現します。
- EC2インスタンス1:イメージのプッシュを許可する
- EC2インスタンス2:イメージのプルを許可する
- EC2インスタンス3:イメージのプッシュ/プルを許可しない
CloudFormationテンプレートファイル
上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。
https://github.com/awstut-an-r/awstut-fa/tree/main/085
テンプレートファイルのポイント解説
EC2
Resources:
  Instance1:
    Type: AWS::EC2::Instance
    Properties:
      IamInstanceProfile: !Ref InstanceProfile1
      ImageId: !Ref ImageId
      InstanceType: !Ref InstanceType
      NetworkInterfaces:
        - DeviceIndex: 0
          SubnetId: !Ref InstanceSubnet
          GroupSet:
            - !Ref InstanceSecurityGroup
      UserData: !Ref UserData
  Instance2:
    Type: AWS::EC2::Instance
    Properties:
      ...
  Instance3:
    Type: AWS::EC2::Instance
    Properties:
      ...
Code language: YAML (yaml)3台のEC2インスタンスを作成します。
全て同様の設定を行います。
インスタンス作成時に、ユーザデータを使用して、初期化処理を行います。
今回は以下の処理を実行します。
#!/bin/bash -xe
yum update -y
amazon-linux-extras install docker
systemctl start docker
usermod -a -G docker ec2-user
Code language: Bash (bash)Dockerをインストール後、これを起動し、ec2-userの権限でDockerコマンドを実行できるようにします。
以下がインスタンス用のIAMロールです。
Resources:
  InstanceRole1:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service:
                - ec2.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
        - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess
  InstanceRole2:
    Type: AWS::IAM::Role
    Properties:
      ...
  InstanceRole3:
    Type: AWS::IAM::Role
    Properties:
      ...
Code language: YAML (yaml)各インスタンス用にIAMロールを作成します。
全て同様の設定を行います。
ポイントはAWS管理ポリシー「AmazonEC2ContainerRegistryFullAccess」をアタッチする点です。
つまりこれらのIAMロールをアタッチすることによって、3台のインスタンスはECRリポジトリにイメージをプッシュ/プルする権限が付与されるということです。
ECR
Resources:
  ECRRepository:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName: !Ref Prefix
      RepositoryPolicyText:
        Version: 2012-10-17
        Statement:
          - Effect: Deny
            Principal: "*"
            NotAction:
              - ecr:BatchCheckLayerAvailability
              - ecr:CompleteLayerUpload
              - ecr:InitiateLayerUpload
              - ecr:PutImage
              - ecr:UploadLayerPart
            Condition:
              ArnEquals:
                aws:PrincipalArn: !Ref InstanceRoleArn1
          - Effect: Deny
            Principal: "*"
            NotAction:
              - ecr:BatchGetImage
              - ecr:GetDownloadUrlForLayer
            Condition:
              ArnEquals:
                aws:PrincipalArn: !Ref InstanceRoleArn2
          - Effect: Deny
            Principal: "*"
            Action: "*"
            Condition:
              ArnNotEquals:
                aws:PrincipalArn:
                  - !Ref InstanceRoleArn1
                  - !Ref InstanceRoleArn2
Code language: YAML (yaml)RepositoryPolicyTextプロパティでリポジトリポリシーを設定します。
リポジトリポリシーは3つのステートメントで構成されています。
1つ目のステートメントはEC2インスタンス1用です。
Condition要素でArnEqualsおよびaws:PrincipalArnを使用して、EC2インスタンス1にアタッチしたIAMロールのARNを指定します。
Effect要素に「Deny」を指定した上で、NotAction要素を使用することによって、NotActionで指定されたアクション以外を拒否することになります。
NotActionにはイメージプッシュに必要なアクションを指定しましたので、インスタンス1はプッシュ以外のアクションが拒否されるということです。
2つ目のステートメントはEC2インスタンス2用です。
1つ目と同様の記法で、EC2インスタンス2にイメージプル以外のアクションを拒否します。
3つ目のステートメントは上記以外のリソースからアクセス用です。
Condition要素でArnNotEqualsおよびaws:PrincipalArnを使用して、上述の2つのIAMロールのARNを指定し、これら以外のリソースを対象とします。
Effect要素に「Deny」を、Actionに「*」を指定することによって、全てのアクションを拒否することになります。
つまり2つのEC2インスタンス以外のアクセスの場合は、全て拒否されるということです。
環境構築
CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
CloudFormationスタックを作成し、スタック内のリソースを確認する
CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。

各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。
- ECRリポジトリ:fa-085
- EC2インスタンス1:i-0bde1cc6fc498a208
- EC2インスタンス2:i-0296e9a1b5f70b522
- EC2インスタンス3:i-0d0cc36a5186db23b
作成されたリソースをAWS Management Consoleから確認します。
ECRを確認します。

正常にECRリポジトリが作成されています。
続いてECRのリポジトリポリシーを確認します。

CloudFormationテンプレートで定義した通りに、リポジトリポリシーが作成されていることがわかります。
ECRにイメージをプッシュするためのコマンドを確認します。

このコマンドを3つのEC2インスタンスで実行して、イメージのプッシュを試みます。
動作確認
EC2インスタンス1
準備が整いましたので、EC2インスタンス1にアクセスします。
インスタンスへのアクセスはSSM Session Managerを使用します。
% aws ssm start-session --target i-0bde1cc6fc498a208
Starting session with SessionId: root-0c908f28046ae77b1
sh-4.2$
Code language: Bash (bash)SSM Session Managerの詳細につきましては、以下のページをご確認ください。

操作するユーザを変更します。
sh-4.2$ sudo su --login ec2-user
[ec2-user@ip-10-0-2-38 ~]$
Code language: Bash (bash)SSM Session Managerのデフォルトユーザであるssm-userから、ec2-userに切り替えました。
これでec2-userの権限で、Dockerコマンドを実行することができます。
検証用のDockerfileを作成後、ECRリポジトリにイメージをプッシュするコマンドを実行します。
[ec2-user@ip-10-0-2-38 ~]$ echo "FROM amazonlinux" > Dockerfile
[ec2-user@ip-10-0-2-38 ~]$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin [account-id].dkr.ecr.ap-northeast-1.amazonaws.com
...
Login Succeeded
[ec2-user@ip-10-0-2-38 ~]$ docker build -t fa-085 .
Sending build context to Docker daemon  11.26kB
Step 1/1 : FROM amazonlinux
latest: Pulling from library/amazonlinux
12df598bc31e: Pull complete
Digest: sha256:cb8a67164376ecca3b9993e6bb7d81dd868b7836d2631582becd140c8edf27bf
Status: Downloaded newer image for amazonlinux:latest
 ---> 06c59c262be8
Successfully built 06c59c262be8
Successfully tagged fa-085:latest
[ec2-user@ip-10-0-2-38 ~]$ docker tag fa-085:latest [account-id].dkr.ecr.ap-northeast-1.amazonaws.com/fa-085:latest
[ec2-user@ip-10-0-2-38 ~]$ docker push [account-id].dkr.ecr.ap-northeast-1.amazonaws.com/fa-085:latest
The push refers to repository [[account-id].dkr.ecr.ap-northeast-1.amazonaws.com/fa-085]
bb99a8750875: Pushed
latest: digest: sha256:ab12022e1a7cd318058af138291d638b1024dcb9844799e8014e6e37d0b35831 size: 529
Code language: Bash (bash)イメージをプッシュすることができました。

確かにイメージがプッシュされていることがわかります。
このようにリポジトリポリシーに従って、EC2インスタンス1からイメージをプッシュすることができました。
続いてイメージのプルを試みます。
[ec2-user@ip-10-0-2-38 ~]$ docker pull [account-id].dkr.ecr.ap-northeast-1.amazonaws.com/fa-085:latest
Error response from daemon: pull access denied for [account-id].dkr.ecr.ap-northeast-1.amazonaws.com/fa-085, repository does not exist or may require 'docker login': denied: User: arn:aws:sts::[account-id]:assumed-role/fa-085-EC2Stack-16BBODB5DWYO1-InstanceRole1-YILWRMUJ51EE/i-0bde1cc6fc498a208 is not authorized to perform: ecr:BatchGetImage on resource: arn:aws:ecr:ap-northeast-1:[account-id]:repository/fa-085 with an explicit deny in a resource-based policy
Code language: Bash (bash)イメージプルに失敗しました。
EC2インスタンス1にアタッチしたIAMロールでは、ECRへのフルアクセスを許可しています。
しかしリポジトリポリシーによって、プッシュ以外のアクションは拒否されたということです。
EC2インスタンス2
同様の方法でインスタンス2も動作確認を行います。
まずイメージをプルします。
% aws ssm start-session --target i-0296e9a1b5f70b522
Starting session with SessionId: root-0ce11bf400c04ba6d
sh-4.2$
sh-4.2$ sudo su --login ec2-user
[ec2-user@ip-10-0-2-59 ~]$
[ec2-user@ip-10-0-2-59 ~]$ docker pull [account-id].dkr.ecr.ap-northeast-1.amazonaws.com/fa-085:latest
latest: Pulling from fa-085
12df598bc31e: Pull complete
Digest: sha256:ab12022e1a7cd318058af138291d638b1024dcb9844799e8014e6e37d0b35831
Status: Downloaded newer image for [account-id].dkr.ecr.ap-northeast-1.amazonaws.com/fa-085:latest
[account-id].dkr.ecr.ap-northeast-1.amazonaws.com/fa-085:latest
[ec2-user@ip-10-0-2-59 ~]$ docker image ls
REPOSITORY                                                 TAG       IMAGE ID       CREATED       SIZE
[account-id].dkr.ecr.ap-northeast-1.amazonaws.com/fa-085   latest    06c59c262be8   3 weeks ago   194MB
Code language: Bash (bash)イメージをプルすることができました。
次にイメージのプッシュを試みます。
[ec2-user@ip-10-0-2-59 ~]$ echo "FROM amazonlinux" > Dockerfile
[ec2-user@ip-10-0-2-59 ~]$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin [account-id].dkr.ecr.ap-northeast-1.amazonaws.com
...
Login Succeeded
[ec2-user@ip-10-0-2-59 ~]$ docker build -t fa-085 .
Sending build context to Docker daemon  11.26kB
Step 1/1 : FROM amazonlinux
latest: Pulling from library/amazonlinux
Digest: sha256:cb8a67164376ecca3b9993e6bb7d81dd868b7836d2631582becd140c8edf27bf
Status: Downloaded newer image for amazonlinux:latest
 ---> 06c59c262be8
Successfully built 06c59c262be8
Successfully tagged fa-085:latest
[ec2-user@ip-10-0-2-59 ~]$ docker tag fa-085:latest [account-id].dkr.ecr.ap-northeast-1.amazonaws.com/fa-085:latest
[ec2-user@ip-10-0-2-59 ~]$ docker push [account-id].dkr.ecr.ap-northeast-1.amazonaws.com/fa-085:latest
The push refers to repository [[account-id].dkr.ecr.ap-northeast-1.amazonaws.com/fa-085]
bb99a8750875: Retrying in 1 second
EOF
Code language: Bash (bash)ECRリポジトリにイメージをプッシュするコマンドを実行した結果、プッシュ時にタイムアウトして失敗しました。
EC2インスタンス2にアタッチしたIAMロールでも、ECRへのフルアクセスを許可しています。
しかしリポジトリポリシーによって、プル以外のアクションは拒否されたということです。
EC2インスタンス3
最後にインスタンス3も動作確認を行います。
まずイメージのプッシュを試みます。
% aws ssm start-session --target i-0d0cc36a5186db23b
Starting session with SessionId: root-0dbc2ca03d6941d57
sh-4.2$
sh-4.2$ sudo su --login ec2-user
[ec2-user@ip-10-0-2-67 ~]$
[ec2-user@ip-10-0-2-67 ~]$ echo "FROM amazonlinux" > Dockerfile
[ec2-user@ip-10-0-2-67 ~]$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin [account-id].dkr.ecr.ap-northeast-1.amazonaws.com
...
Login Succeeded
[ec2-user@ip-10-0-2-67 ~]$ docker build -t fa-085 .
Sending build context to Docker daemon  11.26kB
Step 1/1 : FROM amazonlinux
latest: Pulling from library/amazonlinux
12df598bc31e: Pull complete
Digest: sha256:cb8a67164376ecca3b9993e6bb7d81dd868b7836d2631582becd140c8edf27bf
Status: Downloaded newer image for amazonlinux:latest
 ---> 06c59c262be8
Successfully built 06c59c262be8
Successfully tagged fa-085:latest
[ec2-user@ip-10-0-2-67 ~]$ docker tag fa-085:latest [account-id].dkr.ecr.ap-northeast-1.amazonaws.com/fa-085:latest
[ec2-user@ip-10-0-2-67 ~]$ docker push [account-id].dkr.ecr.ap-northeast-1.amazonaws.com/fa-085:latest
The push refers to repository [[account-id].dkr.ecr.ap-northeast-1.amazonaws.com/fa-085]
bb99a8750875: Retrying in 1 second
EOF
Code language: Bash (bash)プッシュに失敗しました。
続いてイメージのプルを試みます。
[ec2-user@ip-10-0-2-67 ~]$ docker pull [account-id].dkr.ecr.ap-northeast-1.amazonaws.com/fa-085:latest
Error response from daemon: pull access denied for [account-id].dkr.ecr.ap-northeast-1.amazonaws.com/fa-085, repository does not exist or may require 'docker login': denied: User: arn:aws:sts::[account-id]:assumed-role/fa-085-EC2Stack-16BBODB5DWYO1-InstanceRole3-12LD37CD8F85G/i-0d0cc36a5186db23b is not authorized to perform: ecr:BatchGetImage on resource: arn:aws:ecr:ap-northeast-1:[account-id]:repository/fa-085 with an explicit deny in a resource-based policy
Code language: Bash (bash)プルも失敗しました。
EC2インスタンス3にアタッチしたIAMロールでも、ECRへのフルアクセスを許可しています。
しかしリポジトリポリシーによって、プッシュ/プルのアクションは拒否されたということです。
まとめ
ECRリポジトリポリシーの設定方法を確認しました。