Передача нескольких файлов через stdin (через ssh)


15

У меня есть программа на удаленном хосте, выполнение которой мне нужно автоматизировать. Команда выполнения этой программы на той же машине выглядит примерно так:

/path/to/program -a file1.txt -b file2.txt

В этом случае file1.txtи file2.txtиспользуются для совершенно разных вещей внутри программы, поэтому я не могу просто catих объединить. Тем не менее, в моем случае, file1.txtи file2.txtчто я хочу передать в программу существует только на устройстве, а не на машине , где мне нужно , чтобы выполнить программу. Я знаю, что могу передать как минимум один файл через SSH, передав его через stdin:

cat file1.txt | ssh host.name /path/to/program -a /dev/stdin -b file2.txt

но, поскольку мне не разрешено хранить файлы на хосте, мне также нужен способ получить доступ к file2.txtнему. Я думаю, что это могло бы быть возможно через злоупотребление переменными среды и творческое использование catи sedвместе, но я не знаю инструменты достаточно хорошо, чтобы понять, как я использовал бы их, чтобы достигнуть этого. Это выполнимо и как?


2
catи sedне являются решением здесь.
Сликс

Возможно, я смогу смонтировать, но, учитывая прокси и ограничения безопасности, я не уверен, что смогу сойти с рук.
Зеленый Плащ Гай

У вас есть разрешение на удаленной машине для монтирования папки ssh?
Сликс

1
если вы можете открыть сеанс ssh с удаленного компьютера на локальный, то на уровне сети нет проблем с подключением папки SSH.
Slyx

Вы можете сделать переадресацию? Какие системы и оболочки у вас есть на локальном и удаленном конце?
Мосви

Ответы:


18

Если файлы, указанные в качестве аргументов вашей программы, являются текстовыми файлами, и вы можете контролировать их содержимое (вы знаете строку, которая не встречается внутри них), вы можете использовать несколько здесь-документов:

{
    echo "cat /dev/fd/3 3<<'EOT' /dev/fd/4 4<<'EOT' /dev/fd/5 5<<'EOT'"
    cat file1
    echo EOT
    cat file2
    echo EOT
    cat file3
    echo EOT
} | ssh user@host sh

Вот catпример команды, которая принимает имена файлов в качестве аргументов. Это может быть вместо:

echo "/path/to/prog -a /dev/fd/3 3<<'EOT' -b /dev/fd/4 4<<'EOT'

Замените каждый EOTчто-то, что не встречается в каждом из файлов, соответственно.


1
Этот подход довольно умный! Если файлы не являются текстовыми, вы можете кодировать / декодировать их с помощью чего-то вроде base64 или даже хорошего старого кода ... Очень приятно!
filbranden

3
Обратите внимание, что несколько (большинство) реализаций sh реализуют здесь документы с использованием временных файлов, поэтому он может не работать для OP, если им не разрешено хранить файлы на удаленном хосте.
Стефан

2
dash( /bin/shв Debian, Ubuntu, и т.д.), busybox sh, то /bin/shиз FreeBSD и yashне использовать временные файлы здесь-документов. Я фактически проверил это с доступным только для чтения chroot. Конечно,/dev/fd/ файлы должны быть доступны - только на стандартной системе FreeBSD /dev/fd/0-2, поэтому fdescfsих нужно монтировать /dev/fd.
мосви

1
Я фанат информационных разделителей ASCII , которые предназначены для разграничения полей без препятствий. U+001Cэто "Разделитель информации четыре" (разделитель файлов, FS) и идеально подходит для этого случая, хотя двоичные файлы могут использовать его по совпадению. Поэтому я бы предложил EOT="$(printf $'\x1c')"в продвинутых оболочках или же EOT="$(awk 'BEGIN{printf"%c",28}')"для совместимости. Вам нужно будет заключить его в кавычки, когда вы положите его на место, например echo "$EOT$EOT$EOT"(утроено, чтобы снизить шансы совпадения двоичного файла).
Адам Кац

Если вы собираетесь использовать tty, почему бы не использовать ttyкоманду? Если я что-то упустил - что, я полагаю, возможно?
Pryftan

14

Возможно, не совсем то, что вы хотите ... Но, возможно, рассмотрите возможность отправки тарбола через канал, открытый ssh?

Вы сказали, что:

Мне не разрешено хранить файлы на хосте.

Возможно, у вас нет доступного для записи домашнего каталога или другого удобного места для долгосрочного хранения файлов, но я бы сказал, что маловероятно, что у вас нет доступного для записи места, даже если временное tmpfsбудет доступно только для вашего конкретное соединение.

