CakePHP：ssh越しにpostgresqlと接続する

CakePHPで利用するpostgresqlは通常、Webサーバから直接（ポート5432）経由で接続できる必要があります。
データベースサーバがリモートにあり、ポート5432が空いてない場合は接続できません。

対策としてsshのフォーワーディングを利用します。sshのフォワーディングはcron等で起動してもいいのですが（そちらのほうが自然かな…）
ここではcakephpで行うことにします。まず、次のファイルをSshPostgres.phpとして作成してください：

```app/Model/Datasource/Database/SshPostgres.php
<?php
App::uses('Postgres', 'Model/Datasource/Database');

class SshPostgres extends Postgres {
        public $description = "Postgres over SSH";

        protected $_baseConfig = array(
                'persistent'            =>  true,
                'host'                  => 'localhost',
                'login'                 => 'root',
                'password'              => '',
                'database'              => 'cake',
                'port'                  => '5432',
                'rsa'                   => '',
                'remote_username'       => '',
                'remote_ip'             => '',
                'sslmode'               => 'allow',
                'schema'                => 'public',
                'flags'                 => array()
        );

        protected $_connection = null;
        protected $_useAlias = true;

        public function connect() {

                $config = $this->config;
                $this->connected = false;

                $flags = $config['flags'] + array(
                        PDO::ATTR_PERSISTENT => $config['persistent'],
                        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
                );

                try {
                        if (empty($config['unix_socket'])) {
                                $dsn = "pgsql:host={$config['host']};port={$config['port']};dbname={$config['database']};sslmode={$config['sslmode']}";
                        } else {
                                $dsn = "pgsql:unix_socket={$config['unix_socket']};dbname={$config['database']}";
                        }

                        $this->_connection = new PDO( $dsn, $config['login'], $config['password'], $flags);
                        $this->connected = true;
                } catch (PDOException $e) {
                        $cmd="ssh -i {$config['rsa']} {$config['remote_username']}@{$config['remote_ip']} -L5433:{$config['host']}:5432 -N > /dev/null &";
                        shell_exec($cmd);
                        sleep(3);
                }


                try {
                        if (empty($config['unix_socket'])) {
                                $dsn = "pgsql:host={$config['host']};port={$config['port']};dbname={$config['database']};sslmode={$config['sslmode']}";
                        } else {
                                $dsn = "pgsql:unix_socket={$config['unix_socket']};dbname={$config['database']}";
                        }

                        $this->_connection = new PDO( $dsn, $config['login'], $config['password'], $flags);
                        $this->connected = true;
                } catch (PDOException $e) {
                        throw new MissingConnectionException(array(
                                'class' => get_class($this),
                                'message' => $e->getMessage()
                        ));
                }

                return $this->connected;
        }
}
```

肝が幾つかあります：

1. shell_execでsshを実行する場合、出力を/dev/nullにリダイレクトしバックグラウンド実行する（＆）
2. shell_execで即時にsshのセッションが作成できるわけではないのでsleep(3)を入れてスリープする。これにより、次のtryが成功するようになる

app/Config/database.phpは次のようにします：

```database.php
<?php
class DATABASE_CONFIG {

        public $default = array(
                'datasource'      => 'Database/SshPostgres',
                'persistent'      => false,
                'host'            => 'localhost',
                'port'            => '5433',
                'login'           => 'postgres',
                'password'        => '',
                'database'        => 'リモートのpostgresqlのデータベース名',
                'rsa'             => '/var/www/.ssh/id_rsa',
                'remote_username' => 'root',
                'remote_ip'       => 'postgresqlが稼働しているリモートホストのホスト名',
                'prefix'          => '',
                'encoding'        => 'utf8',
        );
?>
```

ここで重要なのは次の点です：
1. Webサーバの権限で閲覧可能な場所に秘密鍵を置く（私の場合はCentosでapacheなので/var/wwwが$HOME）
2. 公開鍵ペアをssh-keygenで作成後、実際にapacheユーザとして接続をコマンドラインから試みる：

```
su -m apache
ssh -i /var/www/.ssh/id_rsa root@リモートホスト
```

目的はknown_hostsにエントリを追加することです。

以上で設定は完了です。