OpenSSH RemoteForwardに動的に割り当てられたポートの決定

OpenSSH RemoteForwardに動的に割り当てられたポートの決定

質問(TL;DR)

リモート転送用にポート(-Rオプションとも呼ばれる)が動的に割り当てられている場合、リモートシステムのスクリプト(たとえば、から始まる.bashrc)がOpenSSHでどのポートを選択するかを決定しますか?


背景

私はOpenSSH(両端)を使用して他の複数のユーザーと共有する中央サーバーに接続します。私のリモートセッション(現在)の場合は、X、cup、およびpulseaudioを渡したいと思います。

最も簡単な方法は、-Xこのオプションを使用してXを渡すことです。割り当てられたXアドレスは環境変数に保存されDISPLAY、ほとんどの場合、対応するTCPポートを確認できます。しかし、XlibはDISPLAY

カップとパルスオーディオにも同様のメカニズムが必要です。これら2つのサービスの基本は、それぞれCUPS_SERVER環境変数との形で存在しますPULSE_SERVER。使用例は次のとおりです。

ssh -X -R12345:localhost:631 -R54321:localhost:4713 datserver

export CUPS_SERVER=localhost:12345
lowriter #and I can print using my local printer
lpr -P default -o Duplex=DuplexNoTumble minutes.pdf #printing through the tunnel
lpr -H localhost:631 -P default -o Duplex=DuplexNoTumble minutes.pdf #printing remotely

mpg123 mp3s/van_halen/jump.mp3 #annoy co-workers
PULSE_SERVER=localhost:54321 mpg123 mp3s/van_halen/jump.mp3 #listen to music through the tunnel

問題はそれを設定CUPS_SERVERしてPULSE_SERVER正しく設定することです。

ポート転送が多いため、動的ポート割り当てが必要です。静的ポート割り当てはオプションではありません。

0OpenSSHには、リモート転送用のバインドポートを指定して(-Rオプション)、リモートサーバーにポートを動的に割り当てるメカニズムがあります。次のコマンドを使用して、OpenSSHはカップとパルス転送用のポートを動的に割り当てます。

ssh -X -R0:localhost:631 -R0:localhost:4713 datserver

このコマンドを使用すると、次のものがssh印刷されますSTDERR

Allocated port 55710 for remote forward to 127.0.0.1:4713
Allocated port 41273 for remote forward to 127.0.0.1:631

私が欲しい情報があります!最終的に私は以下を生成したいと思います。

export CUPS_SERVER=localhost:41273
export PULSE_SERVER=localhost:55710

ところで、「ポート割り当て済み...」メッセージがローカルコンピュータに生成され、リモートSTDERRコンピュータからアクセスできない場所に送信されました。奇妙なことは、OpenSSHがポート転送に関する情報を取得する方法がないようです。

その情報を取得し、それをシェルスクリプトに入れて完全に設定CUPS_SERVERし、PULSE_SERVERリモートホストに配置するにはどうすればよいですか?


行き止まりの路地

私が見つけることができる唯一の簡単な方法は、sshdログから情報を読むことができるまで長い情報を追加することです。これは、root以外のユーザーが合理的にアクセスできるよりもはるかに多くの情報を公開するため、実現可能ではありません。

内部構造の良い表現を印刷する追加のエスケープシーケンスをサポートするためにOpenSSHをパッチするつもりですがpermitted_opens、それが私が望むものであっても、まだサーバーからクライアント側のエスケープシーケンスにアクセスするスクリプトを作成することはできません。横。


もっと良い方法があります。

次の方法は非常に不安定なようで、ユーザーごとに1つのSSHセッションに制限されています。しかし、私には複数の同時セッションが必要で、他のユーザーにはより多くのセッションが必要です。しかし試してみました...

星が正しく並べ替えられたら、鶏の1〜2匹を犠牲にしてsshd私のユーザーとして起動するのではなく、代わりに成功したログイン後に権限を放棄して次のことを悪用する可能性があります。

  • 私のユーザーに属するすべてのリスニングソケットのポート番号のリストを取得します。

    netstat -tlpen | grep ${UID} | sed -e 's/^.*:\([0-9]\+\) .*$/\1/'

  • 私のユーザーが開始したプロセスに属するすべてのリスニングソケットのポート番号のリストを取得します。

    lsof -u ${UID} 2>/dev/null | grep LISTEN | sed -e 's/.*:\([0-9]\+\) (LISTEN).*$/\1/'

  • 最初のセットにはあるが2番目のセットにはないすべてのポートが転送されたポートである可能性が高く、実際にセットを引くとそれぞれ、41273およびXが生成されます。557106010

  • 6010DISPLAYXで識別されたポートを使用してください。

  • 41273lpstat -h localhost:41273 -aリターンのためカップポートです0
  • 55710pactl -s localhost:55710 statリターンのためパルスポートです0。 (私のクライアントのホスト名も印刷します!)

