システム間の連携方式でAPIを使うシーンは増えましたが、社内システムにおいてはFTPがまだまだ現役というところは多いんじゃないでしょうか。
そこでAWS Transfer for FTPを使って、S3バケットにFTPでファイルPUTできる環境を作ったのですが、どうしても「Invalid server configuration」のエラーがPUTもGETもできない・・・
そういうときはIAMのAssume Roleにハマっているかもしれません。
システム構成
今回作成するシステム構成は次のとおりです。
EC2とS3だけの構成から、FTPでS3バケットにファイルPUTするために「AWS Transfer for FTP」を構築していきます。
ただ、Transfer for FTPからAPI GatewayとLambdaが延びていますね・・・
いったい何のためにあるんでしょう?
これが掲題のエラーを引き起こした原因にも繋がってきます。
構築手順
Transfer for FTPの設定画面を見てみる
まずはAWS Transfer Familiyの画面から「サーバーを作成」をクリックしてみます。
さっそくプロトコルを選択する画面が出てきました。
今回はVPC内の通信ということで、素のFTPでファイル転送します。
IDプロバイダーを選択という画面が出てきました。
Whitepaperも読まずにとりあえず作ろうとしていたので、そもそもIDプロバイダーとは何かが分かりません・・・
FTPでファイル転送するときは当然FTPユーザ/パスワードが必要ですよね。
オンプレでWindows ServerのIISでFTPサーバを構築していたときは、OS上にユーザを用意していました。
Transfer for FTPではIDプロバイダー上でFTPユーザ/パスワードを定義するということなので、簡単にFTPがPUTできれば良いだけだから「サービスマネージド」にしようとすると・・・上の画像となりました。
メッセージの通り、非常に残念ながらFTPの場合はサービスマネージド認証が使用できないです。そのためFTPユーザ1つだけの環境の場合も、カスタムIDプロバイダーを構築する必要があります。
そうです、冒頭のシステム構成図にあったAPI GatewayとLambdaはカスタムIDプロバイダーを担っています。
ということで、ここからはWhitepaperも見ながら正規の手順で環境構築を進めていきます。
手順1.FTPユーザに割り振るIAMロールを作成する
実はここが掲題のエラーの主原因です。カスタムIDプロバイダーで定義するFTPユーザ用にIAMロールを作成する必要があり、もし同じエラーに遭遇している方がいらっしゃるなら、この設定ミスのせいでPUTやGETができていないと思われます。
Transfer for FTPはマネージドなFTPサーバを構築してくれますが、その裏には実際にアクセスしたいS3バケットがあります。
そのため、FTPサーバにはS3バケットやオブジェクトへのアクセス権限が必要ですが、具体的な権限はアクセスしてくるFTPユーザに依存します。
AバケットにアクセスできるFTPユーザを使うならFTPサーバにも同じ権限が必要だし、BバケットにアクセスできるFTPユーザなら同じ権限がFTPサーバに必要、ということです。
つまり、FTPユーザは自身が持つS3アクセス権をTransferマネージドのFTPサーバに委任(Assume)することになります。
今回は「TransferS3BacketPolicy」という名前で、次の内容のポリシーを作りました。
S3バケット名はtransfer-test-20211227です。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowListingOfUserFolder",
"Action": [
"s3:ListBucket"
],
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::transfer-test-20211227"
]
},
{
"Sid": "HomeDirObjectAccess",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:GetObjectVersion",
"s3:GetObjectACL",
"s3:PutObjectACL"
],
"Resource": "arn:aws:s3:::transfer-test-20211227/*"
}
]
}
次に、そのポリシーを持ったIAMロール「TransferS3BucketRole」を作ります。
ここで「信頼関係」タブに移り、TransferサービスにAssumeRoleできるよう信頼関係を定義します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "transfer.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
これでFTPユーザに割り振るIAMロールは作成できました。
手順2.カスタムIDプロバイダーを構築する
カスタムIDプロバイダーってどうすれば構築できるの?と言う方に、AWSがテンプレートを用意してくれています。
少し画面下にある「基本的なスタックテンプレート」のリンクをたどると、カスタムIDプロバイダーを構築するCloudFormationのテンプレートファイルがあるので、このリンクのURLをコピーしておきます。
カスタムIDプロバイダーを構築するために、CloudFormationに移って「スタックの作成」から「新しいリソースを使用(標準)」を選択します。
Amazon S3 URLに先ほどコピーしたリンクのURLを張り付け、「次へ」をクリックします。
スタックの名前は「TransferFtpCustomeIdP」にしました。
各パラメータは次のように設定して「次へ」ボタンをクリックします。
- CreateServer:SFTPサーバを同時生成するか否か。作成不要なのでfalse。
- UserHomeDirectory:FTPログイン後のホームディレクトリ。アクセスするS3バケット名を指定。
- UserName:FTPユーザ名。ここではデフォルトのmyuserを指定。
- UserPassword:FTPパスワード。ここではデフォルトのMySuperSecretPasswordを指定。
- UserPublicKey1:SSH接続する際の公開キー。SFTPではないので利用せず。デフォルトのまま。
- UserRoleArn:手順1で作成したIAMロールのArnを指定。
次の各種オプション設定画面は何も入力せず、「次へ」ボタンをクリックします。
するとレビュー画面に遷移し、末尾でCloudFormationによりIAMロールを作成することに対する承認を求められるので、チェックを入れて「スタックの作成」ボタンをクリックします。
数分待つとIAMロール、APIGateway、Lambdaの作成が完了します。
CloudFormationの出力タブにあるRole名とAPIGatewayのURLはこの直後に使うのでコピーしておいてください。
手順3.Transfer for FTPでサーバを構築する
ようやくAWS Transferに戻ってきました。
最初に止まってしまったIDプロバイダー設定画面まで進み、タイプに「カスタムIDプロバイダー」を選択します。
Amazon API Gatewayを使用して・・・のラジオボタンを選択し、手順2でコピーしたURLとロール名を入力します。
Transferのエンドポイントを設定します。
今回はVPC内部からの接続のため、タイプは「VPCでホスト」とします。
エンドポイントを作成するVPCとサブネット、エンドポイントに割り当てるセキュリティグループを選択します。
エンドポイントへのアクセスには21番、8192~8200番のポートを開ける必要があるので、セキュリティグループにはそれらに対する通信許可設定を入れておきます。
Transfer Family 用の FTP サーバは、ポート 21(コントロールチャネル)およびポート範囲 8192-8200(データチャネル)で動作します。
(出典)AWS Transfer Family ユーザガイド
次にTransferからアクセスする先のサービスを選びます。今回は「Amazon S3」とします。
最後に「追加の詳細を設定」画面が出てきますが、ここは全てデフォルト値のままとします。
「確認と作成」画面で設定値に問題なければ「サーバーを作成」ボタンをクリックします。
数分でFTPサーバが起動します。
VPCのエンドポイント画面にうつると、Transfer向けのエンドポイントが作成されていることが分かります。
画面下部のDNS名はFTP接続する際の接続先になるので、コピーしておきましょう。
FTP接続してみる
VPC内のEC2からTransfer for FTPのエンドポイントに対してFTP接続をしてみます。
TransferのFTPサーバはパッシブ接続しか受け付けない仕様となっていますが、Windows標準のFTPクライアントはアクティブ接続しかできないため、注意してください。
今回はWinSCPからFTP接続を試します。
ログイン時のホスト名にエンドポイントのDNS名を指定し、FTPユーザ名・パスワードにはカスタムIdP作成時のパラメータで指定したユーザ名・パスワード(今回はmyuser、MySuperSecretPassword)を入力し、ログインしてみます。
無事ログインできました!
ログイン後のホームディレクトリもtransfer-test-20211227のS3バケットのルートになっています。
ローカルからのファイルPUTも試してみます。
ファイルPUTも成功しました!!
マネジメントコンソールからS3バケット内を確認しましたが、PUTしたファイルがバケット内に格納されています。
掲題のエラーを再現してみる
自分が掲題のエラーにぶち当たったのは、カスタムIdP作成時のCloudFormationで指定するFTPユーザに割り当てるIAMロールとして、接続元のEC2インスタンスに割り当てているIAMロールを指定したときでした。
「ユーザのIAMロールってことは、接続元EC2のロールってことかな?」と勘違いしたんですね。
今回、意図的に異なるIAMロールを指定した上でCloudFormationのスタックを更新して、エラーを再現してみます。
数分経つとスタックが更新されました。
この状態で再度WinSCPからログインをしてみます。
Invalid server configurationのエラーが再現されました。
実はこの状態、FTPログイン自体は成功しており、ログイン後のls(ディレクトリ一覧の取得)でこけています。
FTPログインはTransfer for FTPのマネージドFTPサーバに対するもので、ここはAssume Roleしている/していないは関係ないので、ログインは成功します。
その後のlsはFTPサーバからS3バケットに対するもので、ここでAssume Roleされていないが故にアクセス権限が無く、弾かれているという状況です。
まとめ
業務上必要になったので、さらりと作ってみるかーと触れてみたところ、思わぬところでハマってしまいました。
結果的に自分がやや曖昧にしていた「権限の委任」というところを意識することができたので、個人的には思わぬ収穫となりました。
もし同じエラーで悩んでいる方の助けになれたら幸いです。