Это сложный вопрос, дополнительная обработка на стороне сервера, например SSH_CONNECTION
или DISPLAY
, была бы отличной, но добавить ее нелегко. : часть проблемы в том, что только ssh
клиент знает локальный пункт назначения, пакет запроса (к серверу) содержит только удаленный адрес и порт.
В других ответах здесь есть различные некрасивые решения для захвата этой клиентской стороны и отправки ее на сервер. Вот альтернативный подход, который не очень красив, если честно, но, по крайней мере, эта уродливая партия сохраняется на стороне клиента ;-)
SendEnv
, чтобы мы могли отправить некоторые переменные окружения изначально используются через ssh (вероятно, не по умолчанию) AcceptEnv
, чтобы они принимали такие же (вероятно, не включены по умолчанию) ssh
вывод stderr с динамически загружаемой библиотекой и обновление клиентской среды ssh во время установки соединения Это работает (к счастью, на данный момент в любом случае) поскольку удаленные переадресации устанавливаются и записываются до обмена средой (подтвердите с помощью ssh -vv ...
).Динамически загружаемая библиотека должна захватить функцию write()
libc (ssh_confirm_remote_forward()
→ logit()
→ do_log()
→ написать()
). Перенаправление или перенос функций в двоичный файл ELF (без перекомпиляции) на порядки сложнее, чем то же самое для функции в динамической библиотеке.
На клиенте .ssh/config
(или в командной строке -o SendEnv ...
)
Host somehost
user whatever
SendEnv SSH_RFWD_*
На сервере sshd_config
(root/administrative требуется изменение)
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
#include
#include
#include
// 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
Предостережения:
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 *:*
Это зависит от привязки привилегированных портов
Грамотно подобранные октеты (в моем случае порядковые мнемоники ASCII) помогают распутать клубок в конце дня.