はじめに
業務で踏み台経由でDBへ接続する必要があった時やったこと。
PDOはSSH接続する方法はないっぽいので、SSHトンネルをバックグランドで実行しといて、
それ宛にPDOで接続する。
バックグランド実行の方法はこちら
環境
[クライアント] ---- [ 踏み台サーバー] ---- [データベース]
・Mac ・ユーザー ・アクセスは踏み台からのみ許可
・PHP ・パスワード
・秘密鍵
#コード
バックグラウンドでSSHトンネリングの実行
-N 指定してリモートでコマンド実行できないようにしています。
停止のときに使うため、変数に格納しています。
php
$cmd = "ssh -f -N -L 13306:dbhost:3306 sshuser@sshhost -i key/id_rsa";
exec($cmd);
[クライラント] -> [このトンネルへ13306で接続] -> [sshuser@sshhostとしてデータベースの3306へ転送] -> [データベース]
PDO接続
SSHトンネリングを実行した状態で。
php
//ローカルの13306へ向けて
$dsn = 'mysql:dbname=db;host=127.0.0.1;port=13306';
$user = 'root';
$password = 'root';
try {
$dbh = new PDO($dsn, $user, $password);
echo "接続成功\n";
} catch (PDOException $e) {
echo "接続失敗: " . $e->getMessage() . "\n";
exit();
}
SSHトンネリングの停止
php
//開始時に使ったコマンドを元に調べる
$_cmd = "ps aux | grep '[0-9] ".$cmd."' | awk '{print $2}'";
exec( $_cmd ,$output ,$return_var );
//$ouput[0]にPID入っているのでkill
exec('kill '.$output[0]);
実装
DB設定やらポート設定はかくじで。
SSHTunneling.php
class SSHTunneling{
private static $pids = array();
private static $cmd;
public static function start(){
self::$cmd = 'ssh -f -N -L '.$tunnel_port.':'.DBConfig::$host_name.':'.DBConfig::$port.' '.$ssh_user.'@'.$ssh_host.' -i '.$private_key;
echo "SSHトンネリングを開始します。".PHP_EOL;
exec( self::$cmd ,$output ,$return_var );
if( $return_var != 0) throw new Exception( $output[0] );
self::set_pid();
}
private function set_pid(){
$_cmd = "ps aux | grep '[0-9] ".self::$cmd."' | awk '{print $2}'";
exec( $_cmd ,$outputs ,$return_var );
if( $return_var != 0) throw new Exception( $outputs[0] );
foreach ((array)$outputs as $output) {
self::$pids[] = $output;
}
}
public static function stop(){
echo "SSHトンネリングを終了します".PHP_EOL;
foreach (self::$pids as $pid) {
$_cmd = 'kill '.$pid;
exec( $_cmd ,$output ,$return_var );
if( $return_var != 0) throw new Exception( $output[0] );
}
}
}
php
$dsn = 'mysql:dbname=db;host=127.0.0.1;port='.$tunnel_port;
$db_user = 'root';
$db_password = 'root';
try {
SSHTunneling::start();
$dbh = new PDO($dsn, $db_user , $db_password);
echo "接続成功\n";
} catch (PDOException $e) {
echo "接続失敗: " . $e->getMessage() . "\n";
}finally{
SSHTunneling::stop();
exit;
}