最北端で暮らすSEのknowhow

人生の最終目標は気ままな老後生活

リバースsshトンネルで遠隔地にあるubuntuをsshで操作する


遠隔地のPCはNAT内、中継サーバーはグローバルIP持ちで名前解決可能という前提です。

OS: Ubuntu 24.04
sshのversion: 9.6
ドメイン名: server.example.com
管理用ユーザー: hiro
トンネル専用ユーザー: ssh-tunnel
OS: Ubuntu 24.04
sshのversion: 9.6
autosshのversion: 1.4
ユーザー: user

下記のような感じで接続する。あくまでイメージなのでその他コマンドオプションを指定していない。このまま使わない方が良い。

    [遠隔地PC]
      | # 逆向きトンネルを作っておく
      | ssh -R 12345:localhost:22 ssh-tunnel@server.example.com
      |
      |
      | # 中継サーバーの12345番ポートで遠隔地PCのsshにログインできる
      | ssh -p 12345 user@localhost
    [中継サーバ]
      |
      |
      | # 中継サーバーは12345番をポート開放しないので
      | # sshで中継サーバーにログインして内部から接続する
      | ssh hiro@server.example.com
    [クライアントPC]

中継サーバーでの設定

はじめに中継サーバー上で最低限の設定を行う。専用ユーザーを作成して、超長ぇセキュアなパスワードを設定する。

# useradd -s /usr/sbin/nologin ssh-tunnel
# passwd ssh-tunnel

sshの機能を制限するためsshサーバーの設定に下記を追記する。

Match User ssh-tunnel
        X11Forwarding no
        AllowTcpForwarding remote
        PermitTTY no
        PermitOpen localhost:22
        ForceCommand echo 'Not permitted.'
        ClientAliveInterval 60
        ClientAliveCountMax 1

sshサービスをリロードする。

# systemctl reload ssh

遠隔地PCでの設定

遠隔地PCで中継サーバーに逆向きトンネルを常時接続させるためsshpassをインストールする。

# apt install sshpass

パスワードをスクリプトに記述するので、rootしか触れないようにパーミッション700で接続用スクリプトを作成する。rootのcronで毎分実行するので、sshpassのプロセスが存在しない場合のみ接続する。

#!/bin/bash
PASSWORD="password"
killall -0 sshpass > /dev/null 2>&1
if [ $? = 1 ]; then
    sshpass -p "${PASSWORD}" ssh -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -o "ConnectTimeout 15" -o "ExitOnForwardFailure yes" -N -R 12345:localhost:22 ssh-tunnel@server.example.com
fi

cronにジョブを登録する。

* * * * * /root/bin/connect-reverse-ssh-tunnel.sh

sshのオプション

-o “ServerAliveInterval 30” 30秒ごとにサーバーの応答を確認する。
-o “ServerAliveCountMax 3” サーバーの応答確認を最大3回まで再試行する。
-o “ConnectTimeout 15” 15秒以内に接続できなければエラー終了する。
-o “ExitOnForwardFailure yes” ポートフォワーディングが確立できなかった場合にエラー終了する。
-N リモートシェルを実行しない。
-R 12345:localhost:22 リモートホストの12345番ポートに来た接続を、localhostの22番ポートへ転送する。