顧客フロントSEのIT勉強ブログ

2022 Japan AWS Top Engineer / 2022-23 Japan AWS Certifications Engineer。AWS認定12冠、情報処理試験全冠。顧客フロントSEがなるべく手を動かしながらIT技術を学んでいくブログです。

【AWS Tips】AWS Transfer for FTPの「Invalid server configuration」エラーでPUT/GETできない場合の対処法

f:id:se_o_chan:20211228012915p:plain

システム間の連携方式で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が延びていますね・・・
いったい何のためにあるんでしょう?
これが掲題のエラーを引き起こした原因にも繋がってきます。

f:id:se_o_chan:20211227223723p:plain

構築手順

Transfer for FTPの設定画面を見てみる

まずはAWS Transfer Familiyの画面から「サーバーを作成」をクリックしてみます。

f:id:se_o_chan:20211227224356p:plain

さっそくプロトコルを選択する画面が出てきました。
今回はVPC内の通信ということで、素のFTPでファイル転送します。

f:id:se_o_chan:20211227224522p:plain

IDプロバイダーを選択という画面が出てきました。
Whitepaperも読まずにとりあえず作ろうとしていたので、そもそもIDプロバイダーとは何かが分かりません・・・

f:id:se_o_chan:20211227225000p:plain

FTPでファイル転送するときは当然FTPユーザ/パスワードが必要ですよね。
オンプレでWindows ServerのIISFTPサーバを構築していたときは、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)することになります。

f:id:se_o_chan:20211227233733p:plain

今回は「TransferS3BacketPolicy」という名前で、次の内容のポリシーを作りました。
S3バケット名はtransfer-test-20211227です。

f:id:se_o_chan:20211227235458p:plain

