PHPでftp_put()関数などでFTP通信をする際に、

Warning: ftp_put() [function.ftp-put]: Ok to send data. in /path/to/script.php on line 99

のようなエラーに出くわすことはないだろうか。この場合、PHPが動作しているマシンのiptablesに注意が必要である。FTPログインはできたものの、相手からデータ通信のコネクションが張れないままタイムアウトしているのであろう。

通信相手のFTPサーバがアクティブモードで動作している場合、こちらから相手FTPサーバの21番ポートにコネクトしたのち、相手FTPサーバの20番ポートから、 こちらが通知した1024-65535の間のポートにコネクトしようとしてくる。

なので、こちら(PHPが動作しているマシン)のiptablesのINPUTチェーンで制限を加えている場合、INPUTチェーンでは、 1024-65535の間のポートが空いている必要が出てしまう。
これはあまりにも危険だし、その必要もない。以下のようにする。

iptables -L

を行い、INPUTチェーンに

ACCEPT  all  —  anywhere  anywhere  state RELATED,ESTABLISHED

の行があることを確認する。これは、確立済みのコネクションと、関連するコネクションを許可するものだ。なければ以下のように作る。

iptables -I INPUT 5 -p udp -m state –state ESTABLISHED,RELATED -j ACCEPT

上記、5の部分は挿入するINPUTチェーンの行番号です。

次にiptables関連のカーネルモジュールを確認する。

lsmod

を実行し、ip_conntrack_ftpと、ip_nat_ftpの行があることを確認し、ないものを入れる。それぞれ以下の通り。(多くの場合、ip_conntrack_ftpは入っている。)

modprobe ip_conntrack_ftp
modprobe ip_nat_ftp

これで、アクティブモードのFTPサーバに対して、PHPでFTP通信ができるようになった。

このままではサーバが再起動したら元に戻ってしまう。
/etc/sysconfig/iptables-config
で、使用するモジュールを以下のように設定する。

IPTABLES_MODULES=”ip_nat_ftp”

また、先のiptables設定を保存する。

service iptables save

IPTABLES_MODULES=”ip_nat_ftp