
Почему OpenSSH_8.4p1 завершает другие сеансы, которые используют то же соединение, когда используется ProxyCommand? Есть ли способ предотвратить это?
Примечание: такое поведение, по-видимому, не происходит, если аргумент ProxyCommand опущен.
Действия по воспроизведению:
- Завершите все существующие общие подключения к localhost:
ssh -o ControlPath=/tmp/%C -O exit 127.0.0.1 2>/dev/null
ssh -o ControlPath=/tmp/%C -O exit localhost 2>/dev/null
- Выполните следующую команду дважды параллельно, каждую в отдельном терминале:
ssh -F none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ControlMaster=auto -o ControlPath=/tmp/%C -o ControlPersist=1d \
-o ProxyCommand='ssh -W %h:%p 127.0.0.1' \
localhost 'sleep 3600'
- Прервите первый процесс ssh с помощью SIGINT, набрав control-C.
Ожидаемое поведение
- Завершается только процесс, обработанный SIGINT.
- Другие процессы продолжают работать без изменений.
Фактическое поведение
- Оба процесса завершены.
решение1
Завершается только процесс, обработанный SIGINT.
«Процесс» — ложная предпосылка. Ctrl+C отправляет SIGINT
на передний план процессгруппатерминалаГруппа процессов может включать более одного процесса.
Тогда это актуально:
ProxyCommand
Указывает команду, используемую для подключения к серверу. Командная строка продолжается до конца строки и выполняется с использованиемexec
директивы оболочки пользователя, чтобы избежать затягивания процесса оболочки.
[…]
(источник)
Команда выполняется локально в той же группе процессов, что и main ssh
. В вашем примере команда — , ssh …
но в целом это может быть что угодно. В любом случае команда не знает об этом, ControlMaster
и ControlPersist
вы использовали для main ssh
.
Когда вы нажимаете Ctrl+ C, каждый процесс в группе процессов переднего плана получает SIGINT
. "Основной" процесс ssh
завершается, не влияя на сокет в , ControlPath
поскольку в случае, ControlPersist
отличном от no
этого, сокет с самого начала обрабатываетсяеще один ssh
процесс, который намеренно порождается в своей собственной группе процессов, это позволяет ему выживать. В этом контексте вы можете назвать его действительно основным ssh
.
Неожиданное поведение связано с тем, что команда, указанная с , ProxyCommand
порождается в группе процессов переднего плана и не взаимодействует SIGINT
с , которую ssh
вы хотите прервать. Команда реагирует на сигнал так, как если бы она это делала обычно. В вашем случае команда завершается на SIGINT
. И поскольку команда должна была передавать все данные, главное соединение (действительно основное ssh
) теперь бесполезно. Оно завершается вместе со всеми зависимыми ssh
процессами.
Таким образом, оригинал ssh
выполняет дополнительную работу, чтобы гарантировать, ssh
что обработка главного соединения (и сокета) выживет Ctrl+ C, но он не делает этого для команды, указанной с помощью, ProxyCommand
которая не менее важна. Я думаю, вы можете назвать это ошибкой.
Вероятно, было бы лучше, если бы команда, указанная в, ProxyCommand
была порождена иммунным ssh
вегоГруппа процессов. Я не проанализировал это достаточно тщательно. В любом случае, пока это не так, команда порождается в группе процессов, которая является группой процессов переднего плана в момент нажатия Ctrl+ C, поэтому она получает SIGINT
.
Обходной путь — сделать команду невосприимчивой к SIGINT
. Внутри ProxyCommand
вы не можете использовать trap
напрямую, потому что ProxyCommand
uses exec
автоматически и exec trap …
не имеет смысла и не будет работать. Вам нужна еще одна оболочка:
ssh -F none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o ControlMaster=auto -o ControlPath=/tmp/%C -o ControlPersist=1d \
-o ProxyCommand='sh -c "trap \"\" INT; exec ssh -W %h:%p 127.0.0.1"' \
localhost 'sleep 3600'
Мои тесты показывают, что иммунитет SIGINT
не помешает этому внутреннему устройству ssh
выйти, когда придет время, и основное соединение должно будет прерваться из-за конечной ControlPersist
настройки.