ablog

不器用で落着きのない技術者のメモ

S3 の VPC エンドポイントポリシーに必要な arn

参考

Amazon S3 ゲートウェイエンドポイントは IAM ポリシードキュメントを使用してサービスへのアクセスを制限します。​Amazon ECR で必要な最小限の Amazon S3 バケットアクセス許可のみを許可するには、エンドポイントの IAM ポリシードキュメントを作成するときに Amazon ECR が使用する Amazon S3 バケットへのアクセスを制限します。
次の表は、Amazon ECR が必要とする Amazon S3 バケットポリシーのアクセス許可を示しています。

アクセス権限 説明
arn:aws:s3:::prod-region-starport-layer-bucket/* 各 Docker イメージのレイヤーを含む Amazon S3 バケットへのアクセスを提供します。Amazon ECR によってサポートされている AWS リージョンのリージョン ID (例: 米国東部 (オハイオ) リージョン の場合は us-east-2) を表します。
https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/ecr-minimum-s3-perms.html

Q: VPC エンドポイントを設定して Amazon Linux AMI リポジトリへの接続を許可するにはどうすればよいですか?
インターネットにアクセスしなくても、VPC 内の yum リポジトリにアクセスすることが可能です。 お客様の VPC エンドポイントポリシーで、VPC から Amazon Linux AMI リポジトリをホストする S3 バケットへのトラフィックを許可する必要があります。

以下はこれを実現する VPC エンドポイントポリシーの例です。

{
  "Statement": [
    {
      "Sid": "Amazon Linux AMI Repository Access",
      "Principal": "*",
      "Action": [
        "s3:GetObject"
      ],
      "Effect": "Allow",
      "Resource": [
        "arn:aws:s3:::packages.*.amazonaws.com/*",
        "arn:aws:s3:::repo.*.amazonaws.com/*"
      ]
    }
  ]
}
https://aws.amazon.com/jp/amazon-linux-ami/faqs/#report

Organization 配下のアカウントの S3 バケットのみにアクセス許可する VPC エンドポイントポリシー

Organization 配下のアカウントのS3バケットのみにアクセス許可する VPC エンドポイントポリシー。

{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:*",
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "aws:PrincipalOrgID": "o-axxxxxxxxy"
                }
            }
        }
    ]
}

参考

S3 VPC Endpoint Policy for Private Subnet Zone
The sample endpoint policy below is used to restrict access to whitelisted S3 buckets by IAM users and roles within the same AWS Organization, using aws:PrincipalOrgId global IAM condition key.

Resources:
  PrivateSubnetS3Endpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcId: !Ref Vpc
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.s3"
      RouteTableIds:
        - !Ref PrivateSubnetRouteTable
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          # Read/write object access to specific buckets in the same region 
          - Effect: Allow
            Principal: "*"
            Action: "s3:*"
            Resource: 
              - "arn:aws:s3:::my-bucket-001/*"
              - "arn:aws:s3:::my-bucket-002/*"
            Condition:
              StringEquals:
                "aws:PrincipalOrgID":["o-xxxxxxxxxxx"]
Preventing Leaky Buckets - Enabling Private and Secure Access to S3 for Hybrid Clouds - Sourced
  • aws:PrincipalOrgID – リソースベースのポリシーの Principal 要素を簡単に指定できます。このグローバルキーは、組織内のすべての AWS アカウントのすべてのアカウント ID を一覧表示する代わりに使用できます。組織のメンバーであるすべてのアカウントを一覧表示せずに、Condition 要素に組織 ID を指定することができます。 注記 また、このグローバル条件は、組織のマスターアカウントにも適用されます。詳細については、グローバル条件キーの詳細については、『IAM ユーザーガイド』の「AWS グローバル条件コンテキストキー」でPrincipalOrgID の説明を参照してください。
  • aws:PrincipalOrgPaths – この条件キーを使用して、特定の組織ルート、OU、またはその子のメンバーを照合します。リクエストを行うプリンシパル (ルートユーザー、IAM ユーザー、またはロール) が指定された組織パスにある場合、aws:PrincipalOrgPaths 条件キーは true を返します。パスとは、AWS Organizations エンティティの構造をテキストで表記したものです。パスの詳細については、IAM ユーザーガイドの「AWS Organizations エンティティパスを理解する」を参照してください。この条件キーの使用方法の詳細については、IAM ユーザーガイドの「aws:PrincipalOrgPaths」を参照してください。たとえば次の条件要素は、同じ組織内の 2 つの OU のいずれかのメンバーと一致します。
            "Condition": {
                "ForAnyValue:StringLike": {
                    "aws:PrincipalOrgPaths": [
                        "o-a1b2c3d4e5/r-f6g7h8i9j0example/ou-def0-awsbbbbb/",
                        "o-a1b2c3d4e5/r-f6g7h8i9j0example/ou-jkl0-awsddddd/"
                    ]
                }
            }
  • organizations:ServicePrincipal – EnableAWSServiceAccess オペレーションまたは DisableAWSServiceAccess オペレーションで、他の AWS サービスを使用して信頼されたアクセスを有効または無効にする場合の条件として使用できます。organizations:ServicePrincipal を使用して、これらのオペレーションからのリクエストを、承認されたサービスプリンシパル名のリストに制限できます。たとえば以下のポリシーでは、ユーザーは、AWS Organizations を使用して信頼されたアクセスを有効および無効にする場合のみ、AWS Firewall Manager を指定することができます。
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowOnlyAWSFirewallIntegration",
            "Effect": "Allow",
            "Action": [
                "organizations:EnableAWSServiceAccess",
                "organizations:DisableAWSServiceAccess"
            ],
            "Resource": "*",
            "Condition": { 
                "ForAllValues:StringLike": {
                    "organizations:ServicePrincipal": [ "fms.amazonaws.com" ]
                }
            }
        }
    ]
https://docs.aws.amazon.com/ja_jp/organizations/latest/userguide/orgs_permissions_overview.html

VPC 接続を使った Glue ジョブで VPC エンドポイントポリシーを設定すると "An error occurred (403) when calling the HeadObject operation: Forbidden"

Glue の「接続」を作ってジョブにアタッチすると、VPC エンドポイント経由で S3 にアクセスできる。VPC エンドポイントポリシーでアクセス可能な S3 バケットを絞りたい場合は、業務で使うバケットに加えて Glue ジョブが使うスクリプトディレクトリ(デフォルト: aws-glue-scripts-{AWS Account ID}-{region})、一時ディレクトリ(デフォルト: aws-glue-temporary-{AWS Account ID}-{region}) も許可する必要がある。

事象

  • エラーログ
fatal error: An error occurred (403) when calling the HeadObject operation: Forbidden
Error downloading script ★: fatal error: An error occurred (403) when calling the HeadObject operation: Forbidden
  • ログ
--conf spark.hadoop.yarn.resourcemanager.connect.max-wait.ms=60000 
--conf spark.hadoop.fs.defaultFS=hdfs://172.17.2.84:8020 
--conf spark.hadoop.yarn.resourcemanager.address=172.17.2.84:8032 
--conf spark.dynamicAllocation.enabled=true 
--conf spark.shuffle.service.enabled=true 
--conf spark.dynamicAllocation.minExecutors=1
--conf spark.dynamicAllocation.maxExecutors=2 
--conf spark.executor.memory=5g 
--conf spark.executor.cores=4 
--conf spark.driver.memory=5g 
--JOB_ID j_d92fbdf94cb508d0ec9912ae6cf5d9aab616d829cc92da206f1a7f7849f3790e 
--JOB_RUN_ID jr_1d116a91943a03339a39f5e88a8ff16ac3a74a841f684f7547ab7999dcc44eb8 
--job-bookmark-option job-bookmark-disable 
--scriptLocation s3://aws-glue-scripts-123456789012-ap-northeast-1/admin/PrivateTest ★
--job-language python 
--TempDir s3://aws-glue-temporary-123456789012-ap-northeast-1/admin ★
--JOB_NAME PrivateTest
Detected region ap-northeast-1
Detected glue endpoint https://glue.ap-northeast-1.amazonaws.com
YARN_RM_DNS=172.17.2.84
JOB_NAME = PrivateTest
PYSPARK_VERSION 3 python3
Specifying ap-northeast-1 while copying script.
S3 copy with region specified failed. Falling back to not specifying region.

前提

  • 設定していた VPC エンドポイントポリシー
{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "*",
            "Resource": [
                "arn:aws:s3:::datalake-landing",
                "arn:aws:s3:::datalake-landing/*",
                "arn:aws:s3:::datalake-main",
                "arn:aws:s3:::datalake-main/*"
            ]
        }
    ]
}
  • 実行した Glue ジョブのコード(PySpark)
import sys
from awsglue.transforms import *
from awsglue.utils import getResolvedOptions
from pyspark.context import SparkContext
from awsglue.context import GlueContext
from awsglue.job import Job

sc = SparkContext()
glueContext = GlueContext(sc)
spark = glueContext.spark_session
job = Job(glueContext)
job.init('test job')

df = spark.read.csv("s3://datalake-landing/test.csv")
df.coalesce(1).write.mode('overwrite').csv("s3://datalake-main/")

解決策

{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "*",
            "Resource": [
                "arn:aws:s3:::glue-private-test",
                "arn:aws:s3:::glue-private-test/*",
                "arn:aws:s3:::log-raw-az",
                "arn:aws:s3:::log-raw-az/*"
            ]
        },
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": [
                "arn:aws:s3:::aws-glue-scripts-123456789012-ap-northeast-1",
                "arn:aws:s3:::aws-glue-scripts-123456789012-ap-northeast-1/*"
            ]
        },
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:DeleteObject"
            ],
            "Resource": [
                "arn:aws:s3:::aws-glue-temporary-123456789012-ap-northeast-1",
                "arn:aws:s3:::aws-glue-temporary-123456789012-ap-northeast-1/*"
            ]
        }
    ]
}
  • 上記は東京リージョンでデフォルトのケース。--scriptLocation や --TempDir を明示的に指定する場合はそのバケットの ARN を指定する。

参考

  • --scriptLocation - ETL スクリプトが s3://path/to/my/script.py のような形式で配置されている Amazon Simple Storage Service (Amazon S3) の場所。これは、JobCommand オブジェクトで設定されているスクリプトの場所を上書きします。
  • --TempDir - ジョブの一時ディレクトリとして使用できるバケットへの Amazon S3 パスを指定します。

たとえば、一時ディレクトリを設定するには、以下の引数を渡します。

'--TempDir': 's3-path-to-directory'
https://docs.aws.amazon.com/ja_jp/glue/latest/dg/aws-glue-programming-etl-glue-arguments.html

現状、データソースとしてS3にプライベートアクセスを行いたいだけの場合であってもこのような接続設定を介す必要があります。
これがわかりにくかった。入力したら次に進みます。今回はデータベースへの接続は行わないのですが、JDBC URL/ユーザ名/パスワードが必須
になっているのでここではダミーの情報を入力しました。

AWS GlueのジョブでVPCエンドポイントを利用したS3接続を行う - Qiita

VPC endpoints for Amazon S3 can alleviate these challenges. A VPC endpoint for Amazon S3 enables AWS Glue to use private IP addresses to access Amazon S3 with no exposure to the public internet. AWS Glue does not require public IP addresses, and you don't need an internet gateway, a NAT device, or a virtual private gateway in your VPC. You use endpoint policies to control access to Amazon S3. Traffic between your VPC and the AWS service does not leave the Amazon network.

(中略)

https://docs.aws.amazon.com/glue/latest/dg/images/PopulateCatalog-vpc-endpoint.png

https://docs.aws.amazon.com/glue/latest/dg/vpc-endpoints-s3.html

Glue で閉域網に閉じた構成

  • Glue 接続(VPCエンドポイント経由で S3 にアクセスするための接続)
    • タイプ: JDBC
    • JDBC URL: jdbc:mysql://dummy.com:1234/dummy # ダミー
    • VPC ID: vpc-b******1 # 任意のVPC
    • サブネット:subnet-1******b # 任意のサブネット
    • セキュリティグループ: sg-0***************6 # 任意のセキュリティグループ
    • SSL 接続が必要です: false
    • 説明: -
    • ユーザー名 dummy # ダミー
  • Glue Job(PySpark)
import sys
from awsglue.transforms import *
from awsglue.utils import getResolvedOptions
from pyspark.context import SparkContext
from awsglue.context import GlueContext
from awsglue.job import Job

sc = SparkContext()
glueContext = GlueContext(sc)
spark = glueContext.spark_session
job = Job(glueContext)
job.init('test job')

df = spark.read.csv("s3://datalake-landing/test")
df.coalesce(1).write.mode('overwrite').csv("s3://datalake-main/test")
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Access-to-specific-VPCE-only",
            "Effect": "Deny",
            "Principal": "*",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:DeleteObject"
            ],
            "Resource": [
                "arn:aws:s3:::datalake-landing",
                "arn:aws:s3:::datalake-landing/*"
            ],
            "Condition": {
                "StringNotEquals": {
                    "aws:sourceVpce": "vpce-0a*************fa"
                }
            }
        }
    ]
}
  • VPCエンドポイントポリシー
{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "*",
            "Resource": [
                "arn:aws:s3:::datalake-landing",
                "arn:aws:s3:::datalake-landing/*",
                "arn:aws:s3:::datalake-main",
                "arn:aws:s3:::datalake-main/*",
                "arn:aws:s3:::aws-glue-scripts-123456789012-ap-northeast-1",
                "arn:aws:s3:::aws-glue-scripts-123456789012-ap-northeast-1/*",
                "arn:aws:s3:::aws-glue-temporary-123456789012-ap-northeast-1",
                "arn:aws:s3:::aws-glue-temporary-123456789012-ap-northeast-1/*"
            ]
        }
    ]
}

Lake Formation のアクセス制御設定をクリアする手順

Lake Formation のアクセス制御設定をクリアする手順。

$ aws configure
AWS Access Key ID [None]: 
AWS Secret Access Key [None]: 
Default region name [us-east-1]: 
Default output format [None]: 
$ git clone https://github.com/aws-samples/aws-glue-samples.git
$ cd aws-glue-samples/utilities
$ python use_only_IAM_access_controls

STS の VPC エンドポイントポリシーのサンプル

Lambda in VPC から VPC エンドポイント経由で、STS にアクセスする際のVPCエンドポイントポリシーの例。AWSアカウントID: 123456789012 の Role id: A*******************O からの sts:AssumeRole のみを許可している。

  • STSVPC エンドポイントポリシー。
{
    "Statement": [
        {
            "Action": "sts:AssumeRole",
            "Effect": "Allow",
            "Resource": "*",
            "Condition": {
                "StringLike": {
                    "aws:userid": "A*******************O:*"
                }
            },
            "Principal": {
                "AWS": "123456789012"
            }
        }
    ]
}
  • aws:userid に指定する Role id は aws iam get-role --role-name <ロール名> で取得する。
$  aws iam get-role --role-name LambdaRole
{
    "Role": {
        "Description": "Allows Lambda functions to call AWS services on your behalf.", 
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17", 
            "Statement": [
                {
                    "Action": "sts:AssumeRole", 
                    "Effect": "Allow", 
                    "Principal": {
                        "Service": "lambda.amazonaws.com"
                    }
                }
            ]
        }, 
        "MaxSessionDuration": 3600, 
        "RoleId": "A*******************O",  ★ これを aws:userid に指定する
        "CreateDate": "2020-02-01T06:35:46Z", 
        "RoleName": "LambdaRole", 
        "Path": "/", 
        "Arn": "arn:aws:iam::123456789012:role/LambdaRole"
    }
}

CloudWatch Logs の VPC エンドポイントポリシーでアクセス可能なロググループを絞る

AWSマネジメントコンソールで、[エンドポイント] から CloudWatch Logs のエンドポイント(com.amazonaws.ap-northeast-1.logs)を選択して、[ポリシー]タブを選択、[ポリシーの編集]をクリックして保存する。以下は特定のロググループのみに制限する VPC Endpoint Policy の例。

{
    "Statement": [
        {
            "Action": "*",
            "Effect": "Allow",
            "Resource": "arn:aws:logs:ap-northeast-1:123456789012:log-group:/aws-glue/crawlers:*",
            "Principal": "*"
        }
    ]
}

※"123456789012" は AWS アカウントID