Я запускаю ssh на macOS для перенаправления подключений к локальному сокету домена Unix на сокет домена на другом компьютере. Командная строка для вызова ssh примерно следующая:

$ ssh -nNT -L /var/run/some.socket:/var/run/some.socket -o TCPKeepAlive=yes \
    -o ServerAliveCountMax=10 -o ServerAliveInterval=60 user@destination

После выполнения некоторого нагрузочного тестирования я обнаружил, что иногда некоторые клиентские соединения терпят неудачу, и при изучении журналов я обнаружил следующую ошибку, выводимую из ssh в то же время, когда соединения терпят неудачу:

channel 41: open failed: connect failed: open failed
channel 44: open failed: connect failed: open failed
channel 47: open failed: connect failed: open failed
channel 49: open failed: connect failed: open failed
channel 51: open failed: connect failed: open failed
channel 59: open failed: connect failed: open failed
channel 62: open failed: connect failed: open failed
channel 64: open failed: connect failed: open failed

Параметры нагрузочного теста должны запускать 100 одновременных подключений (подключиться, отправить некоторые данные, получить некоторые данные, отключиться, всего должно быть выполнено 10 000 подключений).

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

Другие сообщения на SO с похожими описаниями, по-видимому, охватывают проблему использования localhost с обходным решением для использования 127.0.0.1, что здесь делает его неактуальным, поскольку это не сокет TCP/IP. Кроме того, часть destination в приведенной выше команде уже указана как IP-адрес.

Немного в недоумении, как исправить и отследить проблему. Я попытался использовать -vvv для получения подробного дампа операции ssh без каких-либо результатов (все, что он регистрирует для соответствующих каналов, это то, что сокет был установлен на неблокирующий).

Обратите внимание, что вызов ssh выполняется из скрипта, и вызову предшествует ulimit -n 1024, что должно предоставить более чем достаточно файловых дескрипторов для обслуживания всех сокетов.

1
LB2 28 Июл 2018 в 07:04
Вы проверили / установили ограничение файловых дескрипторов (для процесса ssh)?
 – 
Ralph Rönnquist
28 Июл 2018 в 02:21
@RalphRönnquist, да, вызов ssh продолжается в сценарии вызовом ulimit -n 1024. Обновление вопроса, чтобы внести это уточнение и указать, что он находится в macOS.
 – 
LB2
28 Июл 2018 в 07:01

1 ответ

Лучший ответ
channel 41: open failed: connect failed: open failed

Это сообщение об ошибке означает, что удаленный SSH-сервер не смог выполнить запрос на пересылку TCP, поскольку ему не удалось подключиться к цели туннеля. Последняя часть сообщения «открыть не удалось» — это сообщение об ошибке с удаленного сервера SSH.

Когда вы запускаете SSH с переадресацией портов, переадресация портов работает следующим образом:

  1. Локальный клиент ssh прослушивает TCP-соединения на локальном порту (в вашем случае /var/run/some.socket).
  2. Когда отправитель подключается к локальному порту, клиент ssh отправляет запрос на канал «direct-tcpip» на сервер. Запрос включает цель туннеля (в вашем случае /var/run/some.socket в удаленной системе).
  3. Удаленный SSH-сервер устанавливает TCP-соединение с целью туннеля.
  4. Локальный клиент ssh и удаленный сервер ssh передают данные в обоих направлениях между соответствующими соединениями TCP и каналом direct-tcpip.

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

Вы должны проверить журнал ssh на удаленном сервере. Процесс сервера SSH мог зарегистрировать сообщение о причине сбоя. Кроме того, вы говорите, что это происходит периодически во время нагрузочного теста, поэтому я бы рассмотрел проблемы на стороне сервера, связанные с нагрузкой. На ум приходит пара возможностей:

  1. Приложение в удаленной системе, прослушивающее /var/run/some.socket, недостаточно быстро обрабатывает запросы на подключение, и накапливается невыполненная работа.
  2. Серверный процесс SSH достигает какого-то предела ресурсов (например, количество дескрипторов открытых файлов)
2
Kenster 28 Июл 2018 в 16:04
Я вижу, что это сообщение создается в channel_input_open_failure(...) в файле channels.c. Эта функция отображается как обработчик в обоих случаях: clientloop.c и serverloop.c. Но если он генерируется на стороне сервера (sshd), я не понимаю, как он попадет на клиент (ssh), где он зарегистрирован. Я не могу найти код, который бы это делал. Эта функция будет «вызвана» либо server_input_channel_open, либо client_input_channel_open, либо channel_post_connecting, которые, по-видимому, являются функциями для клиентской и серверной сторон. Так что, если я увижу это из ssh, не будет ли это означать, что это проблема на стороне клиента (ssh)?
 – 
LB2
30 Июл 2018 в 18:44
Также в логах видно, что запрос на тип подключения direct-streamlocal@openssh.com, а не direct-tcpip
 – 
LB2
30 Июл 2018 в 19:46
SSH поддерживает туннелирование в обоих направлениях: клиент-> сервер и сервер-> клиент. Таким образом, и у клиента, и у сервера есть логика для отправки и обработки сообщений о прямых каналах TCP. channel_input_open_failure() — это просто функция, которая форматирует и регистрирует сообщение об ошибке, которое вы получаете. Он вызывается из цикла клиента, поскольку клиент получил сообщение от сервера о том, что ему не удалось выполнить запрос на открытие туннеля TCP.
 – 
Kenster
30 Июл 2018 в 20:00
И direct-streamlocal@openssh.com — это расширение OpenSSH для туннелей, использующих сокеты домена unix; обычный тип канала direct-tcpip поддерживает только домен TCP.
 – 
Kenster
30 Июл 2018 в 20:01
Так где же код, который отправляет информацию об ошибке со стороны сервера, и код на стороне клиента, чтобы получить указанную информацию об ошибке и передать ее функции форматирования? Нигде не могу найти эту функцию.
 – 
LB2
30 Июл 2018 в 22:52