질문(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
.
우리는 포트 전달을 많이 사용하므로 동적 포트 할당이 필요합니다. 정적 포트 할당은 옵션이 아닙니다.
0
OpenSSH에는 원격 전달을 위한 바인딩 포트( -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
,55710
및6010
; 각각 컵, 펄스, X입니다.6010
를 사용하여 X 포트로 식별됩니다DISPLAY
.41273
lpstat -h localhost:41273 -a
반환 하기 때문에 컵 포트입니다0
.55710
pactl -s localhost:55710 stat
을 반환하기 때문에 펄스 포트입니다0
. (내 클라이언트의 호스트 이름도 인쇄합니다!)
(뺄셈 I을 설정하고 sort -u
위 명령줄의 출력을 저장하고 comm
빼기를 수행하는 데 사용합니다.)
Pulseaudio를 사용하면 클라이언트를 식별할 수 있으며, 모든 의도와 목적을 위해 이는 분리가 필요한 SSH 세션을 분리하기 위한 앵커 역할을 할 수 있습니다. 그러나 나는 묶는 방법 41273
과 같은 과정 55710
을 찾지 못했습니다 . 루트가 아닌 사용자에게는 해당 정보를 공개하지 않습니다. 나는 읽고 싶은 열 에서만 (이 특별한 경우) 을 얻습니다 . 너무 가까워 ...6010
sshd
netstat
-
PID/Program name
2339/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>&3
stderr과 stdout을 교환하는 약간의 트릭입니다. 그러면 stderr이 cat으로 파이프되고 ssh의 모든 일반 출력이 stderr에 표시됩니다.
원격 측 ~/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
grep
SSH를 통해 보내기 전에 먼저 로컬 측에서 "할당된 포트" 메시지를 시도했지만 ssh는 파이프가 stdin에서 열릴 때까지 기다리는 것을 차단하는 것 같습니다. grep은 무언가를 받을 때까지 쓰기 위해 파이프를 열지 않으므로 기본적으로 교착 상태가 발생합니다. cat
그러나 이와 동일한 동작을 수행하지 않는 것 같으며 즉시 쓰기용 파이프를 열어 ssh가 연결을 열 수 있도록 합니다.
이것은 원격 측에서도 동일한 문제이며 왜 read
stdin에서 grep을 사용하는 대신 한 줄씩 실행해야 하는가? 그렇지 않으면 SSH 터널이 닫힐 때까지 `/tmp/allocationports'가 기록되지 않아 전체 목적이 무산됩니다.
ssh의 stderr을 명령으로 파이프하는 것이 ~/getsshport.sh
선호됩니다. 명령을 지정하지 않고 배너 텍스트 또는 파이프에 있는 다른 모든 것이 원격 셸에서 실행되기 때문입니다.
답변3
두 가지를 선택하세요(해당 버전에 대한 기록 참조).scp서버 측에서 좀 더 간단하게) 이렇게 하면 됩니다. 그 내용은 다음과 같습니다.
- 환경 변수를 클라이언트에서 서버로 전달하여 포트 정보가 사용 가능한 시기를 감지한 다음 이를 가져와 사용하는 방법을 서버에 알려줍니다.
- 포트 정보를 사용할 수 있게 되면 이를 클라이언트에서 서버로 복사하여 서버가 해당 정보를 얻을 수 있도록 하고(위의 1부의 도움으로) 사용합니다.
먼저 원격 측에서 설정하려면 env 변수 전송을 활성화해야 합니다.SSHD구성:
sudo yourfavouriteeditor /etc/ssh/sshd_config
줄을 찾아서 AcceptEnv
추가하세요 (또는 아직 줄이 없으면 MY_PORT_FILE
오른쪽 섹션 아래에 줄을 추가하세요 ). Host
나에게 그 줄은 다음과 같았다:
AcceptEnv LANG LC_* MY_PORT_FILE
또한 다시 시작하는 것도 잊지 마세요.SSHD이것이 적용되려면.
또한 아래 스크립트가 작동하려면 mkdir ~/portfiles
원격 측에서 수행하십시오!
그런 다음 로컬 측에서 스크립트 조각을 작성합니다.
- stderr 리디렉션을 위한 임시 파일 이름 생성
- 파일에 콘텐츠가 포함될 때까지 기다리기 위해 백그라운드 작업을 종료합니다.
- 리디렉션하는 동안 파일 이름을 env 변수로 서버에 전달합니다.SSH파일에 대한 표준 오류
- 백그라운드 작업은 별도의 방법을 사용하여 stderr 임시 파일을 서버 측에 복사합니다.scp
- 백그라운드 작업은 플래그 파일을 서버에 복사하여 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
로컬 대상을 알고 있고 요청 패킷(서버에 대한)에는 다음이 포함되어 있다는 것입니다. 원격 주소와 포트만 있습니다.
여기에 있는 다른 답변에는 이 클라이언트 측을 캡처하여 서버로 보내기 위한 다양하고 좋지 않은 솔루션이 있습니다. 솔직히 말해서 그다지 예쁘지는 않지만 적어도 이 추악한 파티는 클라이언트 측에 유지되는 대체 접근 방식이 있습니다 ;-)
- 클라이언트 측에서
SendEnv
SSH를 통해 일부 환경 변수를 기본적으로 보낼 수 있도록 추가/수정합니다(기본값은 아닐 수도 있음). - 서버 측, 동일한 내용을 허용하도록 추가/수정
AcceptEnv
(아마도 기본적으로 활성화되지 않음) ssh
동적으로 로드된 라이브러리로 클라이언트 stderr 출력을 모니터링 하고 SSH 클라이언트 환경을 업데이트합니다.연결 설정 중에- 프로필/로그인 스크립트에서 서버 측 환경 변수를 선택합니다.
환경이 교환되기 전에 원격 전달이 설정되고 기록되기 때문에 이것은 작동합니다(현재로서는 다행히) ssh -vv ...
. 동적으로 로드된 라이브러리는 write()
libc 함수( ssh_confirm_remote_forward()
→ logit()
→ do_log()
→ write()
)를 캡처해야 합니다. ELF 바이너리의 함수를 재컴파일하지 않고 리디렉션하거나 래핑하는 것은 동적 라이브러리의 함수에 대해 동일한 작업을 수행하는 것보다 훨씬 더 복잡합니다.
클라이언트 .ssh/config
(또는 명령줄 -o SendEnv ...
) 에서
Host somehost
user whatever
SendEnv SSH_RFWD_*
서버에서 sshd_config
(루트/관리자 변경 필요)
AcceptEnv LC_* SSH_RFWD_*
이 접근 방식은 Linux 클라이언트에 작동하며 서버에 특별한 것이 필요하지 않습니다. 약간의 조정만 하면 다른 *nix에서도 작동합니다. 최소 OpenSSH 5.8p1에서 최대 7.5p1까지 작동합니다.
다음을 사용하여 Invoke 로 컴파일합니다 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
주의사항:
- 코드에 오류 검사가 많지 않습니다.
- 환경을 바꾸다5월스레드 관련 문제가 발생합니다. 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));
}
이것은 잘 작동하지만 "프로그래밍 방식" 솔루션은 아니지만 클라이언트 코드는 전달을 즉시 추가/제거할 때 목록을 업데이트하지 않는다는 주의 사항(아직 소스에서 XXX로 표시됨)을 주의해야 합니다( ~C
).
서버가 Linux인 경우 옵션이 하나 더 있습니다. 이는 제가 일반적으로 사용하는 옵션이지만 원격이 아닌 로컬 전달에 사용됩니다. lo
127.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 서수 니모닉)은 하루가 끝날 때 혼란을 해결하는 데 도움이 됩니다.