Многие программы (и даже подпрограммы libc) требуют записи /tmp, так что, скорее всего, она будет вам доступна.

Затем вы можете использовать скрипт, который распакует архив во временный каталог, запустит вашу программу и очистит через ssh-соединение.

Что-то вроде:

$ tar cf - file1.txt file2.txt |
  ssh host.name '
      set -e
      tmpdir=$(mktemp -d -t tmp.XXXXXXXXXX)
      cleanup () { rm -rf "$tmpdir"; }
      trap cleanup EXIT
      cd "$tmpdir"
      tar xf -
      /path/to/program -a file1.txt -b file2.txt
  '

Это может потребовать некоторой дополнительной осторожности с путями к файлам, и следует рассмотреть несколько ключевых случаев (проверить их), но общий подход должен работать.

Если доступный для записи каталог недоступен, возможный подход состоит в том, чтобы изменить programтарбол на единый вход и распаковать его содержимое в память. Например, если programэто скрипт на Python, то с помощью встроенного tarfileмодуля легко добиться чего-то подобного.


3
Даже не имея проблем с тарболлингом - я решил более или менее просто написать файл /tmp/и использовать его напрямую.
Зеленый Плащ Гай

2
Подход tarball имеет некоторые преимущества, так как вы используете одно ssh-соединение (нет необходимости проверять успешность / неудачу), и несколько одновременных запусков вряд ли будут сталкиваться друг с другом (используйте и / или удаляйте файлы в / tmp, предназначенные для отдельных запусков Но я думаю, что это лучшее, что вы получите от ssh, так как он существует сейчас. Удачи!
filbranden

3
@GreenCloakGuy Итак, в конце концов, вы можете хранить файлы на сервере очень хорошо. Пожалуйста, измените вопрос соответствующим образом.
Мосви

1
@mosvy может , да, но не хочет . Вопрос стоит в том, «возможно ли это сделать без сохранения каких-либо файлов», ответ на который, по-видимому, «возможно, но не в моем случае»
Green Cloak Guy

5

Если вы можете установить прослушиватель TCP (может быть порт более высокого уровня), то вы можете использовать второй сеанс SSH для установки второго источника ввода с nc .

Пример:

На сервере есть этот скрипт ( ~/script.bash):

#!/usr/bin/env bash
cat "$1" | tr a 1
nc localhost "$2" | tr '[:lower:]' '[:upper:]'

И есть эти два файла локально:

$ cat a 
aaa
aaa
aaa
$ cat b
bbb
bbb
bbb

Теперь сначала запустите второй источник ( $servэто сервер):

ssh "$serv" nc -l -p 2222 -q1 <b &

И правильно запустите команду:

$ ssh "$serv" ./script.bash - 2222 <a
111
111
111
BBB
BBB
BBB
[1]+  Done                    ssh "$serv" nc -l -p 2222 -q1 < b

2

Это было установлено в комментарии, который /tmpдоступен для записи, поэтому просто скопируйте один из файлов заранее:

scp -p file2.txt host.name:/tmp/
ssh host.name "/path/to/program -a /dev/stdin -b /tmp/file2.txt && rm /tmp/file2.txt" < file1.txt

Это также очищает скопированный файл после успешного запуска (измените на &&a, ;если вы хотите удалить его независимо от успеха, но затем обратите внимание, что вы потеряете значение выхода).


Если это неприемлемо, я бы предложил поработать с ним /path/to/programили оберткой, которая может отделить два файла от одного входного потока, такого как:

awk 'FNR == 1 && NR > 1 { printf "%c%c%c", 28, 28, 28 } 1' file1.txt file2.txt \
  | ssh host.name /path/to/tweaked_program

При этом используется разделитель информации ASCII четвертый (разделитель файлов, FS) и он увеличивается в три раза, чтобы минимизировать вероятность совпадения двоичного файла с этой строкой. Вашtweaked_program бы затем разделить на вход данный сепаратор , а затем работать на двух сохраненных файлов в качестве переменных.

Конечно, если вы используете язык, в котором есть библиотеки для работы с тарболлами, более безопасный и понятный подход будет просто заключаться tarв таком коде ssh:

tar -zpc file1.txt file2.txt |ssh host.name /path/to/tweaked_program

И вы tweaked_programраспакуете и откроете архив, сохраните каждый файл в другую переменную, а затем запустите исходную programлогику для переменных.


1

Если вам разрешено перенаправлять порты через ssh, и у вас есть доступ к wgetудаленной машине и к busyboxлокальной машине, вы можете сделать что-то вроде:

mkdir /tmp/test; cd /tmp/test
echo 1st_file > 1st_file
echo 2nd_file > 2nd_file

busybox httpd -f -p 127.0.0.1:11080 &
ssh USER@HOST -R 10080:127.0.0.1:11080 '
        cat <(wget -q -O- http://localhost:10080/1st_file) \
            <(wget -q -O- http://localhost:10080/2nd_file)
'
kill $!

(используя catв качестве примера программу, которая принимает два аргумента файла).

-RНеобходима только возможность переадресации портов через - вместо http вы можете использовать другие методы, например. если ваш netcatподдерживает параметры -dи -N:

nc -Nl localhost 11001 < 1st_file &
nc -Nl localhost 11002 < 2nd_file &
ssh USER@HOST -R 10001:localhost:11001 -R 10002:localhost:11002 '
        cat <(nc -d localhost 10001) <(nc -d localhost 10002)

Могут быть способы заменить <(...)подстановки процессов, если оболочка входа на удаленной машине не похожа на ksh или bash.

В общем, это не так уж и много - лучшее знание о точных настройках системы / оболочек / конфигурации / разрешений (которые вы не предоставили) может позволить найти более разумные решения.


-1

ОБНОВЛЕНИЕ: Это на самом деле не работает, ssh очень четко представляет, что означают stdin и stdout / stderr, и не позволяет узурпировать stderr читать из него. Я удалю этот ответ через несколько дней, так как он не работает. Спасибо за очень интересную дискуссию !!!


К сожалению, нет хорошего способа сделать это напрямую, так как sshклиент будет передавать только три файловых дескриптора ( stdin, stdoutиstderr ) на сервер , и он не имеет положения передать дополнительные дескрипторы файлов (которые были бы полезны для данного конкретного случая использования .)

(Также обратите внимание, что в протоколе SSH предусмотрены условия для передачи дополнительных файловых дескрипторов, только sshклиент не реализует способ использования этой функции. Теоретически, для предоставления этой функции достаточно расширения клиента с помощью патча.)

Один хакерский способ выполнить то, что вы ищете, это использовать дескриптор файла 2 ( stderrдескриптор файла) для передачи второго файла. Что-то вроде:

$ ssh remote.name \
      /path/to/program -a /dev/stdin -b /dev/stderr \
      <file1.txt 2<file2.txt

Это должно работать, это может вызвать проблемы, если programпопытаться записать в stderr. Вы можете обойти это, повторно манипулируя файловыми дескрипторами на удаленном конце перед запуском программы. Вы можете перейти file2.txtк файловому дескриптору 3 и снова открыть stderr для зеркального вывода stdout:

$ ssh remote.name \
      /path/to/program -a /dev/stdin -b /dev/fd/3 \
      '3<&2' '2>&1' \
      <file1.txt 2<file2.txt

Я действительно не проверял это (печатать на телефоне), но, по крайней мере, теоретически я ожидал бы, что это сработает. Дайте мне знать, если по какой-то причине это не так. Ура!
filbranden

Это не сработает вообще. Даже со взломанным ssh. AFAIK stderr использует не отдельный канал, а какой-то особый вид сообщения. Но да, было бы здорово иметь доступ к ssh-каналам как каналы или неназванные сокеты домена unix (вместо того, чтобы делать переадресацию сокетов), просто это кажется невозможным ни в каком виде или форме.
Мосви

@mosvy, fd 0, 1 и 2 в удаленной команде будут 3 разных канала. Данные передаются по 3 различным каналам, мультиплексированным в соединении ssh, но они однонаправлены (от клиента к серверу для fd 0 и от сервера к клиенту для 1 и 2), поэтому даже в системах, где каналы являются двунаправленными, это победило ' т работа. В Linux открытие / dev / stderr в режиме только для чтения дает на другом конце канала, поэтому он никогда не будет работать.
Стефан

Обратите внимание, что в последнем предполагается, что оболочка входа пользователя в систему на remote.name похожа на Bourne ( 3<&2это оператор оболочки Bourne, а sshd запускает оболочку входа пользователя для интерпретации команды, отправленной клиентом)
Стефан Шазелас

2
@ StéphaneChazelas Нет, для stdin / stdout / stderr используется один канал ssh, а данные stderr передаются через SSH_MSG_CHANNEL_EXTENDED_DATAсообщения с типом, установленным в SSH_EXTENDED_DATA_STDERR. Вы можете прочитать все это здесь
Mosvy
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.