Lustre ファイルシステムは S3 にどの IAM でアクセスしているのか調べてみた

Lustre ファイルシステムは S3 にどの IAM でアクセスしているのか調べてみた

Clock Icon2024.12.19

こんにちは!AWS 事業本部コンサルティング部のたかくに(@takakuni_)です。

最近、FSx for Lustre を触る機会がありまして、 Lustre は S3 にどの IAM でアクセスしているのか気になったので調べてみました。

Terraform で定義する部分がない

ことの発端はインフラの構築を HashiCorp Terraform で作成していたことから始まります。

Lustre ファイルシステムは aws_fsx_lustre_file_systemaws_fsx_data_repository_association で作成します。

IAM を定義する部分がないですね。

main.tf
###################################################
# Lustre File System
###################################################
resource "aws_fsx_lustre_file_system" "this" {
  storage_type                = "SSD"
  file_system_type_version    = "2.15"
  storage_capacity            = 1200
  security_group_ids          = [aws_security_group.lustre.id]
  subnet_ids                  = [module.vpc.private_subnets[0]]
  data_compression_type       = "LZ4"
  deployment_type             = "PERSISTENT_2"
  per_unit_storage_throughput = 250

  metadata_configuration {
    mode = "AUTOMATIC"
  }
}

resource "aws_fsx_data_repository_association" "this" {
  file_system_id       = aws_fsx_lustre_file_system.this.id
  data_repository_path = "s3://${aws_s3_bucket.data_repository.bucket}"
  file_system_path     = "/data-repository"

  s3 {
    auto_export_policy {
      events = ["NEW", "CHANGED", "DELETED"]
    }

    auto_import_policy {
      events = ["NEW", "CHANGED", "DELETED"]
    }
  }
}

バケットポリシー側も確認してみましたが、とくに更新されていないですね。

2024-12-19 at 18.18.47-dev-data-622809842341 - S3 バケット  S3  ap-northeast-1@2x.png

一体、どこで操作権限を管理しているのでしょうか。

結論

結論、 Lustre クラスターを作成した際に AWS 側で 2 つのサービスリンクロールを作成します。

  • AWSServiceRoleForAmazonFSx
  • AWSServiceRoleForFSxS3Access_fs-01234567890

このうちの AWSServiceRoleForFSxS3Access_fs-01234567890 の方で S3 とのやりとりを行います。

Amazon FSx uses two service-linked roles named AWSServiceRoleForAmazonFSx and AWSServiceRoleForFSxS3Access_fs-01234567890 that perform certain actions in your account. Examples of these actions are creating elastic network interfaces for your file systems in your VPC and accessing your data repository in an Amazon S3 bucket. For AWSServiceRoleForFSxS3Access_fs-01234567890, this service-linked role is created for each Amazon FSx for Lustre file system you create that is linked to an S3 bucket.

https://docs.aws.amazon.com/fsx/latest/LustreGuide/using-service-linked-roles.html#slr-permissions

AWSServiceRoleForAmazonFSx

AWSServiceRoleForAmazonFSx の方は何をしているのだろう。となりますよね。

