Do You PHP はてブロ

Do You PHPはてなからはてブロに移動しました

PECL::ssh2を使ってみる

このエントリは、Do You PHP?(www.doyouphp.jp)で公開していたコンテンツを移行/加筆/修正したものです。公開の経緯はこちらをどうぞ。目次はこちらです。

初出:2006/01/28

PHPを使ってバッチスクリプトなどを作る際、ファイル転送といえば今まではftp経由が主流(?)かと思いますが、昨今のセキュリティに対する意識向上から何らかの暗号化を施したい場合があります。2005年の初めにPECLにssh2拡張モジュールが登録されマニュアルもそれなりにこなれてきましたので、今更ながら試してみました。

今回のゴールは、PHPから公開鍵認証を行い、リモートコマンドの実行とファイル転送を行う、というものです。

インストール

まずはインストールした環境ですが、OSはCentOS6.4、PHPは以下のようなconfigureオプションを付けたPHP5.6.6です。

./configure  \
--with-apxs2=/usr/local/apache2/bin/apxs \
--prefix=/usr/local/lib/php56 \
--with-pgsql=shared,/usr/local/pgsql \
--with-gd \
--with-jpeg-dir \
--with-png-dir \
--with-xpm-dir \
--with-freetype-dir \
--enable-gd-native-ttf \
--enable-gd-jis-conv \
--enable-dom \
--with-zlib-dir \
--enable-mbstring \
--enable-pdo=shared \
--with-pdo-pgsql=shared,/usr/local/pgsql \
--with-openssl \
--with-pear=/usr/local/lib/php56/pear \
--with-pdo-sqlite=shared \
--with-curl=shared \
--with-mcrypt=shared \
--enable-intl \
--enable-opcache \
--enable-zip=shared \
--disable-ipv6 \
--with-iodbc=shared \
--with-pdo-odbc=shared,iODBC \
--enable-pcntl=shared \
--enable-soap=shared \
--with-readline

「--prefix」オプションを付けているため、phpコマンドのパスがデフォルトとは変わっていますが適宜読み替えてください。
さて、ssh2拡張モジュールのインストールですが、PHPマニュアルにもあるとおり先にopensslとlibssh2をインストールしておく必要があります。なお、最近のLinuxディストリビューションであれば、openssl-develパッケージなど用意されていると思いますので、aptなりup2dateなりyumなりでインストールした方が簡単です。今回の環境ではyumコマンドで各パッケージをインストールしました。

$ sudo yum install -y openssl openssl-devel libssh2 libssh2-devel
$ 

続いて、peclコマンドでssh2拡張モジュールをインストールします。

$ sudo /usr/local/lib/php56/bin/pecl install -a ssh2-beta
$ 

インストールが終わったら、ssh2拡張モジュールをロードするようphp.iniに追記します。

            :
extension=ssh2.so
            :

最後にモジュールが正しくロードされている事を確認します。ApachePHPで利用する場合はApacheを再起動します。

$ /usr/local/lib/php56/bin/php -m | grep ssh2
ssh2
$ 

接続の手順

接続の手順は大まかに

  1. ssh2_connect関数を使用してSSHサーバに接続
  2. 認証用の関数を使用して認証

となります。認証方法にはパスワード認証と公開鍵認証が利用できます。

パスワード認証の例

まず、パスワード認証の場合、ssh2_auth_password関数を使用します。

<?php
$connection = ssh2_connect('targethost', 22);
if (!ssh2_auth_password($connection, 'username', 'password')) {
    die('認証失敗');
}
echo "認証成功" . PHP_EOL;

公開鍵認証の例

公開鍵認証の場合、事前にRSA秘密鍵・公開鍵のペアを作成しておく必要が(当然)あります。また、Apacheと組み合わせる場合はUserディレクティブで指定したユーザー、バッチの場合はバッチを実行するユーザーでの読み込み権限が必要です。今回は、/home/httpd/.sshディレクトリを作成し、所有者をApacheのUserディレクティブでしていたnobodyユーザーにしました。使用する関数は、ssh2_auth_pubkey_fileです。
以下ざっとした手順です。targethostの/etc/ssh/sshd_configの編集が必要になる場合もありますので、適宜Googleで検索してみてください。

$ ssh-keygen -t rsa -b 2048
Generating public/private rsa key pair.
Enter file in which to save the key (/home/somebody/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/somebody/.ssh/id_rsa.
Your public key has been saved in /home/somebody/.ssh/id_rsa.pub.
The key fingerprint is:
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX somebody@client
$ 
$ sudo mkdir -p /home/httpd/.ssh
$ sudo cp -p ~somebody/.ssh/id_rsa* /home/httpd/.ssh/
$ sudo chown -R nobody /home/httpd/
$ sudo chmod go-rwx /home/httpd/.ssh/
$ 

公開鍵を接続先サーバにコピーしauthorized_keys2を作成しておきます。

$ scp /home/somebody/.ssh/id_rsa.pub targethost:./
somebody@targethost's password:
$ ssh targethost
somebody@targethost's password:
$ mkdir .ssh
$ chmod 700 .ssh/
$ mv id_rsa.pub .ssh/authorized_keys2
$ 

さて、PHPコードのほうですが、次のような感じになります。

<?php
$connection = ssh2_connect('targethost', 22, array('hostkey'=>'ssh-rsa'));

/**
 * 公開鍵・秘密鍵の読込権限に注意
 */
if (!ssh2_auth_pubkey_file($connection, 'somebody',
                          '/home/httpd/.ssh/id_rsa.pub',
                          '/home/httpd/.ssh/id_rsa',
                          'passphrase')) {
    die('認証失敗');
}
echo "認証成功" . PHP_EOL;

リモートコマンドの実行例

コマンド実行はssh2_exec関数で行いますが、結果出力を取得する場合はssh2_exec関数から返されるストリームのブロックモードを有効にしておく必要があります。

<?php
               :
/**
 * dfコマンドの実行結果を取得する
 */
$stream = ssh2_exec($connection, 'df -h ');

/**
 * ストリームのブロックモードを有効にし、ストリームからデータを読み出せる
 * 状態になるまで待つ
 */
stream_set_blocking($stream, true);

var_dump(fread($stream, 4096));

ちなみに、ここで返されるストリームはstdoutになります。このため、エラーメッセージなどを取得する場合は、コマンドの後に「2>&1」を付けてstderrと共にstdoutに出力をするか、以下のように一度stderrサブストリームを取得して、結果を取得するようになります。

<?php
               :
/**
 * 存在しないコマンドの実行結果を取得する
 */
$stdout_stream = ssh2_exec($connection, 'non-exist-command ');

/**
 * stderrサブストリームを取得する。ブロックモードへの変更も忘れずに!
 */
$stderr_stream = ssh2_fetch_stream($stdout_stream, SSH2_STREAM_STDERR);
stream_set_blocking($stdout_stream, true);

var_dump(fread($stdout_stream, 4096));
var_dump(fread($stderr_stream, 4096));

ファイルの転送例

これはPHPマニュアルどおり、コマンド一発で行えます。

<?php
              :
if (ssh2_scp_send($connection,
                  '/home/httpd/test.log',
                  '/home/shimooka/test.log', 0644)) {
    echo '転送成功';
} else {
    echo '転送失敗';
}

まとめ

前から知っていたのですが、試してみるのが今頃になってしまいました。今回試してみて公開鍵認証が問題なく行えると分かりました。これは使いどころによってはかなり強力ではないかと思います。
通常、バッチスクリプトならshスクリプトで済ませてしまうことが多いのですが、ちょっと凝ったことをするバッチなどに良いかも知れません。