(集合減算を実行するには、sort -u上記のコマンドラインからの出力を保存し、それを使用してcomm減算を実行します。)

Pulseaudioを使用すると、クライアントを識別でき、すべての意図と目的のために、分離する必要があるSSHセッションを分離するためのアンカーポイントとして機能します。しかし、バインドする方法41273と同じプロセス55710が見つかりませんでした。この情報は、root以外のユーザーには公開されません。私が読みたい列には1つだけが表示されます(この特定の例では)。非常に近い...6010sshdnetstat-PID/Program name2339/54

答え1

残念ながら、以前はあなたの質問を見つけることができませんでしたが、ちょうどkamil-maciorowskiから本当に良い答えを受けました。

https://unix.stackexchange.com/a/584505/251179

要約すると、最初にデフォルト接続を確立し、それをバックグラウンドにしたままにしてから、2番目のコマンドを実行してポート転送を要求/設定するように-O *ctl_cmd*設定します。forward

ssh -fNMS /path/to/socket user@server

port="$(ssh -S /path/to/socket -O forward -R 0:localhost:22 placeholder)"

$portこれにより、ローカルコンピュータとバックグラウンド接続が提供されます。

その後、$portローカルで使用したり、ssh同じ制御ソケットを使用できるリモートサーバーでコマンドを再実行したりできます。

フラグは次のとおりです。

  • - F= sshにバックグラウンドエントリをリクエストしてください。
  • -N= リモートコマンドを実行しないでください。
  • -中=接続共有のためにクライアントを「マスター」モードに切り替えます。
  • -S= 共有接続に使用される制御ソケットの位置。
  • - モールド= 基本プロセスでアクティブな接続の再利用を制御します。

私の場合、接続を確認するためにいくつかの項目を追加しました。

#!/bin/bash

#--------------------------------------------------
# Setup
#--------------------------------------------------

  set -u;

  tunnel_user="user";
  tunnel_host="1.1.1.1";

  local_port="22";
  local_name="my-name";

  path_key="$HOME/.ssh/tunnel_ed25519";

  path_lock="/tmp/tunnel.${tunnel_host}.pid"
  path_port="/tmp/tunnel.${tunnel_host}.port"
  path_log="/tmp/tunnel.${tunnel_host}.log"
  path_socket="/tmp/tunnel.${tunnel_host}.socket"

#--------------------------------------------------
# Key file
#--------------------------------------------------

  if [ ! -f "${path_key}" ]; then

    ssh-keygen -q -t ed25519 -f "${path_key}" -N "";

    /usr/local/bin/tunnel-client-key.sh
      # Sends the public key to a central server, also run via cron, so it can be added to ~/.ssh/authorized_keys
      # curl -s --form-string "pass=${pass}" --form-string "name=$(local_name)" -F "key=@${path_key}.pub" "https://example.com/key/";

  fi

#--------------------------------------------------
# Lock
#--------------------------------------------------

  if [ -e "${path_lock}" ]; then
    c=$(pgrep -F "${path_lock}" 2>/dev/null | wc -l);
      # MacOS 10.15.4 does not support "-c" to count processes, or the full "--pidfile" flag.
  else
    c=0;
  fi

  if [[ "${c}" -gt 0 ]]; then
    if tty -s; then
      echo "Already running";
    fi;
    exit;
  fi;

  echo "$$" > "${path_lock}";

