yuj1osm's tech blog

クラウドやセキュリティなど

Amazon VPC Latticeが一般提供されたので試してみた

昨年のre:Invent2022で発表された、Amazon VPC Latticeが一般提供されたので試してみました。

aws.amazon.com

アップデート概要

  • 異なるVPCにあるアプリケーション間の通信を簡易に実現できる

検証内容

今回は以下の構成を作成して検証してみました。

  • IPレンジが同じVPC「client-vpc」と「target-vpc」を用意し、それぞれにプライベートサブネットを配置する
  • 図には明記していないが、「target-vpc」にはALB「target-alb」を配置するため、プライベートサブネットは以下のように2つ配置する
    EC2インスタンス「target-ec2」を配置するのは一方のみ

それぞれのプライベートサブネットにEC2インスタンス「client-ec2」と「target-ec2」を配置する

  • VPC外にLambda関数「target-lambda」を配置する

  • 「client-ec2」から「target-alb」経由で「target-ec2」にHTTP通信を行う
  • 「client-ec2」から「target-lambda」にHTTP通信を行う

VPC Latticeの構築

サービスネットワークとサービスの作成

VPC」→「VPC格子」から、「サービスネットワークを作成する」を押下します。

サービスネットワーク名を入力します。
サービスネットワーク名:testservicenetwotk01

サービスの関連付けを行いますが、まだサービスを作成していないので、「Amazon VPC Latticeサービスを作成」を押下します。

サービス名を入力します。
サービス名:testservice01

その他はデフォルトのまま次へ進みます。

最後に「VPC Latticeサービスを作成」を押下します。

サービス「testservice01」が作成されました。

サービスネットワーク作成の画面に戻り、サービスの関連付けで先ほど作成したサービス「testservice01」を選択します。

VPCの関連付けで、VPC「client-vpc」を選択します。
セキュリティグループは、インバウンドでHTTPが許可されているものを作成し、選択します。

その他はデフォルトのまま「サービスネットワークの作成」を押下します。

サービスネットワーク「testservicenetwork01」が作成されました。

ターゲットグループの作成

「ターゲットグループの作成」を押下します。

ターゲットタイプはALBを選択します。
ターゲットグループ名を入力し、プロトコルとポートをHTTPと80に設定します。
ターゲットグループ名:targetgroup01

VPCは「target-vpc」を選択し、その他はデフォルトのまま次へ進みます。

ターゲットの登録で、事前に作成しておいた「ta-get-alb」を選択し、「ターゲットグループの作成」を押下します。

ターゲットグループ「targetgroup01」が作成されました。

リスナーの作成

サービス「testservice01」から「ルーティング」タブを選択し、「リスナーの追加」を押下します。

リスナーの追加で、プロトコルとポートをHTTPと80に設定します。
デフォルトアクションで、ターゲットグループ「targetgroup01」を選択し、「追加」を押下します。

リスナーが作成されました。

異なるVPC間におけるEC2インスタンスの疎通確認

「target-ec2」にはApacheをインストールして実行させています。
注意点として、Apacheはデフォルトでは403応答によりヘルスチェックが失敗するため、以下のようにindex.htmlを作成しておくとよいでしょう。

$echo "<html><head><title>Test Page</title></head><body>Hello world</body></html>" > /var/www/html/index.html

「client-ec2」から、サービスのドメイン名に対してcurlを実行します。
以下のレスポンスが返ってきたため、「target-ec2」と疎通が取れていることを確認できました。

