S3でVPCエンドポイントからのアクセスのみ許可するバケットポリシーを作成しましたが、マネジメントコンソールからもアクセスしたいといったことがあったので特定のIAMユーザーは許可するバケットポリシーを作成してみました。
リソース作成
バケットポリシー以外のリソースはCloudFormationで作成します。
構成図としては以下のようになります。
EC2からS3へのアクセスはVPCエンドポイント経由になるように設定を行います。
リソースの作成に使用したCloudFormationテンプレートは以下になります。
AWSTemplateFormatVersion: "2010-09-09"
Description: Test Stack
Parameters:
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
VPCCIDR:
Default: 192.168.0.0/16
Type: String
PrivateSubnet01CIDR:
Default: 192.168.0.0/24
Type: String
EC2VolumeSize:
Default: 32
Type: Number
EC2VolumeIOPS:
Default: 3000
Type: Number
EC2AMI:
Default: ami-0310b105770df9334
Type: AWS::EC2::Image::Id
EC2InstanceType:
Default: t3.micro
Type: String
Resources:
# ------------------------------------------------------------#
# VPC
# ------------------------------------------------------------#
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VPCCIDR
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: test-vpc
# ------------------------------------------------------------#
# Subnet
# ------------------------------------------------------------#
PrivateSubnet01:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: ap-northeast-1a
CidrBlock: !Ref PrivateSubnet01CIDR
Tags:
- Key: Name
Value: test-subnet-private-1a
VpcId: !Ref VPC
# ------------------------------------------------------------#
# RouteTable
# ------------------------------------------------------------#
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: test-private-rtb
PrivateRtAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable
SubnetId: !Ref PrivateSubnet01
# ------------------------------------------------------------#
# Security Group
# ------------------------------------------------------------#
EC2SG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: for EC2
GroupName: test-sg-ec2
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
FromPort: -1
IpProtocol: -1
ToPort: -1
Tags:
- Key: Name
Value: test-sg-ec2
VpcId: !Ref VPC
VPCEndpointSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: for VPC Endpoint
GroupName: test-sg-vpc-endpoint
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
FromPort: -1
IpProtocol: -1
ToPort: -1
SecurityGroupIngress:
- CidrIp: 192.168.0.0/16
FromPort: 443
IpProtocol: tcp
ToPort: 443
Tags:
- Key: Name
Value: test-sg-vpc-endpoint
VpcId: !Ref VPC
# ------------------------------------------------------------#
# IAM
# ------------------------------------------------------------#
EC2IAMRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- 'sts:AssumeRole'
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
- arn:aws:iam::aws:policy/AmazonS3FullAccess
RoleName: test-iam-role-ec2
Tags:
- Key: Name
Value: test-iam-role-ec2
EC2IAMInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: test-iam-instanceprofile-ec2
Roles:
- !Ref EC2IAMRole
# ------------------------------------------------------------#
# EC2
# ------------------------------------------------------------#
EC2:
Type: AWS::EC2::Instance
Properties:
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
DeleteOnTermination: true
Encrypted: true
Iops: !Ref EC2VolumeIOPS
VolumeSize: !Ref EC2VolumeSize
VolumeType: gp3
DisableApiTermination: false
IamInstanceProfile: !Ref EC2IAMInstanceProfile
ImageId: !Ref EC2AMI
InstanceType: !Ref EC2InstanceType
NetworkInterfaces:
- DeleteOnTermination: true
DeviceIndex: 0
GroupSet:
- !Ref EC2SG
SubnetId: !Ref PrivateSubnet01
Tags:
- Key: Name
Value: test-ec2
# ------------------------------------------------------------#
# VPC Endpoint
# ------------------------------------------------------------#
S3Endpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
RouteTableIds:
- !Ref PrivateRouteTable
ServiceName: !Sub com.amazonaws.${AWS::Region}.s3
VpcEndpointType: Gateway
VpcId: !Ref VPC
SystemsManagerEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcEndpointType: Interface
PrivateDnsEnabled: true
ServiceName: !Sub com.amazonaws.${AWS::Region}.ssm
VpcId: !Ref VPC
SubnetIds:
- !Ref PrivateSubnet01
SecurityGroupIds:
- !Ref VPCEndpointSG
SystemsManagerMessageEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcEndpointType: Interface
PrivateDnsEnabled: true
ServiceName: !Sub com.amazonaws.${AWS::Region}.ssmmessages
VpcId: !Ref VPC
SubnetIds:
- !Ref PrivateSubnet01
SecurityGroupIds:
- !Ref VPCEndpointSG
# ------------------------------------------------------------#
# S3
# ------------------------------------------------------------#
S3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub bucket-${AWS::AccountId}-${AWS::Region}
今回は検証用の為、EC2が使用するIAMロールにS3FullAccessを設定しています。
実際使用する際は必要なアクションだけに絞ったIAMポリシーを設定することをお勧めします。
デプロイは以下のコマンドで行います。
aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --capabilities CAPABILITY_NAMED_IAM
デプロイが完了するとEC2にセッションマネージャーで接続することが出来ます。
接続方法は以下のドキュメントに記載されているのでご確認ください。
セッションを開始する
接続後、以下のコマンドを実行するとS3へのアクセスが確認できます。
aws s3 ls s3://S3バケット名
ここまでの手順ではまだS3にファイルを置いていないので上記のコマンドを実行しても何も表示されません。
なので、ファイルを以下のコマンドでアップロードしてみます。
cd
touch test.txt
aws cp ./test.txt s3://S3バケット名
aws s3 ls s3://S3バケット名
cpコマンドでS3にtest.txtをコピーしています。
lsコマンドを実行するとファイルが確認できます。
また、バケットポリシーで制限をしていないためマネジメントコンソールからも確認することが可能です。
バケットポリシーを設定する
ここから本題です。
特定の VPC エンドポイントからアクセスを許可するバケットポリシーは以下のようになります。
特定の VPC エンドポイントへのアクセスの制限
{
"Version": "2012-10-17",
"Id": "Policy1415115909152",
"Statement": [
{
"Sid": "Access-to-specific-VPCE-only",
"Principal": "*",
"Action": "s3:*",
"Effect": "Deny",
"Resource": ["arn:aws:s3:::S3バケット名",
"arn:aws:s3:::S3バケット名/*"],
"Condition": {
"StringNotEquals": {
"aws:SourceVpce": "VPCエンドポイントID"
}
}
}
]
}
上記のバケットポリシーを設定するとマネジメントコンソールからアクセスができなくなります。
実際に設定して確認してみます。
EC2で以下のコマンドを実行してください。
policy.jsonには上記のバケットポリシーを記載してください。
aws s3api put-bucket-policy --bucket S3バケット名 --policy file://policy.json
コマンド実行後、マネジメントコンソールから該当のS3にアクセスすると以下のようになります。
EC2からのアクセスを確認してみます。
以下のlsコマンドを実行します。
aws s3 ls s3://S3バケット名
実行するとアップロードしたtest.txtが確認できます。
もし、この時点で権限エラーが発生した場合はバケットポリシーに問題があるためEC2からコマンドでバケットポリシーを更新するか、以下のドキュメントの手順に従ってルートユーザーでバケットポリシーを更新してください。
Amazon S3 バケットへのすべてのユーザーのアクセスを誤って拒否しました。アクセスを回復するにはどうしたらいいですか?
特定のIAMユーザーを許可するバケットポリシーを作成してみます。
ユーザーの制御にはaws:PrincipalArnというグローバル条件キーを使用します。
こちらの条件キーを使用することでリクエスト元のIAMユーザーのARNとバケットポリシーで設定したARNで比較を行うことが可能になります。
つまりConditionの中でaws:PrincipalArnを設定することで特定のIAMユーザーはアクセスを許可させることが可能になります。
実際に設定するバケットポリシーは以下のようになります。
{
"Version": "2012-10-17",
"Id": "Policy1415115909152",
"Statement": [
{
"Sid": "Access-to-specific-VPCE-only",
"Principal": "*",
"Action": "s3:*",
"Effect": "Deny",
"Resource": ["arn:aws:s3:::S3バケット名",
"arn:aws:s3:::S3バケット名/*"],
"Condition": {
"StringNotEquals": {
"aws:SourceVpce": "VPCエンドポイントID",
"aws:PrincipalArn": "IAMユーザーARN"
}
}
}
]
}
実際に上記のバケットポリシーを設定してみます。
設定はEC2から行います。
policy.jsonを上記のバケットポリシーに修正して以下のコマンドを実行します。
aws s3api put-bucket-policy --bucket S3バケット名 --policy file://policy.json
実行後、アクセス確認のために以下のコマンドでS3バケット内を確認します。
aws s3 ls s3://S3バケット名
EC2からは正常にtest.txtが確認できます。
次にaws:PrincipalArnで設定したIAMユーザーでマネジメントコンソールからアクセスしてみます。
ARNで指定したIAMユーザーの場合は以下のように正常にS3内のオブジェクトが確認できます。
次にaws:PrincipalArnで設定していないIAMユーザーでアクセスします。
許可されていないユーザーの場合は以下のように権限エラーが発生します。
さいごに
aws:PrincipalArnはIAMユーザー以外にもIAMロールのARNでも評価させることが可能なのでスイッチロールさせている環境にも使用することが可能です。