#--------------------------------------------------
# Port forward
#--------------------------------------------------

  retry=0;

  while true; do

    #--------------------------------------------------
    # Log cleanup
    #--------------------------------------------------

      if [ ! -f "${path_log}" ]; then
        touch "${path_log}";
      fi

      tail -n 30 "${path_log}" > "${path_log}.tmp";

      mv "${path_log}.tmp" "${path_log}";

    #--------------------------------------------------
    # Exit old sockets
    #--------------------------------------------------

      if [ -S "${path_socket}" ]; then

        echo "$(date) : Exit" >> "${path_log}";

        ssh -S "${path_socket}" -O exit placeholder;

      fi

    #--------------------------------------------------
    # Lost lock
    #--------------------------------------------------

      if [ ! -e "${path_lock}" ] || ! grep -q "$$" "${path_lock}"; then

        echo "$(date) : Lost Lock" >> "${path_log}";

        exit;

      fi

    #--------------------------------------------------
    # Master connection
    #--------------------------------------------------

      echo "$(date) : Connect ${retry}" >> "${path_log}";

      ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -o ServerAliveInterval=30 -o ExitOnForwardFailure=yes -fNTMS "${path_socket}" -i "${path_key}" "${tunnel_user}@${tunnel_host}" >> "${path_log}" 2>&1;

    #--------------------------------------------------
    # Setup and keep checking the port forwarding
    #--------------------------------------------------

      old_port=0;

      while ssh -S "${path_socket}" -O check placeholder 2>/dev/null; do

        new_port=$(ssh -S "${path_socket}" -O forward -R "0:localhost:${local_port}" placeholder 2>&1);

        if [[ "${new_port}" -gt 0 ]]; then

          retry=0;

          if [[ "${new_port}" -ne "${old_port}" ]]; then

            ssh -i "${path_key}" "${tunnel_user}@${tunnel_host}" "tunnel.port.sh '${new_port}' '${local_name}'" >> "${path_log}" 2>&1;
              # Tell remote server what the port is, and local_name.
              # Don't use socket, it used "-N"; if done, a lost connection keeps sshd running on the remote host, even with ClientAliveInterval/ClientAliveCountMax.

            echo "$(date) : ${new_port}" >> "${path_log}";

            echo "${new_port}" > "${path_port}";

            old_port="${new_port}";

            sleep 1;

          else

            sleep 300; # Looks good, check again in 5 minutes.

          fi

        else # Not a valid port number (0, empty string, number followed by an error message, etc?)

          ssh -S "${path_socket}" -O exit placeholder 2>/dev/null;

        fi

      done

    #--------------------------------------------------
    # Cleanup
    #--------------------------------------------------

      if [ ! -f "${path_port}" ]; then
        rm "${path_port}";
      fi

      echo "$(date) : Disconnected" >> "${path_log}";

    #--------------------------------------------------
    # Delay before next re-try
    #--------------------------------------------------

      retry=$((retry+1));

      if [[ $retry -gt 10 ]]; then
        sleep 180; # Too many connection failures, try again in 3 minutes
      else
        sleep 5;
      fi

  done

答え2

ローカルクライアントにパイプを作成し、stderrをそのパイプにリダイレクトして同じ結果を得ました。このパイプもsshの入力にリダイレクトされました。失敗する可能性のある既知のポートを想定するために複数のSSH接続は必要ありません。これにより、ログインバナーと「割り当てられたポート###...」テキストがリモートホストにリダイレクトされます。

getsshport.shリダイレクトされた入力を読み取り、ポートを確認するリモートホストで実行される単純なスクリプトがホストにあります。このスクリプトが終了しない限り、SSHリモート転送は開いたままです。

地域側

mkfifo pipe
ssh -R "*:0:localhost:22" user@remotehost "~/getsshport.sh" 3>&1 1>&2 2>&3 < pipe | cat > pipe

3>&1 1>&2 2>&3stderrをcatにパイプすることができ、sshのすべての一般的な出力がstderrに表示されるようにstderrとstdoutを置き換えるのは少しトリックです。

リモート側~/getsshport.sh

#!/bin/sh
echo "Connection from $SSH_CLIENT"
while read line
do
    echo "$line" # echos everything sent back to the client
    echo "$line" | sed -n "s/Allocated port \([0-9]*\) for remote forward to \(.*\)\:\([0-9]*\).*/client port \3 is on local port \1/p" >> /tmp/allocatedports
done

sshを介して送信する前に、「割り当てられたポート」メッセージをローカルに送信しようとしましたが、grepパイプがstdinで開かれるのを待つsshブロックのように見えます。 grepは何かを受け取るまで書き込み用にパイプを開かないので、デフォルトではデッドロックが発生します。catしかし、同じ動作をするようには思えず、即時書き込みのためにパイプを開くと、sshは接続を開くことができます。

これはリモート側でも同じ問題です。なぜreadstdinの代わりにgrepを1行ずつ実行するのですか?そうしないと、SSHトンネルが閉じるまで「/tmp/allocationports」が記録されず、目的全体が失われます。

