pgAdmin 4のSSHトンネル地獄からの脱出:Network Errorを根絶するアーキテクトの処方箋
生粋のエンジニアなら一度は遭遇するだろう。本番環境のデータベースをセキュアに守るため、踏み台サーバー(Bastian Host)を挟み、いざpgAdmin 4からSSHトンネル経由でPostgreSQLへ接続を試みた瞬間に突きつけられる無慈悲なメッセージ:
> `Network Error`
このエラーの何がタチが悪いかといえば、原因のレイヤーがネットワーク、SSHプロトコル、鍵のフォーマット、果てはpgAdmin内部のParamiko(PythonのSSHライブラリ)の挙動まで多岐にわたる点だ。GUIのトグルをいくつかカチカチやったところで解決するものではない。
今回は、この「Network Error」の根本原因を低レイヤから解体し、ファイアウォールを完全に手なづけ、二度と接続エラーに悩まされないための極限の知見を授ける。
—
1. 内部アーキテクチャの理解:pgAdmin 4のSSHトンネルの罠
多くのエンジニアは誤解しているが、pgAdmin 4はブラウザベースのUIであっても、実態はPython(Flask)製のエンドポイントを持つWebアプリケーションサーバーである。
デスクトップ版であれサーバー版であれ、pgAdminからSSHトンネルを掘る際、内部ではPythonのSSH実装ライブラリである Paramiko がバックグラウンドで稼働している。
[ pgAdmin 4 (Browser / Client) ]
│ (HTTP/WebSocket)
▼
[ pgAdmin Server (Python / Flask) ]
│ (Paramiko SSH Tunnel) ──> [ 踏み台サーバー (SSH) ]
│ │ (TCP Port Forwarding)
│ ▼
└───────────────────────────> [ PostgreSQL (DB Server) ]
このアーキテクチャゆえに、以下の特有の障害ポイントが存在する。
1. クライアントのSSHクライアント設定(`~/.ssh/config`)がデフォルトでは無視される(pgAdmin独自の実装に依存するため)。
2. Paramikoの古いバージョンや実装仕様による秘密鍵のパーミッションチェックの厳格さ。
3. 長時間のクエリ実行やアイドル状態におけるTCPタイムアウト(NAT/ファイアウォールの断絶)。
—
2. 頻出原因その1:秘密鍵(Private Key)のパーミッションとフォーマット
「ローカルのターミナルから `ssh -i key.pem user@bastion` では入れるのに、pgAdminだとNetwork Errorになる」という現象の8割はこれだ。
対策:OpenSSH形式への強制変換とパーミッションの厳守
Paramikoは、レガシーなPEM形式(RSA旧形式)や、一部のPuTTY形式(`.ppk`)、あるいはOS依存のパーミッション緩慢さを極端に嫌う。
1. パーミッションの鉄則
Linux/macOS環境であれば、秘密鍵のパーミッションは必ず `600` に設定すること。
chmod 600 ~/.ssh/id_rsa_bastion
2. PEM形式(PKCS#1 / PKCS#8)への確実な変換
もし鍵が最新のOpenSSH形式(`BEGIN OPENSSH PRIVATE KEY`)で、Paramikoのバージョン起因で弾かれている場合は、レガシーなRSAフォーマットに明示的にダウングレード・再生成するのが最も確実だ。
ssh-keygen -p -m PEM -f ~/.ssh/id_rsa_bastion
—
3. 頻出原因その2:ファイアウォール越えとキープアライブ(KeepAlive)の欠如
「接続直後は成功するが、少し放置するとクエリ実行時に `Network Error` または `Connection reset by peer` になる」場合、これは間違いなくファイアウォールまたはクラウドのロードバランサー(AWS ALB/NLB等)によるアイドルタイムアウトが原因だ。
ステートフルなファイアウォールは、一定時間パケットの往来がないTCPコネクションを容赦なく切断する。SSHトンネルのセッションが切断されれば、その上を通っているPostgreSQLとの通信も即死する。
対策:Paramiko層へのKeepAlive強制挿入
pgAdmin 4のGUIには細かいSSHのKeepAlive設定項目が隠されている場合がある。これを確実に行うためには、pgAdminが内部で参照する設定ファイルをハックするか、SSHのグローバルなフォールバックを利用する。
しかし、最も確実なアプローチは、pgAdminに頼らずローカルで常時接続のSSHポートフォワーディング(ローカルフォワード)を張ることだ。プロフェッショナルな現場では、GUIのビルトイン機能に依存せず、CLIでトンネルを確立してからpgAdminで「直結」するのが定石である。
—
4. プロの選択:CLIによる堅牢なSSHポートフォワーディングとpgAdmin接続
不安定なpgAdminのビルトインSSHトンネル機能を捨て、ローカルのポート(例: `15432`)を踏み台経由でDBに直結させる。この構成が最も堅牢であり、CI/CDツールや他のDBクライアント(DBeaver等)とも流用できる。
以下のシェルスクリプト(またはsystemdサービス、tmuxセッション)を用いて、自動再接続(Auto-reconnect)機能付きの頑強なトンネルを構築せよ。
頑強なSSHトンネル確立スクリプト (`secure-tunnel.sh`)
!/bin/bash
==========================================
堅牢なSSHポートフォワーディング・スクリプト
==========================================
踏み台サーバーの設定
BASTION_HOST=”bastion.example.com”
BASTION_USER=”ec2-user”
SSH_KEY=”$HOME/.ssh/id_rsa_bastion”
内部DBサーバーの設定
DB_HOST=”internal-db.local”
DB_PORT=”5432″
ローカル側のフォワード先ポート
LOCAL_PORT=”15432″
echo “==> Establishing secure SSH tunnel to ${DB_HOST} via ${BASTION_HOST}…”
オプション解説:
-N : リモートコマンドを実行しない(ポートフォワード専用)
-T : 疑似端末の割り当てをしない
-o ServerAliveInterval=30 : 30秒ごとにKeepAliveパケットを送信し、切断を防止
-o ServerAliveCountMax=3 : KeepAliveの応答がない許容回数(計90秒で切断判定)
-o ExitOnForwardFailure=yes : ポートフォワードに失敗したら即座に終了
-L : ローカルポート -> リモートホスト:ポート
while true; do
ssh -i “${SSH_KEY}” \
-N -T \
-o ServerAliveInterval=30 \
-o ServerAliveCountMax=3 \
-o ExitOnForwardFailure=yes \
-L “${LOCAL_PORT}:${DB_HOST}:${DB_PORT}” \
“${BASTION_USER}@${BASTION_HOST}”
echo “[WARN] SSH tunnel disconnected. Reconnecting in 5 seconds…”
sleep 5
done
pgAdmin側の接続設定
上記のスクリプトを実行した状態で、pgAdmin 4からは以下のように設定する。
- Host name/address: `127.0.0.1` (または `localhost`)
- Port: `15432`
- Maintenance database: 接続対象のDB名
- Username: PostgreSQLのユーザー名
- SSH Tunnelタブ: 「Use SSH tunnel」のチェックは外す(ここが重要)。すでにローカルでトンネルが確立されているため、pgAdminからは通常のローカル接続として振る舞わせる。
この構成をとることで、pgAdminのParamiko起因のメモリリーク、予期せぬ切断、鍵のパーミッションエラーといった無駄なデバッグ工数から完全に解放される。
—
5. アーキテクトの最終提言:なぜ「ビルトイン機能」を過信してはならないのか
pgAdmin 4は優れたDB管理ツールだが、その本質は「Webベースの汎用管理クライアント」である。インフラストラクチャの複雑性(SSHのハンドシェイク、ファイアウォールのステート維持、鍵の暗号化方式の差異)を、単一のWebアプリケーションのGUI内部にカプセル化しようとすること自体に無理がある。
真にスケーラブルで堅牢なデータパイプラインおよびインフラ管理を目指すのであれば:
1. ネットワーク層の制御(トンネリング)はSSHクライアント(OpenSSH)やVPN(WireGuard等)に任せる。
2. データベースクライアント(pgAdmin)は純粋に「TCPの向こう側のDBを叩く仕事」に専念させる。
この責務分離(Separation of Concerns)を徹底することこそが、障害耐性を高め、開発チーム全体の認知負荷を劇的に下げる唯一にして最大の近道である。明日から「Network Error」に怯える日々を終わらせよう。