Вопрос (вкратце)
При динамическом назначении портов для удаленной переадресации ( -R
опция) как скрипт на удаленной машине (например, полученный из .bashrc
) может определить, какие порты были выбраны OpenSSH?
Фон
Я использую OpenSSH (на обоих концах) для подключения к нашему центральному серверу, который я разделяю с несколькими другими пользователями. Для моего удаленного сеанса (на данный момент) я хотел бы перенаправить X, cups и pulseaudio.
Самый простой — пересылка X с использованием -X
опции. Выделенный адрес X хранится в переменной окружения DISPLAY
, и из него я могу определить соответствующий порт TCP, в большинстве случаев по крайней мере. Но мне это почти никогда не нужно, потому что Xlib уважает DISPLAY
.
Мне нужен аналогичный механизм для cups и pulseaudio. Основы для обоих сервисов существуют в виде переменных окружения 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
правильности.
Мы часто используем переадресацию портов, поэтому мне нужно динамическое распределение портов. Статическое распределение портов не вариант.
OpenSSH имеет механизм динамического выделения портов на удаленном сервере, указав 0
в качестве bind-port для удаленной пересылки (опция -R
). Используя следующую команду, OpenSSH будет динамически выделять порты для пересылки cups и pulse.
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
Однако сообщения "Allocated port ..." создаются на моей локальной машине и отправляются на STDERR
, к которому я не могу получить доступ на удаленной машине. Как ни странно, OpenSSH, похоже, не имеет средств для получения информации о переадресации портов.
Как мне получить эту информацию, чтобы поместить ее в скрипт оболочки для адекватной настройки CUPS_SERVER
на PULSE_SERVER
удаленном хосте?
Тупики
Единственное, что я смог легко найти, это увеличить детализацию информации, sshd
пока ее можно будет прочитать из журналов. Это нежизнеспособно, поскольку эта информация раскрывает гораздо больше информации, чем разумно сделать доступной для пользователей без прав root.
Я думал о том, чтобы исправить OpenSSH для поддержки дополнительной escape-последовательности, которая выводит хорошее представление внутренней структуры permitted_opens
, но даже если это то, что мне нужно, я все равно не могу написать скрипт для доступа к escape-последовательностям клиента со стороны сервера.
Должен быть лучший способ.
Следующий подход кажется очень нестабильным и ограничен одним таким сеансом 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
порт cups, потому чтоlpstat -h localhost:41273 -a
возвращает0
.55710
является портом импульса, потому чтоpactl -s localhost:55710 stat
возвращает0
. (Он даже выводит имя хоста моего клиента!)
(Чтобы выполнить вычитание множеств I sort -u
, сохраните вывод из приведенных выше командных строк и используйте comm
для выполнения вычитания.)
Pulseaudio позволяет мне идентифицировать клиента, и, по всем намерениям и целям, это может служить якорем для разделения сеансов SSH, которые необходимо разделить. Однако я не нашел способа связать 41273
, 55710
и 6010
с тем же sshd
процессом. netstat
не буду раскрывать эту информацию не-root пользователям. Я получаю только -
в PID/Program name
столбце, где я хотел бы прочитать 2339/54
(в этом конкретном случае). Так близко ...
решение1
К сожалению, я не нашел ваш вопрос раньше, но только что получил действительно хороший ответ от kamil-maciorowski:
https://unix.stackexchange.com/a/584505/251179
Подводя итог, сначала установите главное соединение, оставьте его в фоновом режиме, а затем выполните вторую команду с -O *ctl_cmd*
параметром set to 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 переход в фоновый режим.
- -Н= Не выполнять удаленную команду.
- -М= Переводит клиента в режим «главного» для совместного использования подключения.
- -С= Расположение контрольного разъема для совместного использования подключений.
- -О= Управление главным процессом мультиплексирования активных соединений.
В моем случае я добавил еще немного, чтобы продолжать проверять соединение:
#!/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, чтобы предположить свободный известный порт, который может дать сбой. Таким образом, баннер входа и текст "Allocated port ### ..." перенаправляются на удаленный хост.
У меня есть простой скрипт на хосте 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
построчно, а не просто grep из stdin - в противном случае `/tmp/allocatedports' не будет записан, пока не будет закрыт туннель ssh, что сводит на нет весь смысл
Предпочтительнее передавать stderr ssh в команду типа like ~/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 при перенаправлениисшstderr в файл
- фоновое задание продолжает копировать временный файл 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
Примечание: Приведенный выше код, конечно, не очень тщательно протестирован и может содержать всевозможные ошибки, ошибки копирования-вставки и т. д. Любой, кто его использует, должен также его понимать,Используйте на свой риск!Я протестировал его, используя только локальное соединение, и у меня это сработало в моей тестовой среде 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
(требуется изменение прав root/администратора)
AcceptEnv LC_* SSH_RFWD_*
Этот подход работает для клиентов Linux и не требует ничего особенного на сервере, он должен работать для других *nix с некоторыми небольшими изменениями. Работает как минимум с OpenSSH 5.8p1 до 7.5p1.
Скомпилировать с помощью gcc -Wall -shared -ldl -Wl,-soname,rfwd -o rfwd.so rfwd.c
Invoke с:
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));
}
Это работает отлично, хотя это не «программное» решение, с той оговоркой, что клиентский код не обновляет список (пока что он помечен как 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) помогают распутать путаницу в конце дня.