AWSServiceRoleForAmazonFSx は ENI の作成や CloudWatch Logs へのメトリクス発行を行うようです。

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "CreateFileSystem",
			"Effect": "Allow",
			"Action": [
				"ds:AuthorizeApplication",
				"ds:GetAuthorizedApplicationDetails",
				"ds:UnauthorizeApplication",
				"ec2:CreateNetworkInterface",
				"ec2:CreateNetworkInterfacePermission",
				"ec2:DeleteNetworkInterface",
				"ec2:DescribeAddresses",
				"ec2:DescribeDhcpOptions",
				"ec2:DescribeNetworkInterfaces",
				"ec2:DescribeRouteTables",
				"ec2:DescribeSecurityGroups",
				"ec2:DescribeSubnets",
				"ec2:DescribeVPCs",
				"ec2:DisassociateAddress",
				"ec2:GetSecurityGroupsForVpc",
				"route53:AssociateVPCWithHostedZone"
			],
			"Resource": "*"
		},
		{
			"Sid": "PutMetrics",
			"Effect": "Allow",
			"Action": ["cloudwatch:PutMetricData"],
			"Resource": ["*"],
			"Condition": {
				"StringEquals": {
					"cloudwatch:namespace": "AWS/FSx"
				}
			}
		},

		{
			"Sid": "TagResourceNetworkInterface",
			"Effect": "Allow",
			"Action": ["ec2:CreateTags"],
			"Resource": ["arn:aws:ec2:*:*:network-interface/*"],
			"Condition": {
				"StringEquals": {
					"ec2:CreateAction": "CreateNetworkInterface"
				},
				"ForAllValues:StringEquals": {
					"aws:TagKeys": "AmazonFSx.FileSystemId"
				}
			}
		},
		{
			"Sid": "ManageNetworkInterface",
			"Effect": "Allow",
			"Action": [
				"ec2:AssignPrivateIpAddresses",
				"ec2:ModifyNetworkInterfaceAttribute",
				"ec2:UnassignPrivateIpAddresses"
			],
			"Resource": ["arn:aws:ec2:*:*:network-interface/*"],
			"Condition": {
				"Null": {
					"aws:ResourceTag/AmazonFSx.FileSystemId": "false"
				}
			}
		},
		{
			"Sid": "ManageRouteTable",
			"Effect": "Allow",
			"Action": ["ec2:CreateRoute", "ec2:ReplaceRoute", "ec2:DeleteRoute"],
			"Resource": ["arn:aws:ec2:*:*:route-table/*"],
			"Condition": {
				"StringEquals": {
					"aws:ResourceTag/AmazonFSx": "ManagedByAmazonFSx"
				}
			}
		},
		{
			"Sid": "PutCloudWatchLogs",
			"Effect": "Allow",
			"Action": [
				"logs:DescribeLogGroups",
				"logs:DescribeLogStreams",
				"logs:PutLogEvents"
			],
			"Resource": "arn:aws:logs:*:*:log-group:/aws/fsx/*"
		},
		{
			"Sid": "ManageAuditLogs",
			"Effect": "Allow",
			"Action": [
				"firehose:DescribeDeliveryStream",
				"firehose:PutRecord",
				"firehose:PutRecordBatch"
			],
			"Resource": "arn:aws:firehose:*:*:deliverystream/aws-fsx-*"
		}
	]
}

また、 AWSServiceRoleForAmazonFSx は FSx for Lustre ファイルシステム全体で利用されます。

The AWSServiceRoleForAmazonFSx is used by all Amazon FSx file system types; some of the listed permissions are not applicable to FSx for Lustre.

https://docs.aws.amazon.com/fsx/latest/LustreGuide/using-service-linked-roles.html#create-slr

やってみる

Lustre ファイルシステムの作成

それでは、 Lustre ファイルシステムを作成して、 IAM ロールの挙動を確認してみましょう。

Terraform で諸々作成します。

main.tf
###################################################
# Provider
###################################################
terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "5.81.0"
    }
  }
}

provider "aws" {
  region = "ap-northeast-1"
}

data "aws_caller_identity" "self" {}
data "aws_region" "current" {}

locals {
  prefix     = "dev"
  region     = data.aws_region.current.name
}

###################################################
# Network
###################################################
module "vpc" {
  source = "terraform-aws-modules/vpc/aws"

  name = local.prefix
  cidr = "10.0.0.0/16"

  azs                = ["${local.region}a"]
  private_subnets    = ["10.0.1.0/24"]
}

###################################################
# Security Group for Lustre File System
###################################################
resource "aws_security_group" "lustre" {
  name        = "${local.prefix}-lustre-sg"
  vpc_id      = module.vpc.vpc_id
  description = "${local.prefix}-hyperpod-sg"

  tags = {
    Name = "${local.prefix}-lustre-sg"
  }
}

# Ingress
resource "aws_vpc_security_group_ingress_rule" "lustre_all_traffic_self" {
  security_group_id            = aws_security_group.lustre.id
  referenced_security_group_id = aws_security_group.lustre.id
  ip_protocol                  = "-1"
}

# Egress
resource "aws_vpc_security_group_egress_rule" "lustre_all_traffic_self" {
  security_group_id            = aws_security_group.lustre.id
  referenced_security_group_id = aws_security_group.lustre.id
  ip_protocol                  = "-1"
}

###################################################
# Data Repository for Lustre File System
###################################################
resource "aws_s3_bucket" "data_repository" {
  bucket        = "${local.prefix}-data-${local.account_id}"
  force_destroy = true
}