{
    "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」を作ります。

f:id:se_o_chan:20211227235644p:plain

ここで「信頼関係」タブに移り、TransferサービスにAssumeRoleできるよう信頼関係を定義します。

f:id:se_o_chan:20211227235845p:plain

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "transfer.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

これでFTPユーザに割り振るIAMロールは作成できました。

手順2.カスタムIDプロバイダーを構築する

カスタムIDプロバイダーってどうすれば構築できるの?と言う方に、AWSがテンプレートを用意してくれています。

docs.aws.amazon.com

少し画面下にある「基本的なスタックテンプレート」のリンクをたどると、カスタムIDプロバイダーを構築するCloudFormationのテンプレートファイルがあるので、このリンクのURLをコピーしておきます。

カスタムIDプロバイダーを構築するために、CloudFormationに移って「スタックの作成」から「新しいリソースを使用(標準)」を選択します。

f:id:se_o_chan:20211227230335p:plain

Amazon S3 URLに先ほどコピーしたリンクのURLを張り付け、「次へ」をクリックします。

f:id:se_o_chan:20211227230541p:plain

スタックの名前は「TransferFtpCustomeIdP」にしました。
各パラメータは次のように設定して「次へ」ボタンをクリックします。

  • CreateServer:SFTPサーバを同時生成するか否か。作成不要なのでfalse。
  • UserHomeDirectory:FTPログイン後のホームディレクトリ。アクセスするS3バケット名を指定。
  • UserName:FTPユーザ名。ここではデフォルトのmyuserを指定。
  • UserPassword:FTPパスワード。ここではデフォルトのMySuperSecretPasswordを指定。
  • UserPublicKey1:SSH接続する際の公開キー。SFTPではないので利用せず。デフォルトのまま。
  • UserRoleArn:手順1で作成したIAMロールのArnを指定。

f:id:se_o_chan:20211228000201p:plain

次の各種オプション設定画面は何も入力せず、「次へ」ボタンをクリックします。
するとレビュー画面に遷移し、末尾でCloudFormationによりIAMロールを作成することに対する承認を求められるので、チェックを入れて「スタックの作成」ボタンをクリックします。

数分待つとIAMロール、APIGateway、Lambdaの作成が完了します。
CloudFormationの出力タブにあるRole名とAPIGatewayのURLはこの直後に使うのでコピーしておいてください。

f:id:se_o_chan:20211228001435p:plain

手順3.Transfer for FTPでサーバを構築する

ようやくAWS Transferに戻ってきました。

最初に止まってしまったIDプロバイダー設定画面まで進み、タイプに「カスタムIDプロバイダー」を選択します。
Amazon API Gatewayを使用して・・・のラジオボタンを選択し、手順2でコピーしたURLとロール名を入力します。

f:id:se_o_chan:20211228003937p:plain

Transferのエンドポイントを設定します。
今回はVPC内部からの接続のため、タイプは「VPCでホスト」とします。
エンドポイントを作成するVPCとサブネット、エンドポイントに割り当てるセキュリティグループを選択します。

f:id:se_o_chan:20211228002325p:plain

エンドポイントへのアクセスには21番、8192~8200番のポートを開ける必要があるので、セキュリティグループにはそれらに対する通信許可設定を入れておきます。

Transfer Family 用の FTP サーバは、ポート 21(コントロールチャネル)およびポート範囲 8192-8200(データチャネル)で動作します。

(出典)AWS Transfer Family ユーザガイド

次にTransferからアクセスする先のサービスを選びます。今回は「Amazon S3」とします。

f:id:se_o_chan:20211228002730p:plain

最後に「追加の詳細を設定」画面が出てきますが、ここは全てデフォルト値のままとします。
「確認と作成」画面で設定値に問題なければ「サーバーを作成」ボタンをクリックします。
数分でFTPサーバが起動します。

f:id:se_o_chan:20211228003006p:plain

VPCのエンドポイント画面にうつると、Transfer向けのエンドポイントが作成されていることが分かります。
画面下部のDNS名はFTP接続する際の接続先になるので、コピーしておきましょう。

f:id:se_o_chan:20211228003437p:plain

FTP接続してみる

VPC内のEC2からTransfer for FTPのエンドポイントに対してFTP接続をしてみます。

TransferのFTPサーバはパッシブ接続しか受け付けない仕様となっていますが、Windows標準のFTPクライアントはアクティブ接続しかできないため、注意してください。

今回はWinSCPからFTP接続を試します。
ログイン時のホスト名にエンドポイントのDNS名を指定し、FTPユーザ名・パスワードにはカスタムIdP作成時のパラメータで指定したユーザ名・パスワード(今回はmyuser、MySuperSecretPassword)を入力し、ログインしてみます。

f:id:se_o_chan:20211228005819p:plain

無事ログインできました!
ログイン後のホームディレクトリもtransfer-test-20211227のS3バケットのルートになっています。

f:id:se_o_chan:20211228005904p:plain

ローカルからのファイルPUTも試してみます。

f:id:se_o_chan:20211228010016p:plain

ファイルPUTも成功しました!!
マネジメントコンソールからS3バケット内を確認しましたが、PUTしたファイルがバケット内に格納されています。

f:id:se_o_chan:20211228010115p:plain

f:id:se_o_chan:20211228011247p:plain

掲題のエラーを再現してみる

自分が掲題のエラーにぶち当たったのは、カスタムIdP作成時のCloudFormationで指定するFTPユーザに割り当てるIAMロールとして、接続元のEC2インスタンスに割り当てているIAMロールを指定したときでした。

「ユーザのIAMロールってことは、接続元EC2のロールってことかな?」と勘違いしたんですね。

今回、意図的に異なるIAMロールを指定した上でCloudFormationのスタックを更新して、エラーを再現してみます。

f:id:se_o_chan:20211228010651p:plain

数分経つとスタックが更新されました。

f:id:se_o_chan:20211228010759p:plain

この状態で再度WinSCPからログインをしてみます。
Invalid server configurationのエラーが再現されました。

f:id:se_o_chan:20211228010846p:plain

実はこの状態、FTPログイン自体は成功しており、ログイン後のls(ディレクトリ一覧の取得)でこけています。
FTPログインはTransfer for FTPのマネージドFTPサーバに対するもので、ここはAssume Roleしている/していないは関係ないので、ログインは成功します。
その後のlsはFTPサーバからS3バケットに対するもので、ここでAssume Roleされていないが故にアクセス権限が無く、弾かれているという状況です。

まとめ

業務上必要になったので、さらりと作ってみるかーと触れてみたところ、思わぬところでハマってしまいました。

結果的に自分がやや曖昧にしていた「権限の委任」というところを意識することができたので、個人的には思わぬ収穫となりました。

もし同じエラーで悩んでいる方の助けになれたら幸いです。