$ curl -v http://testservice01-XXXXXXXXXX.vpc-lattice-svcs.ap-northeast-1.on.aws
*   Trying 169.254.171.96:80...
* Connected to testservice01-XXXXXXXXXX.vpc-lattice-svcs.ap-northeast-1.on.aws (169.254.171.96) port 80 (#0)
> GET / HTTP/1.1
> Host: testservice01-XXXXXXXXXX.vpc-lattice-svcs.ap-northeast-1.on.aws
> User-Agent: curl/7.88.1
> Accept: */*
>
< HTTP/1.1 200 OK
< date: Sat, 15 Apr 2023 14:39:44 GMT
< content-type: text/html; charset=UTF-8
< content-length: 75
< server: Apache/2.4.56 (Amazon Linux)
< last-modified: Sat, 15 Apr 2023 14:20:03 GMT
< etag: "4b-5f960a761f614"
< accept-ranges: bytes
<
<html><head><title>Test Page</title></head><body>Hello world</body></html>
* Connection #0 to host testservice01-XXXXXXXXXX.vpc-lattice-svcs.ap-northeast-1.on.aws left intact

VPC内EC2インスタンスVPC外Lambdaの疎通確認

ターゲットグループをLambdaに変更してみます。 ターゲットグループ名:targetgroup02

Lambda関数で「target-lambda」を選択し、「ターゲットグループの作成」を押下します。

ターゲットグループ「targetgroup02」が作成されました。

サービス「testservice01」のリスナーで、ターゲットグループを「targetgroup02」に変更します。

「client-ec2」から、サービスのドメイン名に対してcurlを実行します。
以下のレスポンスが返ってきたため、「target-lambda」と疎通が取れていることを確認できました。

$ curl -v http://testservice01-XXXXXXXXXX.vpc-lattice-svcs.ap-northeast-1.on.aws
*   Trying 169.254.171.96:80...
* Connected to testservice01-XXXXXXXXXX.vpc-lattice-svcs.ap-northeast-1.on.aws (169.254.171.96) port 80 (#0)
> GET / HTTP/1.1
> Host: testservice01-XXXXXXXXXX.vpc-lattice-svcs.ap-northeast-1.on.aws
> User-Agent: curl/7.88.1
> Accept: */*
>
< HTTP/1.1 200 OK
< date: Sat, 15 Apr 2023 14:52:03 GMT
< content-type: application/json
< content-length: 20
< x-amzn-requestid: XXXXXXXXXX
< x-amzn-remapped-content-length: 0
< x-amz-executed-version: $LATEST
< x-amzn-trace-id: root=XXXXXXXXXX;sampled=0;lineage=0c55971e:0
<
* Connection #0 to host testservice01-XXXXXXXXXX.vpc-lattice-svcs.ap-northeast-1.on.aws left intact
"Hello from Lambda!"

まとめ

細かなルーティングを意識することなく、L7の世界で異なるVPC間の通信ができました。
VPC Latticeがネットワークを抽象化してくれることで、インスタンスやコンテナ、サーバレスといったサービスの通信を同等に管理することが可能です。
また、VPCのCIDRが重複していても通信が可能なことは嬉しい点です。
マイクロサービスの設計に大いに活躍するサービスだと思います。

AWSのMFAデバイスが複数登録可能になったことによるMFA強制ポリシーの変更

AWSでMFA強制をしている組織は多いと思います。
先日、従来のMFA強制ポリシーを適用したまま、新しいIAMユーザ追加を行ったときに、MFA登録ができない事象がありました。
調査したところ、昨年の以下のアップデートが影響していたようです。

aws.amazon.com

アップデート概要

  • ルートユーザとIAMユーザに複数のMFAデバイスを登録できるようになった

従来のポリシーでMFAを登録

以下のポリシーを適用したIAMユーザを登録する。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowViewAccountInfo",
            "Effect": "Allow",
            "Action": [
                "iam:GetAccountPasswordPolicy",
                "iam:ListVirtualMFADevices"
            ],
            "Resource": "*"
        },
        {
            "Sid": "AllowManageOwnPasswords",
            "Effect": "Allow",
            "Action": [
                "iam:ChangePassword",
                "iam:GetUser"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Sid": "AllowManageOwnAccessKeys",
            "Effect": "Allow",
            "Action": [
                "iam:CreateAccessKey",
                "iam:DeleteAccessKey",
                "iam:ListAccessKeys",
                "iam:UpdateAccessKey"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Sid": "AllowManageOwnSigningCertificates",
            "Effect": "Allow",
            "Action": [
                "iam:DeleteSigningCertificate",
                "iam:ListSigningCertificates",
                "iam:UpdateSigningCertificate",
                "iam:UploadSigningCertificate"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Sid": "AllowManageOwnSSHPublicKeys",
            "Effect": "Allow",
            "Action": [
                "iam:DeleteSSHPublicKey",
                "iam:GetSSHPublicKey",
                "iam:ListSSHPublicKeys",
                "iam:UpdateSSHPublicKey",
                "iam:UploadSSHPublicKey"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Sid": "AllowManageOwnGitCredentials",
            "Effect": "Allow",
            "Action": [
                "iam:CreateServiceSpecificCredential",
                "iam:DeleteServiceSpecificCredential",
                "iam:ListServiceSpecificCredentials",
                "iam:ResetServiceSpecificCredential",
                "iam:UpdateServiceSpecificCredential"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Sid": "AllowManageOwnVirtualMFADevice",
            "Effect": "Allow",
            "Action": [
                "iam:CreateVirtualMFADevice",
                "iam:DeleteVirtualMFADevice"
            ],
            "Resource": "arn:aws:iam::*:mfa/${aws:username}"
        },
        {
            "Sid": "AllowManageOwnUserMFA",
            "Effect": "Allow",
            "Action": [
                "iam:DeactivateMFADevice",
                "iam:EnableMFADevice",
                "iam:ListMFADevices",
                "iam:ResyncMFADevice"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Sid": "DenyAllExceptListedIfNoMFA",
            "Effect": "Deny",
            "NotAction": [
                "iam:CreateVirtualMFADevice",
                "iam:EnableMFADevice",
                "iam:GetUser",
                "iam:ListMFADevices",
                "iam:ListVirtualMFADevices",
                "iam:ResyncMFADevice",
                "sts:GetSessionToken"
            ],
            "Resource": "*",
            "Condition": {
                "BoolIfExists": {
                    "aws:MultiFactorAuthPresent": "false"
                }
            }
        }
    ]
}

IAMユーザでログイン後にMFAを登録しようとすると、以下のエラーが発生してMFA登録ができません。

User: arn:aws:iam::<accountID>:user/testuser01 is not authorized to perform: iam:CreateVirtualMFADevice on resource: arn:aws:iam::<accountID>:mfa/TestFMADevice because no identity-based policy allows the iam:CreateVirtualMFADevice action

変更されたポリシーでMFAを登録

以下のポリシーを適用したIAMユーザを登録する。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowViewAccountInfo",
            "Effect": "Allow",
            "Action": [
                "iam:GetAccountPasswordPolicy",
                "iam:ListVirtualMFADevices"
            ],
            "Resource": "*"
        },
        {
            "Sid": "AllowManageOwnPasswords",
            "Effect": "Allow",
            "Action": [
                "iam:ChangePassword",
                "iam:GetUser"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Sid": "AllowManageOwnAccessKeys",
            "Effect": "Allow",
            "Action": [
                "iam:CreateAccessKey",
                "iam:DeleteAccessKey",
                "iam:ListAccessKeys",
                "iam:UpdateAccessKey"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Sid": "AllowManageOwnSigningCertificates",
            "Effect": "Allow",
            "Action": [
                "iam:DeleteSigningCertificate",
                "iam:ListSigningCertificates",
                "iam:UpdateSigningCertificate",
                "iam:UploadSigningCertificate"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Sid": "AllowManageOwnSSHPublicKeys",
            "Effect": "Allow",
            "Action": [
                "iam:DeleteSSHPublicKey",
                "iam:GetSSHPublicKey",
                "iam:ListSSHPublicKeys",
                "iam:UpdateSSHPublicKey",
                "iam:UploadSSHPublicKey"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Sid": "AllowManageOwnGitCredentials",
            "Effect": "Allow",
            "Action": [
                "iam:CreateServiceSpecificCredential",
                "iam:DeleteServiceSpecificCredential",
                "iam:ListServiceSpecificCredentials",
                "iam:ResetServiceSpecificCredential",
                "iam:UpdateServiceSpecificCredential"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Sid": "AllowManageOwnVirtualMFADevice",
            "Effect": "Allow",
            "Action": [
                "iam:CreateVirtualMFADevice"
            ],
            "Resource": "arn:aws:iam::*:mfa/*"
        },
        {
            "Sid": "AllowManageOwnUserMFA",
            "Effect": "Allow",
            "Action": [
                "iam:DeactivateMFADevice",
                "iam:EnableMFADevice",
                "iam:ListMFADevices",
                "iam:ResyncMFADevice"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Sid": "DenyAllExceptListedIfNoMFA",
            "Effect": "Deny",
            "NotAction": [
                "iam:CreateVirtualMFADevice",
                "iam:EnableMFADevice",
                "iam:GetUser",
                "iam:ListMFADevices",
                "iam:ListVirtualMFADevices",
                "iam:ResyncMFADevice",
                "sts:GetSessionToken"
            ],
            "Resource": "*",
            "Condition": {
                "BoolIfExists": {
                    "aws:MultiFactorAuthPresent": "false"
                }
            }
        }
    ]
}

IAMユーザでログイン後にMFAを登録しようとすると、今後は設定画面まで進めることができ、MFA登録ができました。

MFA強制ポリシーの変更部分を比較

今回のアップデートでMFA強制ポリシーは以下の部分が変更になりました。

"Resource": "arn:aws:iam::*:mfa/${aws:username}"の末尾にある${aws:username}が * になっています。
つまり、従来はユーザ名がMFA登録の際にユーザ名が適用されていたので、この部分は問題なく動作していました。
しかし、複数デバイス登録可能になったことで、ユーザが任意の名前を入力できるようになり、今回のエラーを発生させていました。

従来のMFA強制ポリシーを適用している場合、アップデート前のMFAデバイス登録は影響を受けませんが、アップデート後にMFAデバイスを登録しようとした場合にエラーが発生します。
最新のMFA強制ポリシーは公式ドキュメントで紹介されていますのでご確認ください。

aws.amazon.com

ちなみに、削除権限の"iam:DeleteVirtualMFADevice"も削除されています。
これは、任意のデバイス名にできることで、他社からCLIなどを使った削除ができる可能性があるため、削除はさせないようにする対策だと考えられます。

まとめ

MFAデバイスが複数登録可能になったことで、MFA強制ポリシーが変更になりました。
従来のMFA強制ポリシーだと、アップデート以降はMFA登録が失敗するため、ポリシーの見直しが必要になります。
公式ドキュメントでポリシーは紹介されていますが、自社の運用に合ったポリシーを設計していただければと思います。

AWS Systems ManagerのAutomationとChange Calendarが統合されました

AWS Systems Manager AutomationとAWS Systems Manager Change Calendarが統合されました。
これにより、カレンダーで指定した期間のみAutomationの実行を許可/拒否できるようになるので、検証してみました。

アップデート概要

  • AWS Systems Manager AutomationとAWS Systems Manager Change Calendarが統合された
  • これまでは、ドキュメントでカレンダーをチェックする処理をユーザが実装する必要があったが、今回のアップデートでオートメーションとカレンダーを関連付けることで、ユーザが実装することなく手軽に統合できるようになった

aws.amazon.com

オートメーションを実行する

統合前にAutomationを実行して挙動を確認します。
テスト用のEC2インスタンスを用意します。

AWS Systems Manager」→「オートメーション」→「オートメーションの実行」を押下します。

EC2インスタンスを再起動するドキュメント「AWS-RestartEC2Instance」が標準で用意されているので、今回はそれを使用します。
入力パラメータで用意したEC2インスタンスを選択して実行します。

しばらく待つと実行が完了します。

カレンダーを設定する

それでは、カレンダーを設定して先ほどの手順を実行してみます。
「オートメーション」→「設定」→「設定」を押下します。

「Turn on Change Calendar integration」にチェックを入れます。

「Create Change Calender」を押下します。

今回は、カレンダータイプを「デフォルトで開く」にして作成します。

カレンダータイプは以下の違いがあるので、要件に応じて設定するとよいです。

  • 「デフォルトで開く」はイベント期間中は実行を拒否
     例えば、メンテナンスやリリース作業の時間帯は実行拒否したい場合に、拒否する時間帯をイベントとして登録
  • 「デフォルトで閉じる」はイベント期間中のみ実行を許可
     例えば、実行頻度が低く限られた時間帯に実行許可したい場合に、許可する時間帯をイベントとして登録

カレンダーが作成できたので、イベントを作成してみます。

今回のカレンダータイプは「デフォルトで開く」なので、イベントスケジュールの時間帯は実行を拒否できます。

イベントが作成されました。

オートメーションの画面で作成したカレンダーを選択し、保存します。

オートメーションの実行拒否を確認

先ほどオートメーションを実行した手順で、「AWS-RestartEC2Instance」を実行してみます。
すると、「Cannot Start Automation Execution because calendar(test-calender) is in CLOSED state」と表示され実行が拒否されました。

Change Managerの実行拒否を確認

AWS Systems ManagerにはChange Managerという機能があり、オートメーションに承認機能を追加したものです。
このChange Managerも同様に実行拒否されるのか確認します。

Change Managerテンプレートを作成します。
ランブックは「AWS-RestartEC2Instance」を指定します。
このテンプレートでリクエストを作成します。

ランブックのパラメータは、テスト用のEC2インスタンスを選択します。

リクエストを作成しました。

指定した承認者の画面で、「承認」を押下します。

ステータスが「完了 (エラーあり)」と表示されています。

詳細を確認すると、「Failed to start runbook due to: Cannot Start Automation Execution because calendar(test-calender) is in CLOSED state」と表示され実行が拒否されました。

まとめ

オートメーションやChange Managerは便利ですが、メンテナンスやリリース作業時には実行して欲しくない要件があると思います。
これまでは、ドキュメントでカレンダーをチェックする処理をユーザが実装する必要がありましたが、今回のアップデートで手軽に統合ができるようになりました。
本番環境に対するリスク低減や内部統制の観点でも、非常に使い勝手が良い機能だと思います。

AWS Security Hubのコントロールをワンライナーで一括無効化する

AWS Security Hubのコントロールは、必要に応じて無効化すると管理がしやすいです。
しかしながら、AWSマネジメントコンソールからAWS Security Hubのコントロールを一括無効化できません。
そこで、AWS Security Hubのコントロールワンライナーで一括無効化してみます。

コントロールを手動で無効化する

まずは手動で無効化してみます。
セキュリティ基準から任意のコントロールを選択します。

「コントロールの無効化」を押下します。

無効化する理由を記載して、「無効化」を押下します。

無効化されました。

しかしながら、AWSマネジメントコンソールからAWS Security Hubのコントロールを一括無効化できません。
そのため、複数のコントロールを無効化する際は、先ほどの手順で1つずつ無効化する必要がありますが、時間がかかりミスの原因にもなります。

コントロールワンライナーで一括無効化する

今度は、ワンライナーで一括無効化してみます。
コマンドの入力方法はいろいろありますが、今回はAWS CloudShellを使います。

今回は、「AWS 基礎セキュリティのベストプラクティス v1.0.0」のAPIGateway関連を一括無効化してみます。
以下のコマンドを実行します。
※コマンド冒頭のは自身のAWSアカウントIDに置き換えてください。

awsAccountId=<awsAccountId>; for arn in $(aws securityhub describe-standards-controls --standards-subscription-arn "arn:aws:securityhub:ap-northeast-1:$awsAccountId:subscription/aws-foundational-security-best-practices/v/1.0.0" --query "Controls[].[StandardsControlArn]" --output text); do if [[ "$arn" =~ "APIGateway" ]]; then aws securityhub update-standards-control --control-status "DISABLED" --disabled-reason "監視不要のため" --standards-control-arn "$arn"; echo "$arn is Disabled"; else :; fi; done

以下が実行結果です。
※出力結果のは、コマンド冒頭ので入力したAWSアカウントIDが入ります。

$ awsAccountId=<awsAccountId>; for arn in $(aws securityhub describe-standards-controls --standards-subscription-arn "arn:aws:securityhub:ap-northeast-1:$awsAccountId:subscription/aws-foundational-security-best-practices/v/1.0.0" --query "Controls[].[StandardsControlArn]" --output text); do if [[ "$arn" =~ "APIGateway" ]]; then aws securityhub update-standards-control --control-status "DISABLED" --disabled-reason "監視不要のため" --standards-control-arn "$arn"; echo "$arn is Disabled"; else :; fi; done
arn:aws:securityhub:ap-northeast-1:<awsAccountId>:control/aws-foundational-security-best-practices/v/1.0.0/APIGateway.1 is Disabled
arn:aws:securityhub:ap-northeast-1:<awsAccountId>:control/aws-foundational-security-best-practices/v/1.0.0/APIGateway.2 is Disabled
arn:aws:securityhub:ap-northeast-1:<awsAccountId>:control/aws-foundational-security-best-practices/v/1.0.0/APIGateway.3 is Disabled
arn:aws:securityhub:ap-northeast-1:<awsAccountId>:control/aws-foundational-security-best-practices/v/1.0.0/APIGateway.4 is Disabled
arn:aws:securityhub:ap-northeast-1:<awsAccountId>:control/aws-foundational-security-best-practices/v/1.0.0/APIGateway.5 is Disabled
arn:aws:securityhub:ap-northeast-1:<awsAccountId>:control/aws-foundational-security-best-practices/v/1.0.0/APIGateway.8 is Disabled
arn:aws:securityhub:ap-northeast-1:<awsAccountId>:control/aws-foundational-security-best-practices/v/1.0.0/APIGateway.9 is Disabled

APIGateway関連のコントロールをいくつか見てみると、無効化されていることが確認できます。

まとめ

AWSマネジメントコンソールからAWS Security Hubのコントロールを一括無効化できません。
CLIからAPI経由で無効化できるので、必要に応じて一括無効化できるようなコマンドを用意しておくと便利です。

goofysを使用したAmazon Linux2へのS3マウント

S3バケット内のオブジェクトを、CLIで手軽に操作したい時があると思います。
今回は、goofysを使用したAmazon Linux2へのS3マウントを紹介します。

goofysとは

Goで記述されたツールで、S3バケットファイルシステムとしてマウントすることができます。

github.com

goofysをセットアップする

事前準備

マウントさせるS3バケットを作成し、テスト用のファイルをアップロードします。

マウントするEC2インスタンスを作成します。

インスタンスに付与するロールには、S3を読み込みできる権限を付けておきましょう。
読み込むだけでよいなら、「AmazonS3ReadOnlyAccess」を設定するとよいでしょう。

goofsyインストール

作成したインスタンスにログインし、以下のコマンドでgoofysをインストールします。

$ sudo yum -y update
$ sudo yum install -y golang fuse
$ sudo wget https://github.com/kahing/goofys/releases/download/v0.24.0/goofys -P /usr/local/bin/
$ sudo chmod 755 /usr/local/bin/goofys

S3をマウントする

マウントポイントを作成します。

$ sudo mkdir -m 755 test-goofys-s3

続いて、fstabに追記します。

$ sudo vim /etc/fstab

以下の形式で追記します。
今回は読み込み専用でマウントします。

/usr/local/bin/goofys#<バケット名> /S3/<マウントポイント> fuse _netdev,allow_other,--dir-mode=0755,--file-mode=0644,--uid=1001,--gid=0,ro 0 0

追記後は以下のようになります。

#
UUID=<uuid>     /           xfs    defaults,noatime  1   1
/usr/local/bin/goofys#test-goofys-s3 /mnt/test-goofys-s3 fuse _netdev,allow_other,--dir-mode=0755,--file-mode=0644,--uid=1001,--gid=0,ro 0 0

fstabに追記したら保存して、マウントします

$ sudo mount -a

mountコマンドやdfコマンドでマウントされていることを確認します。

$ mount
(省略)
/mnt/test-goofys-s3 on /mnt/test-goofys-s3 type fuse (ro,relatime,user_id=0,group_id=0,default_permissions,allow_other)
(省略)
$ df -h
(省略)
test-goofys-s3  1.0P     0  1.0P   0% /mnt/test-goofys-s3
(省略)

S3バケットのオブジェクトをCLIで確認できました。

$ ls /mnt/test-goofys-s3/
test.txt   test2.txt
$ cat /mnt/test-goofys-s3/test.txt
This is test document
$ cat /mnt/test-goofys-s3/test2.txt
This is test document2

まとめ

goofysを使用することで、S3マウントが簡単に実現できました。
CLIでオブジェクトの操作ができるようになるので、grepなどで検索することが可能になり、複雑な分析にも役立ちます。
ただし、goofysはAWSのサポート外なので、本番サービスに使用するのは控え、あくまで内部利用にとどめましょう。

AWS Storage Gatewayを使用してEC2 WindowsにS3バケットをクロスアカウントでドライブマウントする

AWS Storage Gatewayを使用して、EC2に構築したWindowsに、他アカウントのS3バケットをドライブマウントしてみました。

登場するリソースが多く設定も複雑なので、まずは今回作成する構成図を示します。

アカウントAにStorage Gatewayを立てて、アカウントBのWindowからStorage Gateway経由でS3をドライブマウントします。

AWS Storage Gatewayとは

AWS Storage Gateway は、オンプレミスにあるファイルサーバなどのストレージとAmazon S3の大容量ストレージを密接に連携させ、堅牢かつ可用性の高いストレージを構築することが可能になるサービスです。

aws.amazon.com

AWS Storage Gatewayの構築

構築はアカウントAでの作業です。

バケット作成

ドライブマウントするバケットを作成します。
バケット名:test-storagegw-bucket

セキュリティグループ作成

Storage Gatewayインスタンス用のセキュリティグループを作成します。
セキュリティグループ名:test-storagegw-sg
インバウンドルール:

S3エンドポイント用のセキュリティグループを作成します。
セキュリティグループ名:test-s3endpoint-sg
インバウンドルール:

  • 「アカウントA」のVPCのIP CIDR→HTTPS(443)

エンドポイント作成

Storage Gatewayを使用して、S3バケットとプライベート通信をするために、エンドポイントを作成します。
エンドポイント名:test-s3-ep
サービス:S3 Interface型

セキュリティグループは前述の「test-s3endpoint-sg」を選択します。

ゲートウェイ作成

Storage Gatewayから「ゲートウェイ」→「ゲートウェイの作成」を押下します。

ゲートウェイ名:test-storagegw-gw
ゲートウェイタイプ:Amazon S3 ファイルゲートウェイ

ホストプラットフォームに「Amazon EC2」を選択して、「設定をカスタマイズ」→「インスタンスの起動」を押下します。
ここでインスタンスの要件が記述されており、インスタンスタイプは「m5.xlarge」、EBSは追加で「150GiB」必要になります。

インスタンス作成

インスタンス作成の画面に遷移しますので、要件通りに作成しています。
インスタンス名:test-storagegw-ec2
インスタンスタイプ:m5.xlarge

後ほどSSHログインに必要なため、キーペアを作成しておきます。
ネットワークはパブリックに配置します。
パブリックIPの自動割り当ては、後ほどElastic IPを割り当てるため無効化しておきます。
セキュリティグループは最初に作成した、「test-storagegw-sg」を選択します。

ストレージはルートボリュームに加え、150GiBのEBSボリュームを追加します。

インスタンスが起動しました。

Elastic IPを割り当てます。

インスタンスにElastic IPが割り当てられていることを確認します。

ゲートウェイ作成(続き)

ゲートウェイ作成の画面に戻り、「ゲートウェイのセットアップの確認」にチェックを付け、次に進みます。

IPアドレスに「test-storagegw-eip」のIPアドレスを入力し、次に進みます。

設定を確認し、「次へ」を押下します。

ここまでの設定がうまくいくと、「ゲートウェイの設定」画面に遷移します。
「キャッシュストレージの設定」にてローカルディスクをロードしているので1分程度待ちます。

ロードが完了したら、「CloudWatchロググループ」と「CloudWatchアラーム」の作成を選択して、「設定」を押下します。

ゲートウェイが作成されました。

注意
ゲートウェイの設定」に遷移せず、以下のようなエラーが表示された場合は、設定が間違っている可能性があります。
よくある間違いとして、入力したIPアドレスやセキュリティグループの誤りがありますので、設定を確認してみましょう。

ファイル共有作成

Storage Gatewayの「ファイル共有」→「ファイル共有の作成」を押下します。

先ほど作成したゲートウェイ「test-storagegw-gw」を選択します。
バケットは最初に作成した「test-storagegw-bucket」を入力します。
「S3のVPCエンドポイントを使用」にチェックを付けて、最初に作成したVPCエンドポイントIDを選択します。

注意
エンドポイントのサービスがS3ではなかったり、インバウンドに443が許可されていない場合、以下のようなエラーが表示されますので、設定を確認してみましょう。

オブジェクトへのアクセスは「SMB」を選択します。
監査ログ用のロググループは作成します。

Storage Gatewayは、EBS→S3の場合は即時共有されますが、S3→EBSは即時共有されません。
そこで、「S3からのキャッシュの自動更新」で更新頻度を設定します。
ここではTTL5分とします。

ストレージ設定はデフォルトのままにします。

SMBの認証方法を「ゲストアクセス」にして、「ゲストパスワード」を設定します。

最後に設定を確認して、「作成」を押下します。

作成が完了するまで1分ほど待ちます。
ステータスが「更新中」→「使用不可」→「利用可能」と遷移します。
「使用不可」と表示されても焦らず待ってみましょう。

ファイル共有が作成されると、詳細画面からドライブマウントのコマンドを取得できるので、コピーしておきましょう。

ここで、ファイル共有の作成過程で作成されたロールも確認しておきましょう。
「StorageGatewayBucketAccessRole~」というサービスロールが作成されています。
Storage GatewayがS3との共有に必要な権限が設定されています。

許可

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:GetAccelerateConfiguration",
                "s3:GetBucketLocation",
                "s3:GetBucketVersioning",
                "s3:ListBucket",
                "s3:ListBucketVersions",
                "s3:ListBucketMultipartUploads"
            ],
            "Resource": "arn:aws:s3:::test-storagegw-bucket",
            "Effect": "Allow"
        },
        {
            "Action": [
                "s3:AbortMultipartUpload",
                "s3:DeleteObject",
                "s3:DeleteObjectVersion",
                "s3:GetObject",
                "s3:GetObjectAcl",
                "s3:GetObjectVersion",
                "s3:ListMultipartUploadParts",
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": "arn:aws:s3:::test-storagegw-bucket/*",
            "Effect": "Allow"
        }
    ]
}

信頼関係

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "storagegateway.amazonaws.com"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": “<accountID>",
                    "aws:SourceArn": "arn:aws:storagegateway:ap-northeast-1:“<accountID>:gateway/<gatewayID>"
                }
            }
        }
    ]
}

挙動の確認

ドライブマウント確認

アカウントBのEC2に立てたWindowからドライブマウントをしてみます。

このWindowはElastic IPを使ってインターネット通信を行います。

ファイル共有作成後にコピーしたコマンドを適切に変更して、Windowのコマンドプロンプトで実行します。

net use Z: \\<test-storagegw-eip>\test-storagegw-bucket /user:sgw-XXXXXXXX\smbguest

パスワードを聞かれるので、ファイル共有作成時に設定した、SMBの「ゲストパスワード」を入力します。
成功すると、「The command completed successfully.」と表示されます。

エクスプローラーを見ると、ZドライブにS3がマウントされていることを確認できました。

ファイル共有確認

WindowsでZドライブにファイルを作成します。

アカウントAのS3バケットを見ると、Windowsで作成されたファイルが反映されています。

次に、アカウントAのS3バケットにファイルを作成します。

しばらく待つと、WindowsのZドライブにファイルが反映されています。
ファイル共有作成時のTTLの設定どおり、最大5分の遅延があります。

CloudWatch確認

ゲートウェイとファイル共有の作成時に、CloudWatch Logsやアラームの作成も行ったので、ここで確認しておきます。

ゲートウェイ用と、ファイル共有用のロググループが作成されています。

ゲートウェイ用のロググループです。
セットアップ時のログが記録されています。

ファイル共有用のロググループです。
先ほど、WindowsからS3にファイル共有されたことを示すログが記録されています。
S3からWindwosへのファイル共有に関するログは記録されていないようです。

こちらはCloudWatchアラームです。
ファイル共有のIOや共有失敗に関するアラームが作成されているので、必要に応じて通知を受け取れるようにするとよいでしょう。

SSH接続

Storage GatewayインスタンスへはSSH接続できます。
ただし、Storage Gateway用にカスタムされたものなので自由度は低そうですが、ここでネットワーク周りの設定や確認ができます。
ログインユーザは「admin」で、インスタンス作成時に作成したキーペアを使ってログインします。

まとめ

AWS Storage Gatewayを使用して、EC2に構築したWindowsに、他アカウントのS3バケットをドライブマウントすることができました。
作成するリソースが多く複雑なので、冒頭のような構成図を書き起こしてみることをお勧めします。
今回はAWS同士でしたが、オンプレミスや他ベンダーのクラウドサービスに立てたサーバから、S3をドライブマウントすることもできます。
ハイブリッドクラウド環境におけるデータ連携に非常に役立つ機能だと思います。

AWS CloudTrail LakeにAWS Configの情報を取り込めるようになりました

AWS CloudTrail LakeにAWS Configの情報を取り込めるようになったので、検証してみました。

aws.amazon.com

アップデート概要

AWS CloudTrail Lake は、CloudTrailログを取り込むことでSQLベースの分析を可能にします。
今回のアップデートで、Configの設定項目情報も取り込めるようになりました。
CloudTrailはユーザベースの調査を可能にする一方、Configはリソースベースの調査を可能にするため、これらを統合して分析することが可能です。

検証

イベントデータストアの作成

CloudTrail Lakeの画面から、「イベントストアの作成」を押下します。

イベントデータストアに適当な名前を付けます。

イベントタイプは「設定項目」を選択します。

内容を確認し作成します。

イベントデータストアが作成されました。

Config設定項目情報の検索

セキュリティグループ「test-sg」を作成します。

CloudTrail Lakeで以下のクエリを使って検索すると、作成したセキュリティグループに関する情報を得ることができました。

クエリ

SELECT
    eventTime, eventData.configuration, eventData.resourceId, eventData.resourceName, eventData.resourceType
FROM
    <config-event-data-store-id>
WHERE
    eventTime > '2022-12-02 16:00:00' AND eventTime < '2022-12-02 17:00:00' AND eventData.resourceName = 'test-sg'
ORDER
    BY eventTime DESC;

2つのイベントデータストアを統合して検索

事前にCloudTrail用のイベントデータストアを作成しておきます。

今度はセキュリティグループ「test2-sg」を作成します。
以下のクエリにより、CloudTrail用のイベントデータストアをとConfig設定項目情報用のイベントデータストアを統合して検索できます。
結果を見ると、CloudTrailとConfigの両方の情報が得られていることが分かります。

クエリ

SELECT
    config.eventTime, config.eventData.configuration, config.eventData.resourceId, config.eventData.resourceName, config.eventData.resourceType, userIdentity.username, trail.eventName, trail.eventSource
FROM
    <config-event-data-store-id> AS config JOIN <trail-event-data-store-id> AS trail ON config.eventData.resourceName = element_at(trail.requestParameters, 'groupName') 
WHERE
    config.eventTime > '2022-12-02 17:00:00' AND config.eventTime < '2022-12-02 18:00:00'
ORDER
    BY config.eventTime DESC;

まとめ

AWS CloudTrail LakeにAWS Configの設定項目情報を簡単に取り込み、検索することができました。
さらに、CloudTrailとConfigを統合して検索することができ、分析がしやすくなったのではないかと思います。