resource "aws_s3_bucket_public_access_block" "data_repository" {
  bucket                  = aws_s3_bucket.data_repository.bucket
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

resource "aws_s3_bucket_ownership_controls" "data_repository" {
  bucket = aws_s3_bucket.data_repository.bucket
  rule {
    object_ownership = "BucketOwnerPreferred"
  }
}

###################################################
# Lustre File System
###################################################
resource "aws_fsx_lustre_file_system" "this" {
  storage_type                = "SSD"
  file_system_type_version    = "2.15"
  storage_capacity            = 1200
  security_group_ids          = [aws_security_group.lustre.id]
  subnet_ids                  = [module.vpc.private_subnets[0]]
  data_compression_type       = "LZ4"
  deployment_type             = "PERSISTENT_2"
  per_unit_storage_throughput = 250

  metadata_configuration {
    mode = "AUTOMATIC"
  }
}

resource "aws_fsx_data_repository_association" "this" {
  file_system_id       = aws_fsx_lustre_file_system.this.id
  data_repository_path = "s3://${aws_s3_bucket.data_repository.bucket}"
  file_system_path     = "/data-repository"

  s3 {
    auto_export_policy {
      events = ["NEW", "CHANGED", "DELETED"]
    }

    auto_import_policy {
      events = ["NEW", "CHANGED", "DELETED"]
    }
  }
}

IAM コンソールを見てみます。AWSServiceRoleForFSxS3Access_fs-01234567890 が作成されていますね。

2024-12-19 at 17.48.11-ロール  IAM  Global@2x.png

信頼されたエンティティには s3.data-source.lustre.fsx.amazonaws.com が許可されています。非常に興味深いです。

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Principal": {
				"Service": "s3.data-source.lustre.fsx.amazonaws.com"
			},
			"Action": "sts:AssumeRole"
		}
	]
}

ポリシーの中身を見てみる

AWSServiceRoleForFSxS3Access_fs-01234567890 の IAM ポリシーを見てみましょう。許可ポリシーが 2 つ付与されています。

2024-12-19 at 17.50.45-AWSServiceRoleForFSxS3Access_fs-04c2eb924c0a1afff  IAM  Global@2x.png

FSxDeleteServiceLinkedRoleAccess

FSxDeleteServiceLinkedRoleAccess は Lustre と S3 の関連付け削除時に IAM 自体を削除するためのポリシーのようです。

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Action": [
				"iam:DeleteServiceLinkedRole",
				"iam:GetServiceLinkedRoleDeletionStatus",
				"iam:GetRole"
			],
			"Resource": "arn:*:iam::*:role/aws-service-role/s3.data-source.lustre.fsx.amazonaws.com/AWSServiceRoleForFSxS3Access_*"
		}
	]
}

https://docs.aws.amazon.com/ja_jp/aws-managed-policy/latest/reference/FSxDeleteServiceLinkedRoleAccess.html

FSxS3Access

カスタマーインラインで定義された FSxS3Access ポリシーも見てみます。特定の S3 バケットに対して S3 API の許可が行われていますね。

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Action": [
				"s3:AbortMultipartUpload",
				"s3:DeleteObject",
				"s3:PutObject",
				"s3:Get*",
				"s3:List*",
				"s3:PutBucketNotification"
			],
			"Resource": [
				"arn:aws:s3:::dev-data-123456789012/*",
				"arn:aws:s3:::dev-data-123456789012"
			]
		}
	]
}

アタッチを増やしてみる

それでは、S3 との関連性を増やしてみましょう。

main.tf
###################################################
# Provider
###################################################
terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "5.81.0"
    }
  }
}

provider "aws" {
  region = "ap-northeast-1"
}

data "aws_caller_identity" "self" {}
data "aws_region" "current" {}

locals {
  prefix     = "dev"
  region     = data.aws_region.current.name
}

###################################################
# Network
###################################################
module "vpc" {
  source = "terraform-aws-modules/vpc/aws"

  name = local.prefix
  cidr = "10.0.0.0/16"

  azs                = ["${local.region}a"]
  private_subnets    = ["10.0.1.0/24"]
}

###################################################
# Security Group for Lustre File System
###################################################
resource "aws_security_group" "lustre" {
  name        = "${local.prefix}-lustre-sg"
  vpc_id      = module.vpc.vpc_id
  description = "${local.prefix}-hyperpod-sg"

  tags = {
    Name = "${local.prefix}-lustre-sg"
  }
}

# Ingress
resource "aws_vpc_security_group_ingress_rule" "lustre_all_traffic_self" {
  security_group_id            = aws_security_group.lustre.id
  referenced_security_group_id = aws_security_group.lustre.id
  ip_protocol                  = "-1"
}

# Egress
resource "aws_vpc_security_group_egress_rule" "lustre_all_traffic_self" {
  security_group_id            = aws_security_group.lustre.id
  referenced_security_group_id = aws_security_group.lustre.id
  ip_protocol                  = "-1"
}

###################################################
# Data Repository for Lustre File System
###################################################
resource "aws_s3_bucket" "data_repository" {
  bucket        = "${local.prefix}-data-${local.account_id}"
  force_destroy = true
}