sshのstderrを同様のコマンドにパイプすることをお勧めします~/getsshport.sh。コマンドを指定しないと、パイプのバナーテキストやその他のエントリがリモートシェルで実行されるためです。

答え3

両方持ってください(バージョン履歴参照、このバージョンSCPサーバー側の観点から見ると、もう少し簡単です。これが効果があります。ポイントは次のとおりです。

  1. ポート情報が利用可能な時期を検出する方法をサーバーに通知する環境変数をクライアントからサーバーに渡し、それを取得して使用します。
  2. ポート情報が利用可能になったら、それをクライアントからサーバーにコピーし、サーバーがそれを取得できるようにし(上記のパート1の助けを借りて)使用します。

まず、リモート側で設定するには環境変数転送を有効にする必要があります。SSHD構成:

sudo yourfavouriteeditor /etc/ssh/sshd_config

含まれている行を見つけてAcceptEnv追加します(まだ追加していない場合は、右側のMY_PORT_FILEセクションの下に追加)。Host私にとって、その行は次のとおりです。

AcceptEnv LANG LC_* MY_PORT_FILE

再起動を忘れないでくださいSSHDこれを効果的にするため。

また、次のスクリプトが正常に動作するようにmkdir ~/portfilesリモート側で実行してみてください!


その後、ローカルでスクリプトフラグメント

  1. stderrリダイレクト用の一時ファイル名の作成
  2. ファイルにコンテンツが含まれるのを待つバックグラウンドタスクを残します。
  3. リダイレクト時にファイル名を環境変数としてサーバーに渡すSSHファイルの標準エラー
  4. バックグラウンドジョブは、別の方法を使用してstderr一時ファイルをサーバー側にコピーするために続行されます。SCP
  5. さらに、バックグラウンドジョブはフラグファイルをサーバーにコピーし、stderrファイルが準備されたことを示します。

スクリプトの断片:

REMOTE=$USER@datserver

PORTFILE=`mktemp /tmp/sshdataserverports-$(hostname)-XXXXX`
test -e $PORTFILE && rm -v $PORTFILE

# EMPTYFLAG servers both as empty flag file for remote side,
# and safeguard for background job termination on this side
EMPTYFLAG=$PORTFILE-empty
cp /dev/null $EMPTYFLAG

# this variable has the file name sent over ssh connection
export MY_PORT_FILE=$(basename $PORTFILE)

# background job loop to wait for the temp file to have data
( while [ -f $EMPTYFLAG -a \! -s $PORTFILE ] ; do
     sleep 1 # check once per sec
  done
  sleep 1 # make sure temp file gets the port data

  # first copy temp file, ...
  scp  $PORTFILE $REMOTE:portfiles/$MY_PORT_FILE

  # ...then copy flag file telling temp file contents are up to date
  scp  $EMPTYFLAG $REMOTE:portfiles/$MY_PORT_FILE.flag
) &

# actual ssh terminal connection    
ssh -X -o "SendEnv MY_PORT_FILE" -R0:localhost:631 -R0:localhost:4713 $REMOTE 2> $PORTFILE

# remove files after connection is over
rm -v $PORTFILE $EMPTYFLAG

その後、正しいリモート側の断片.bashrc:

# only do this if subdir has been created and env variable set
if [ -d ~/portfiles -a "$MY_PORT_FILE" ] ; then

       PORTFILE=~/portfiles/$(basename "$MY_PORT_FILE")
       FLAGFILE=$PORTFILE.flag
       # wait for FLAGFILE to get copied,
       # after which PORTFILE should be complete
       while [ \! -f "$FLAGFILE" ] ; do 
           echo "Waiting for $FLAGFILE..."
           sleep 1
       done

       # use quite exact regexps and head to make this robust
       export CUPS_SERVER=localhost:$(grep '^Allocated port [0-9]\+ .* localhost:631[[:space:]]*$' "$PORTFILE" | head -1 | cut -d" " -f3)
       export PULSE_SERVER=localhost:$(grep '^Allocated port [0-9]\+ .* localhost:4713[[:space:]]*$' "$PORTFILE" | head -1 | cut -d" " -f3)
       echo "Set CUPS_SERVER and PULSE_SERVER"

       # copied files served their purpose, and can be removed right away
       rm -v -- "$PORTFILE" "$FLAGFILE"
fi

ノート:もちろん、上記のコードは徹底的にテストされておらず、さまざまなバグ、コピー&ペーストエラーなどが含まれる可能性があります。使ってみた人なら誰でももっとよく理解できるだろうが、自分の責任で使用してください!私はlocalhost接続を使ってテストし、私のテスト環境ではうまくいきました。 YMMV。

答え4

これは難しい問題です。SSH_CONNECTIONまたは、行に沿って追加のサーバー側処理がある場合はDISPLAY良いですが、追加するのは簡単ではありません。問題の一部は、クライアントだけがローカル宛先をssh知っていて(サーバーへの)要求パケットにはローカル宛先のみを知っていることです。リモートアドレスとポート。

ここにある他の答えには、このクライアントをキャプチャしてサーバーに送信する、あまりよくないさまざまなソリューションがあります。ここに別のアプローチがあります。正直言ってあまりきれいではありませんが、少なくとも醜い当事者はクライアント側に残ります;-)

  • クライアント、SendEnvSSHを介してデフォルトで一部の環境変数を送信できるように追加/変更します(おそらくデフォルトではない可能性があります)
  • AcceptEnvサーバー側、同じ内容を許可する追加/変更(デフォルトでは有効になっていない可能性があります)
  • 動的にロードされたライブラリを使用してクライアントのstderr出力を監視sshし、SSHクライアント環境を更新します。接続設定中
  • 構成ファイル/ログインスクリプトで、サーバー側の環境変数を選択します。

