VPC内のEC2インスタンスから外部のFTPサーバとデータ転送するときに気をつけること
2006年に就職した下條です。世代的と言ったら言い訳になってしまいますが、私はFTPを仕事できちんと使ったことはほとんどありません。個人的にレンタルサーバを使うなどでFTPを使うことは多々ありアスキーモードとバイナリモードなどは知っていましたが、プロトコルについてはよく把握していませんでした。
ただ最近、AWSのVPC内のEC2インスタンスから外部のFTPサーバにファイルをアップロードする機会があり、ちょっとハマってしまいました。
今回は、VPC内のEC2インスタンスから外部のFTPサーバに対してアクティブモードでファイル転送する際の注意事項を2点をご紹介いたします。
※これはVPC内のEC2インスタンスに限らず、NAT越しにFTPする際には発生する基本的な話ですが、近年のFTPユーザの減少とAWSユーザの増加を踏まえて記事にする意味はあるかと考えました。
まず、大前提としてFTPにはアクティブモードとパッシブモードがあります。アクティブモードとパッシブモードについてはいろいろなところで説明がありますのでここで詳細に説明することは省略しますが、データ転送時のフローにおいて、
- アクティブモードでは、クライアントがPORTコマンドで接続を待つポートを指定し、サーバからクライアントの指定ポートに対してコネクションを張る。
- パッシブモードでは、PASVコマンドをサーバに送信し、サーバがポートをクライアントに通知、クライアントからサーバのそのポートに対してコネクションを張る。
という違いがあります。
問題になるのはアクティブモードです。アクティブモードでの要注意点は以下です。
- FTPサーバからクライアントに対してコネクションを張る。
- FTPサーバからクライアントへのコネクションは、クライアントからPORTコマンドで指定された接続先に接続する。
これを踏まえると、EC2からのFTPデータ転送では以下の2つの対応が必要となります。
FTPサーバからEC2インスタンスへのポート開放
アクティブモードの場合にはFTPサーバからEC2インスタンスへのコネクションが発生しますので、ネットワーク的に通ることが大前提です。AWSのセキュリティグループ、ネットワークACL、もしくはホストのiptablesなどで適切にポートを開放する必要があります。もしセキュリティ的に開放できないのであれば、アクティブモードでのデータ転送はできません。
EC2インスタンスからFTPサーバへのPORTコマンド
FTPではPORTコマンドを使ってクライアントからサーバに、クライアントのIPアドレス・ポート番号を教えるという動作になっています。しかし、EC2インスタンスはプライベートIPをFTPサーバに教えてしまいます。FTPサーバはそのプライベートIPでEC2インスタンスにアクセスしようとして、繋がらないという話になります。
エラーメッセージはこんな感じです。メッセージを見るとPORTコマンドでプライベートIPを通知していることが分かります。
ftp> put hoge.txt /hoge/hoge.txt
local: hoge.txt remote: /hoge/hoge.txt
ftp: setsockopt (ignored): 許可がありません
---> PORT 172,16,10,10,136,111
500 Invalid PORT Command.
ftp: bind: アドレスは既に使用中です
対処としては、インスタンスにEIPを付け、EIPをPORTコマンドで通知するようにするのが簡単です。もしくは動的にインスタンスのパブリックIPを取得し、それをPORTコマンドで通知することもできると思います。ちなみに、EC2インスタンスのパブリックIPアドレスは以下のコマンドで取得できます。
これを踏まえてファイル転送するとなった場合の話ですが、例えばRubyの場合、標準のnet/ftpライブラリにはPORTコマンドでの指定機能が備わっていないのですが、それを拡張した net-ftp-port_commandというgemを使うと非常に簡単に実現できます。
参考URL:
EC2からactive modeでFTP通信ができない件
Net::FTPでNAT越えする
以上、FTPの基礎的な話でした。今後あまりFTPを使う機会はないと思いますが、心に留めておきたいと思います。