OpenSSH RemoteForward에 대해 동적으로 할당된 포트 결정

OpenSSH RemoteForward에 대해 동적으로 할당된 포트 결정

질문(TL;DR)

원격 전달을 위해 포트( -R옵션이라고도 함)가 동적으로 할당되면 원격 시스템의 스크립트(예: 에서 시작 .bashrc)가 OpenSSH에서 어떤 포트를 선택할지 어떻게 결정합니까?


배경

저는 OpenSSH(양쪽 끝)를 사용하여 다른 여러 사용자와 공유하는 중앙 서버에 연결합니다. 내 원격 세션(현재)의 경우 X, cup 및 pulseaudio를 전달하고 싶습니다.

가장 간단한 방법은 -X이 옵션을 사용하여 X를 전달하는 것입니다. 할당된 X 주소는 환경 변수에 저장되며 DISPLAY, 대부분의 경우 해당 TCP 포트를 확인할 수 있습니다. 하지만 Xlib는 DISPLAY.

컵과 펄스 오디오에도 비슷한 메커니즘이 필요합니다. 이 두 서비스의 기본은 각각 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로그에서 정보를 읽을 수 있을 때까지 긴 정보를 추가하는 것입니다. 이는 루트가 아닌 사용자가 합리적으로 액세스할 수 있는 것보다 훨씬 더 많은 정보를 공개하므로 실현 가능하지 않습니다.

내부 struct 의 좋은 표현을 인쇄하는 추가 이스케이프 시퀀스를 지원하기 위해 OpenSSH를 패치할 생각이지만 permitted_opens, 그것이 내가 원하는 것임에도 여전히 서버에서 클라이언트측 이스케이프 시퀀스에 액세스하는 스크립트를 작성할 수 없습니다. 옆.


더 좋은 방법이 있을 거에요

다음 방법은 매우 불안정한 것으로 보이며 사용자당 하나의 SSH 세션으로 제한됩니다. 그러나 나에게는 이러한 동시 세션이 두 개 이상 필요하고 다른 사용자에게는 더 많은 세션이 필요합니다. 하지만 시도해 봤는데...

별이 올바르게 정렬되면 닭 한두 마리를 희생하여 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/'

  • 첫 번째 세트에는 있지만 두 번째 세트에는 없는 모든 포트가 전달된 포트일 가능성이 높으며 실제로 세트를 빼면 각각 41273, 및 X가 생성됩니다.557106010

  • 6010DISPLAYX 로 식별된 포트를 사용하십시오 .

  • 41273lpstat -h localhost:41273 -a반환 때문에 컵 포트입니다 0.
  • 55710pactl -s localhost:55710 stat반환 때문에 펄스 포트입니다 0. (내 클라이언트의 호스트 이름도 인쇄합니다!)

(집합 빼기를 수행하기 위해 sort -u위 명령줄의 출력을 저장하고 이를 사용하여 comm빼기를 수행합니다.)

Pulseaudio를 사용하면 클라이언트를 식별할 수 있으며 모든 의도와 목적을 위해 이는 분리해야 하는 SSH 세션을 분리하기 위한 앵커 포인트 역할을 합니다. 그러나 바인딩하는 방법 41273과 동일한 프로세스 55710를 찾지 못했습니다 . 이 정보는 루트가 아닌 사용자에게는 공개되지 않습니다. 내가 읽고 싶은 열에는 하나만 표시됩니다 (이 특정 예에서는). 아주 근접한...6010sshdnetstat-PID/Program name2339/54

답변1

불행히도 이전에는 귀하의 질문을 찾지 못했지만 방금 kamil-maciorowski로부터 정말 좋은 답변을 받았습니다.

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

요약하자면, 먼저 기본 연결을 설정하고 이를 백그라운드에 그대로 둔 다음 두 번째 명령을 실행하고 포트 전달을 요청/설정 -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동일한 제어 소켓을 사용할 수 있는 원격 서버에서 명령을 다시 실행할 수 있습니다.

플래그는 다음과 같습니다.

  • -에프= ssh에 백그라운드 진입을 요청하세요.
  • -N= 원격 명령을 실행하지 마십시오.
  • -중= 연결 공유를 위해 클라이언트를 "마스터" 모드로 전환하세요.
  • -에스= 공유 연결에 사용되는 제어 소켓의 위치입니다.
  • -영형= 기본 프로세스에서 활성 연결 재사용을 제어합니다.

제 경우에는 연결을 계속 확인하기 위해 몇 가지 항목을 더 추가했습니다.

#!/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을 한 줄씩 실행해야 할까요? 그렇지 않으면 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(루트/관리자 변경 필요)

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 서수 니모닉) 하루가 끝날 때 혼란을 해결하는 데 도움이 됩니다.

관련 정보