ssh -vv ...環境が交換される前にリモート転送が設定され、記録(確認とともに)されるため、これは機能します(幸いなことに現在では)。動的にロードされたライブラリはlibc関数write()ssh_confirm_remote_forward()→→→)をキャプチャする必要があります。 ELFバイナリの関数を再コンパイルせずにリダイレクトまたはラップすることは、動的ライブラリの関数で同じことを行うよりもはるかに複雑です。logit()do_log()write()

クライアント.ssh/config(またはコマンドライン-o SendEnv ...)から

Host somehost
  user whatever
  SendEnv SSH_RFWD_*

サーバーでsshd_config(root / admin変更が必要)

AcceptEnv LC_* SSH_RFWD_*

この方法はLinuxクライアントで動作し、サーバーで特別な作業を行う必要はなく、少し調整するだけで他の* nixでも動作します。少なくともOpenSSH 5.8p1から7.5p1まで動作します。

呼び出しのコンパイルgcc -Wall -shared -ldl -Wl,-soname,rfwd -o rfwd.so rfwd.c :

LD_PRELOAD=./rfwd.so ssh -R0:127.0.0.1:4713 -R0:localhost:631 somehost

パスワード:

#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#include <string.h>
#include <stdlib.h>

// gcc -Wall -shared  -ldl -Wl,-soname,rfwd -o rfwd.so rfwd.c