resource "aws_s3_bucket_public_access_block" "data_repository" {
  bucket                  = aws_s3_bucket.data_repository.bucket
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

resource "aws_s3_bucket_ownership_controls" "data_repository" {
  bucket = aws_s3_bucket.data_repository.bucket
  rule {
    object_ownership = "BucketOwnerPreferred"
  }
}

+ resource "aws_s3_bucket" "data_repository_2" {
+   bucket        = "${local.prefix}-data2-${local.account_id}"
+   force_destroy = true
+ }
+
+ resource "aws_s3_bucket_public_access_block" "data_repository_2" {
+   bucket                  = aws_s3_bucket.data_repository_2.bucket
+   block_public_acls       = true
+   block_public_policy     = true
+   ignore_public_acls      = true
+   restrict_public_buckets = true
+ }
+
+ resource "aws_s3_bucket_ownership_controls" "data_repository_2" {
+   bucket = aws_s3_bucket.data_repository_2.bucket
+   rule {
+     object_ownership = "BucketOwnerPreferred"
+   }
+ }

###################################################
# Lustre File System
###################################################
resource "aws_fsx_lustre_file_system" "this" {
  storage_type                = "SSD"
  file_system_type_version    = "2.15"
  storage_capacity            = 1200
  security_group_ids          = [aws_security_group.lustre.id]
  subnet_ids                  = [module.vpc.private_subnets[0]]
  data_compression_type       = "LZ4"
  deployment_type             = "PERSISTENT_2"
  per_unit_storage_throughput = 250

  metadata_configuration {
    mode = "AUTOMATIC"
  }
}

resource "aws_fsx_data_repository_association" "this" {
  file_system_id       = aws_fsx_lustre_file_system.this.id
  data_repository_path = "s3://${aws_s3_bucket.data_repository.bucket}"
  file_system_path     = "/data-repository"

  s3 {
    auto_export_policy {
      events = ["NEW", "CHANGED", "DELETED"]
    }

    auto_import_policy {
      events = ["NEW", "CHANGED", "DELETED"]
    }
  }
}

+ resource "aws_fsx_data_repository_association" "this_2" {
+   file_system_id       = aws_fsx_lustre_file_system.this.id
+   data_repository_path = "s3://${aws_s3_bucket.data_repository_2.bucket}"
+   file_system_path     = "/data-repository2"
+
+   s3 {
+     auto_export_policy {
+       events = ["NEW", "CHANGED", "DELETED"]
+     }
+
+     auto_import_policy {
+       events = ["NEW", "CHANGED", "DELETED"]
+     }
+   }
+ }

IAM ポリシーが増える形ではなく FSxS3Access のリソース句がアップデートされました。

2024-12-19 at 17.58.21-AWSServiceRoleForFSxS3Access_fs-04c2eb924c0a1afff  IAM  Global@2x.png

FSxS3Access.json
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Action": [
				"s3:AbortMultipartUpload",
				"s3:DeleteObject",
				"s3:PutObject",
				"s3:Get*",
				"s3:List*",
				"s3:PutBucketNotification"
			],
			"Resource": [
				"arn:aws:s3:::dev-data2-123456789012/*",
				"arn:aws:s3:::dev-data-123456789012/*",
				"arn:aws:s3:::dev-data2-123456789012",
				"arn:aws:s3:::dev-data-123456789012"
			]
		}
	]
}

何を気にしていたかというと IAM ロールにアタッチ可能な IAM ポリシーの数は固定値で 20 までなので、どういった形で抜けているのか気になった次第でした。

https://repost.aws/knowledge-center/iam-increase-policy-size

Lustre と S3 の関連性に関してはとくに言及されていないようでした。

https://docs.aws.amazon.com/fsx/latest/LustreGuide/limits.html

変更して良いのか

編集ができないと記載されていますね。

Amazon FSx does not allow you to edit these service-linked roles. After you create a service-linked role, you cannot change the name of the role because various entities might reference the role. However, you can edit the description of the role using IAM. For more information, see Editing a service-linked role in the IAM User Guide.

https://docs.aws.amazon.com/fsx/latest/LustreGuide/using-service-linked-roles.html#edit-slr

確かにサービスリンクロールのため、説明以外変更するボタンがなかったです。

2024-12-19 at 18.06.23-AWSServiceRoleForFSxS3Access_fs-04c2eb924c0a1afff  IAM  Global.png

まとめ

以上、「Lustre ファイルシステムは S3 にどの IAM でアクセスしているのか調べてみた」でした。

「そういえば、 Terraform で IAM を定義していないな」と思ったので調べてみました。このブログがどなたかの参考になれば幸いです。

AWS 事業本部コンサルティング部のたかくに(@takakuni_)でした!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.