Вопрос (TL; DR)
При динамическом назначении портов для удаленной переадресации (иначе -R
), как скрипт на удаленном компьютере (например, из источника .bashrc
) может определить, какие порты были выбраны OpenSSH?
Фон
Я использую OpenSSH (на обоих концах) для подключения к нашему центральному серверу, которым я делюсь с несколькими другими пользователями. Для моего удаленного сеанса (на данный момент) я хотел бы переслать X, чашки и pulseaudio.
Наиболее тривиальным является пересылка X с использованием -X
опции. Выделенный X-адрес хранится в переменной среды, DISPLAY
и из этого я могу определить соответствующий порт TCP, в большинстве случаев в любом случае. Но мне вряд ли когда-нибудь понадобится, потому что Xlib чтит DISPLAY
.
Мне нужен аналогичный механизм для чашек и pulaudio. Основы для обоих сервисов существуют в форме переменных среды 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-порт для удаленной пересылки ( -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 для поддержки дополнительной 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
; чашки, пульс и Х соответственно.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
не будет раскрывать эту информацию пользователям без прав root. Я получаю только -
в PID/Program name
столбце, где я хотел бы прочитать 2339/54
(в данном конкретном случае). Так близко ...
источник
netstat
PID не будет отображаться для процессов, которыми вы не владеете или которые являются пространством ядра. НапримерОтветы:
Возьмите два (смотрите историю для версии, которая делает scp со стороны сервера и немного проще), это должно сделать это. Суть этого заключается в следующем:
Во-первых, для настройки на удаленной стороне вам необходимо разрешить отправку переменной env в конфигурации sshd :
Найдите строку с
AcceptEnv
и добавьтеMY_PORT_FILE
к ней (или добавьте строку в правойHost
части, если ее еще нет). Для меня линия стала такой:Также не забудьте перезапустить sshd, чтобы это вступило в силу.
Кроме того, чтобы нижеприведенные скрипты работали, делайте это
mkdir ~/portfiles
на удаленной стороне!Затем на локальной стороне, фрагмент сценария, который будет
Фрагмент скрипта:
Затем фрагмент для удаленной стороны, подходящий для .bashrc :
Примечание . Приведенный выше код, конечно, не очень тщательно протестирован и может содержать все виды ошибок, ошибок копирования и вставки и т. Д. Любой, кто его использует, также лучше это понимает, используйте на свой страх и риск! Я проверил это, используя только локальное соединение, и это сработало для меня, в моей тестовой среде. YMMV.
источник
scp
с удаленной стороны на местную сторону, что я не могу. У меня был похожий подход, но я бы обернул егоssh
в фоновом режиме после установления соединения, затем отправил этот файл с локального на удаленный сервер,scp
а затем вытащилssh
клиента на передний план и запустил скрипт на удаленной стороне. Я не понял, как правильно создавать сценарии фоновой обработки и упорядочения локальных и удаленных процессов. Обертывание и интеграция локальногоssh
клиента с некоторыми удаленными сценариями вроде этого не выглядит хорошим подходом.(while [ ... ] ; do sleep 1 ; done ; scp ... )&
. Затем дождитесь появления на сервере переднего плана.bashrc
(при условии, что клиент отправит правильную переменную env), чтобы файл появился. Я обновлю ответ позже после некоторого тестирования (вероятно, нет времени до завтра).~/.ssh-${PID}-forwards
Фрагмент для локальной стороны, подходящий для .bashrc:
источник
Я добился того же, создав канал на локальном клиенте, затем перенаправив stderr на канал, который также перенаправляется на вход ssh. Не требуется нескольких соединений ssh, чтобы предположить свободный известный порт, который может дать сбой. Таким образом, баннер входа в систему и текст «Выделенный порт ### ...» перенаправляются на удаленный хост.
У меня есть простой скрипт на хосте,
getsshport.sh
который запускается на удаленном хосте, который читает перенаправленный ввод и анализирует порт. Пока этот сценарий не заканчивается, ssh remote forward остается открытым.местная сторона
3>&1 1>&2 2>&3
Это небольшая хитрость, чтобы поменять местами stderr и stdout, чтобы stderr был передан по каналу cat, а все обычные выходные данные ssh показаны на stderr.удаленная сторона ~ / getsshport.sh
Я попытался
grep
сначала отправить сообщение «выделенный порт» на локальной стороне перед отправкой через ssh, но кажется, что ssh заблокирует ожидание открытия канала на stdin. grep не открывает канал для записи, пока не получит что-то, так что это в основном тупики.cat
однако, похоже, не имеет такого же поведения, и открывает канал для записи немедленно, позволяя ssh открыть соединение.это та же проблема на удаленной стороне, и почему
read
строка за строкой, а не просто grep из stdin - в противном случае `/ tmp / allocports 'не будет записан до тех пор, пока не будет закрыт туннель ssh, что наносит ущерб всей цели~/getsshport.sh
Предпочтительной является передача stderr из ssh в подобную команду , так как без указания команды, текста баннера или чего-либо еще в канале выполняется на удаленной оболочке.источник
renice +10 $$; exec cat
перед тем,done
чтобы сэкономить ресурсы.Это сложная задача , дополнительная обработка на стороне сервера по линии
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 ...
)На сервере
sshd_config
(требуется root / административное изменение)Этот подход работает для клиентов Linux и не требует ничего особенного на сервере, он должен работать для других * nix с некоторыми незначительными изменениями. Работает как минимум с OpenSSH 5.8p1 до 7.5p1.
Компилировать с
gcc -Wall -shared -ldl -Wl,-soname,rfwd -o rfwd.so rfwd.c
Invoke с:Код:
(Существуют некоторые ловушки для медведей-глибков, связанные с версионированием символов с помощью этого подхода, но у
write()
него нет этой проблемы.)Если вы чувствуете себя смелым, вы можете взять
setenv()
соответствующий код и добавить его вssh.c
ssh_confirm_remote_forward()
функцию обратного вызова.Это устанавливает переменные среды с именами
SSH_RFWD_nnn
, проверьте их в своем профиле, например, вbash
Предостережения:
ssh
в настоящее время явно не регистрируется полная переадресация формы * local: port: remote: port * (при необходимости потребуется дальнейший анализdebug1
сообщений с помощьюssh -v
), но это не требуется для вашего варианта использованияВы можете (частично) сделать это в интерактивном режиме с помощью escape
~#
, как ни странно, реализация пропускает каналы, которые прослушивают, она только перечисляет открытые (т. Е. TCP ESTABLISHED) каналы и ни в коем случае не печатает полезные поля. Видетьchannels.c
channel_open_message()
Вы можете исправить эту функцию, чтобы напечатать детали для
SSH_CHANNEL_PORT_LISTENER
слотов, но это только даст вам локальные переадресации ( каналы не то же самое, что фактические пересылки ). Или вы можете исправить это, чтобы вывести две таблицы пересылки из глобальнойoptions
структуры:Это прекрасно работает, хотя это и не «программное» решение, с оговоркой, что клиентский код не (пока он помечен как XXX в источнике) обновляет список при добавлении / удалении переадресаций на лету (
~C
)Если сервер (ы) - Linux, у вас есть еще один вариант, который я обычно использую, хотя для локальной пересылки, а не удаленной.
lo
127.0.0.1/8, в Linux вы можете прозрачно привязать любой адрес в 127/8 , поэтому вы можете использовать фиксированные порты, если используете уникальные адреса 127.xyz, например:Это зависит от привязки привилегированных портов <1024, OpenSSH не поддерживает возможности Linux и имеет жестко проверенную проверку UID на большинстве платформ.
Мудро выбранные октеты (в моем случае порядковая мнемоника ASCII) помогают распутать беспорядок в конце дня.
источник