#define DEBUG 0
#define dfprintf(fmt, ...) \
    do { if (DEBUG) fprintf(stderr, "[%14s#%04d:%8s()] " fmt, \
          __FILE__, __LINE__, __func__,##__VA_ARGS__); } while (0)

typedef ssize_t write_fp(int fd, const void *buf, size_t count);
static write_fp *real_write;

void myinit(void) __attribute__((constructor));
void myinit(void)
{
    void *dl;
    dfprintf("It's alive!\n");
    if ((dl=dlopen(NULL,RTLD_NOW))) {
        real_write=dlsym(RTLD_NEXT,"write");
        if (!real_write) dfprintf("error: %s\n",dlerror());
        dfprintf("found %p write()\n", (void *)real_write);
    } else {
        dfprintf(stderr,"dlopen() failed\n");
    }
}

ssize_t write(int fd, const void *buf, size_t count)
{
     static int nenv=0;

     // debug1: Remote connections from 192.168.0.1:0 forwarded to local address 127.0.0.1:1000
     //  Allocated port 44284 for remote forward to 127.0.0.1:1000
     // debug1: All remote forwarding requests processed
     if ( (fd==2) && (!strncmp(buf,"Allocated port ",15)) ) {
         char envbuf1[256],envbuf2[256];
         unsigned int rport;
         char lspec[256];
         int rc;

         rc=sscanf(buf,"Allocated port %u for remote forward to %256s",
          &rport,lspec);

         if ( (rc==2) && (nenv<32) ) {
             snprintf(envbuf1,sizeof(envbuf1),"SSH_RFWD_%i",nenv++);
             snprintf(envbuf2,sizeof(envbuf2),"%u %s",rport,lspec);
             setenv(envbuf1,envbuf2,1);
             dfprintf("setenv(%s,%s,1)\n",envbuf1,envbuf2);
         }
     }
     return real_write(fd,buf,count);
}

(この方法を使用するシンボリックバージョン管理に関連するいくつかのglibcベアトラップがありますが、write()この問題は存在しません。)

勇気がある場合は、setenv()関連コードを取得してssh.c ssh_confirm_remote_forward()コールバック関数にパッチを適用できます。

これにより、という環境変数が設定されますSSH_RFWD_nnn。構成ファイルでこれらの変数を確認してください。bash

for fwd in ${!SSH_RFWD_*}; do
    IFS=" :" read lport rip rport <<< ${!fwd}
    [[ $rport -eq "631" ]] && export CUPS_SERVER=localhost:$lport
    # ...
done

指示:

  • コードにエラーチェックがない
  • 環境を変える可能スレッドに関連する問題が発生します。 PAMはスレッドを使用します。問題があるとは思わないが、まだテストしていない。
  • ssh* local:port:remote:port* 形式の完全な転送は現在明示的に文書化されていませんが(必要に応じてdebug1メッセージの追加解析が必要ですssh -v)、ユースケースではこれを必要としません。

奇妙なことは、OpenSSHがポート転送に関する情報を取得する方法がないようです。

escapeを使用して(部分的に)対話的にこれを行うことができます。~#奇妙なことに、実装はリスニングチャネルをスキップして開いている(TCP ESTABLISHEDなど)チャネルのみを一覧表示し、いずれにせよ有用なフィールドを印刷しません。バラよりchannels.c channel_open_message()

スロットの詳細を印刷する関数をパッチすることはできますが、SSH_CHANNEL_PORT_LISTENERこれはローカル転送のみ可能ですチャンネル実際の状況と異なる今後)。あるいは、それらをパッチして、グローバル構造から両方の転送テーブルをダンプすることもできますoptions

#include "readconf.h"
Options options;  /* extern */
[...]
snprintf(buf, sizeof buf, "Local forwards:\r\n");
buffer_append(&buffer, buf, strlen(buf));
for (i = 0; i < options.num_local_forwards; i++) {
     snprintf(buf, sizeof buf, "  #%d listen %s:%d connect %s:%d\r\n",i,
       options.local_forwards[i].listen_host,
       options.local_forwards[i].listen_port,
       options.local_forwards[i].connect_host,
       options.local_forwards[i].connect_port);
     buffer_append(&buffer, buf, strlen(buf));
}
snprintf(buf, sizeof buf, "Remote forwards:\r\n");
buffer_append(&buffer, buf, strlen(buf));
for (i = 0; i < options.num_remote_forwards; i++) {
     snprintf(buf, sizeof buf, "  #%d listen %s:%d connect %s:%d\r\n",i,
       options.remote_forwards[i].listen_host,
       options.remote_forwards[i].listen_port,
       options.remote_forwards[i].connect_host,
       options.remote_forwards[i].connect_port);
     buffer_append(&buffer, buf, strlen(buf));
}

これは「プログラミング方式」ソリューションではありませんが、うまく機能します。ただし、転送を動的に追加/削除すると、クライアントコードはリスト(~C)を更新しません(ただし、ソースコードではXXXとして表示されます)。


サーバーがLinuxの場合、私が一般的に使用する別のオプションがありますが、リモート転送ではなくローカル転送用です。lo127.0.0.1/8です。 Linuxでは、次のことができます。127/8のすべてのアドレスに透明にバインドしたがって、一意の127.xyzアドレスを使用している場合は、固定ポートを使用できます。例:

mr@local:~$ ssh -R127.53.50.55:44284:127.0.0.1:44284 remote
[...]
mr@remote:~$ ss -atnp src 127.53.50.55
State      Recv-Q Send-Q        Local Address:Port          Peer Address:Port 
LISTEN     0      128            127.53.50.55:44284                    *:*    

これは特権ポート<1024バインディングに依存し、OpenSSHはLinux機能をサポートせず、ほとんどのプラットフォームでハードコードされたUID検証機能を提供します。

オクテットを賢く選択すると(私の場合はASCII序数ニーモニック)、一日の終わりに混乱を解決するのに役立ちます。

関連情報