Читать 6 мин

Работает ли очистка удаленного процесса после отмены SSH?

Очистка удаленных процессов после отмены SSH требует групп процессов, надежного статуса, тестов дочерних процессов и честного плана на случай отключения.

Работает ли очистка удаленного процесса после отмены SSH?

Отмененный запуск агента не равен остановленной удаленной команде. Локальный процесс может завершиться штатно, пока SSH-транспорт еще работает. Транспорт может исчезнуть, а удаленная оболочка продолжить работу. Оболочка может завершиться, а ее потомки останутся в другой группе процессов. Если свести все эти события к одному статусу «отменено», рано или поздно после сообщения агента об остановке у вас продолжат работать миграция базы данных, установка пакета, worker тестов или помощник развертывания.

Проблему не решит одна хитрая обработка сигналов. Нужен договор об отмене: он должен называть удаленный запуск, создавать границу, которую можно завершить вместе со всеми потомками, сохранять достаточно вывода для диагностики прерванного запуска и предусматривать действия на удаленной стороне, если контроллер исчез. Создайте и проверьте такой договор до того, как разрешите агенту выполнять команды с побочными эффектами.

У отмены SSH есть четыре отдельных перехода

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

RFC 4254 разделяет эти понятия. В нем отдельно описаны сообщение о закрытии канала и запрос канала signal для имен вроде TERM, INT и HUP. Закрытие канала относится к транспорту. Оно не означает «отправить SIGTERM всем удаленным потомкам». В RFC также сказано, что данные, отправленные до закрытия, по возможности должны быть доставлены. Слова «по возможности» многое меняют, когда ноутбук засыпает, сетевой маршрут пропадает или локальный процесс принудительно завершается.

Это различие выявляет распространенную ошибку:

  1. Агент запускает ssh host long-command.
  2. Пользователь нажимает кнопку отмены.
  3. Запуск агента убивает локальный дочерний процесс.
  4. Интерфейс помечает задачу как отмененную.
  5. long-command или один из его потомков продолжает работать на хосте.

Пятый пункт не является редким исключением. Это результат по умолчанию, если серверу не сообщили, что команду нужно завершить, или если команда отсоединилась от сессии до исчезновения соединения.

Хороший договор об отмене точно описывает, что попытается сделать локальная сторона и за что отвечает удаленная сторона:

  • Запускающая сторона создает один распознаваемый удаленный запуск со случайным идентификатором.
  • Удаленная оболочка запускает рабочую нагрузку в собственной группе или сессии процессов.
  • При обычной отмене отправляется TERM этой группе, а результат записывается.
  • Оболочка отправляет KILL только после заданного льготного периода.
  • Удаленный срок действия или аренда завершает работу, если контроллер не возвращается.
  • Вывод и итоговый статус переживают поток SSH.

Не называйте задачу отмененной, пока не получили один из двух результатов: подтвержденную итоговую удаленную запись или явный результат «состояние неизвестно». Уверенность после потери соединения замедляет разбор инцидента, потому что все начинают с ложной предпосылки.

Одного удаленного PID недостаточно для очистки потомков

Убийство PID удаленной оболочки безопасно только тогда, когда оболочка не порождает процессы, не запускает конвейер, не создает фоновый процесс и не вызывает инструмент, который запускает помощников. Таких команд немного.

Рассмотрим обычную удаленную команду:

build-assets | tee build.log &
wait

У оболочки один PID, но у конвейера несколько процессов. tee может продолжить запись журнала после завершения оболочки. Компилятор может создать worker-процессы. Менеджер пакетов может передать работу сервису. Если отправить kill -TERM "$shell_pid", вы завершите одного участника большой группы и почти ничего не узнаете об остальных.

Группы процессов дают правильную единицу отмены для короткого удаленного запуска. В Linux каждый процесс принадлежит группе процессов, а каждая группа процессов входит в сессию. Сигналы, создаваемые терминалом, отправляются группе процессов переднего плана, поэтому поведение терминала иногда кажется более магическим, чем оно есть. Документация Linux для setpgid(2) также уточняет, что дочерний процесс наследует группу родителя, пока что-то не изменит ее.

Для команды, которой управляет агент, создайте для рабочей нагрузки новую сессию. Обычно лидер сессии имеет PID, равный его PGID и SID. Тогда отрицательный PID в kill обращается к группе процессов:

kill -TERM -- -"$pgid"

Начальный минус отличает завершение процесса от завершения его группы. Ключ -- тоже важен: он не дает некорректному значению быть разобранным как опция.

Не предполагайте вслепую, что PID рабочей нагрузки равен ее идентификатору группы. Проверьте это при запуске. Оболочка-обертка, менеджер сервисов или программа, вызывающая setpgid, могут изменить дерево. Это минимальная команда проверки, которую стоит держать в наборе тестов:

ps -o pid=,ppid=,pgid=,sid=,stat=,etime=,command= -p "$pid"

Типичный вывод выглядит так:

24182  24177  24182  24182 Ss       00:03 bash ./worker.sh /tmp/agent-runs/6c4...

Здесь PID, PGID и SID совпадают. Это подтверждает, что kill -TERM -- -24182 обращается к нужной границе. Если PGID не совпадает с записью запуска, завершите запуск с ошибкой, а не гадайте.

У группы процессов все же есть ограничения. Потомок может вызвать setsid, среда контейнеров может переместить процесс, а рабочая нагрузка может попросить менеджер сервисов запустить что-то вне группы. Иногда это законно. Но это означает, что ваша гарантия отмены закончилась на такой передаче. Считайте отсоединенную работу отдельным типом задачи с собственным идентификатором, операцией остановки и записью аудита.

Обертке отмены нужен настоящий путь очистки

Удаленная оболочка-обертка должна владеть PID рабочей нагрузки, обрабатывать ожидаемые сигналы завершения, обращаться к группе нагрузки, немного ждать и записывать итог. Не используйте pkill command-name, не анализируйте неструктурированный список процессов и не убивайте все процессы учетной записи. Эти сокращенные пути работают до того дня, когда два запуска агента используют одного пользователя, имя хоста меняется или имя команды совпадает с чужой работой.

Эта заготовка ориентирована на Linux и намеренно проста. Она создает защищенный каталог запуска, запускает одну рабочую нагрузку в новой сессии, пишет вывод в файлы и завершает группу нагрузки, когда обертка получает TERM, INT или HUP.

#!/usr/bin/env bash
set -Eeuo pipefail

run_id=${1:?run ID required}
shift
run_dir="${HOME}/.agent-runs/${run_id}"
umask 077
mkdir -p "$run_dir"

child_pid=""
child_pgid=""
finished=0

write_status() {
  local state=$1
  local code=${2:-}
  local tmp="$run_dir/status.tmp"
  printf '{"run_id":"%s","state":"%s","exit_code":"%s"}\n' \
    "$run_id" "$state" "$code" >"$tmp"
  mv "$tmp" "$run_dir/status.json"
}

stop_group() {
  if [[ -z ${child_pgid:-} ]]; then
    return
  fi

  kill -TERM -- "-$child_pgid" 2>/dev/null || true
  for _ in 1 2 3 4 5; do
    if ! kill -0 -- "-$child_pgid" 2>/dev/null; then
      return
    fi
    sleep 1
  done
  kill -KILL -- "-$child_pgid" 2>/dev/null || true
}

cancel() {
  local signal=$1
  trap - TERM INT HUP
  write_status "cancelling:$signal"
  stop_group
  wait "$child_pid" 2>/dev/null || true
  write_status "cancelled:$signal"
  finished=1
  exit 143
}

trap 'cancel TERM' TERM
trap 'cancel INT' INT
trap 'cancel HUP' HUP

write_status "starting"
setsid "$@" >"$run_dir/stdout.log" 2>"$run_dir/stderr.log" &
child_pid=$!
child_pgid=$(ps -o pgid= -p "$child_pid" | tr -d ' ')

if [[ "$child_pgid" != "$child_pid" ]]; then
  printf 'unexpected PGID for %s: %s\n' "$child_pid" "$child_pgid" \
    >"$run_dir/stderr.log"
  kill -TERM "$child_pid" 2>/dev/null || true
  write_status "launch_failed"
  exit 70
fi

printf '%s\n' "$child_pid" >"$run_dir/pid"
printf '%s\n' "$child_pgid" >"$run_dir/pgid"
write_status "running"

set +e
wait "$child_pid"
code=$?
set -e

if [[ $finished -eq 0 ]]; then
  write_status "finished" "$code"
fi
exit "$code"

Эта обертка предотвращает одну конкретную ошибку: сигнал отмены доходит до обертки, но она убивает только себя и оставляет рабочую нагрузку. Она не обещает остановить намеренно отсоединенных потомков и не должна этого обещать.

В руководстве Linux для setsid(2) сказано, что setsid() создает новую сессию и делает вызывающий процесс лидером новой группы, изначально без управляющего терминала. Команда util-linux setsid запускает программу в новой сессии и при необходимости создает дочерний процесс. Поэтому это практическая граница для запуска команды, а не волшебный переключатель очистки.

Держите обертку узкой. Она должна запускать, записывать, останавливать и сообщать. Не прячьте в ней бизнес-логику. У рабочей нагрузки по-прежнему должны быть собственные правила обработки транзакций, очистки временных файлов и идемпотентности.

Чистое отключение транспорта не гарантирует очистку

Пользователи SSH часто делают слишком далеко идущие выводы из проверки в терминале. Они запускают команду с PTY, закрывают терминал, видят завершение процесса после SIGHUP и решают, что очистка при отключении работает. Затем агент использует неинтерактивный SSH-канал без PTY, и поведение меняется.

PTY создает семантику терминала. Закрытие терминала может привести к доставке SIGHUP, но только при условиях, связанных с управляющими терминалами и группами процессов переднего плана. В руководстве Linux SIGHUP описан как закрытие управляющего терминала или завершение управляющего процесса. Там не сказано, что каждое отключение SSH сигнализирует каждому процессу, запущенному через SSH.

Для агентов обычно лучше использовать неинтерактивный SSH: вывод получается чище, а сюрпризов от запуска оболочки меньше. Кроме того, вы не зависите случайно от поведения терминала. Используйте PTY только когда он нужен удаленной программе, например старому установщику, который иначе отказывается работать. В таком случае зафиксируйте, что PTY входит в поведение команды, и тестируйте его отдельно.

Есть три случая отключения, которые стоит различать:

Клиент отправляет намеренную отмену

Локальный супервизор все еще поддерживает соединение и может отправить протокольный сигнал либо открыть отдельную аутентифицированную управляющую команду, которая подаст сигнал записанному PGID. Это лучший случай. Удаленная обертка получает TERM, выполняет очистку и записывает cancelled:TERM.

Не полагайтесь на то, что SSH-клиент всегда одинаково обработает локальный SIGINT, если вы не проверили конкретную библиотеку и команду, которые поставляете. Терминальный клиент, встроенная SSH-библиотека и MCP-инструмент могут по-разному отображать локальную отмену. Одни закрывают сокет, другие завершают локальный процесс, третьи могут отправить SSH-запрос signal. Это разные реализации интерфейса, который пользователь называет «отменой».

Локальный клиент падает или теряет сеть

Удаленная команда может продолжить работу. Сервер не может отличить временную проблему маршрутизации от намерения пользователя продолжить работу, если протокол не объясняет ему, что делать. Честный ответ здесь, удаленная аренда.

При запуске запишите deadline_epoch в каталог удаленного запуска. Локальный супервизор продлевает его, пока запуск остается разрешенным. Удаленный watchdog проверяет значение и после истечения срока вызывает тот же путь очистки группы процессов. Выбирайте срок аренды под операцию. Пяти минут может хватить для shell-команды, но это может быть опасно мало для сборки с долгими, но нормальными периодами тишины.

Удаленный хост выходит из строя или перезагружается

Вы можете потерять и процесс, и итоговый статус. Не сообщайте «отменено» или «завершено» только потому, что SSH-соединение оборвалось. Пометьте запуск как неизвестный, пока последующая сверка не прочитает журнал хоста, состояние развертывания, запись блокировки или результат, специфичный для приложения.

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

Частичный вывод подтверждает наблюдение, но не завершение

Направьте SSH через единый шлюз
Используйте встроенный MCP-шлюз, чтобы направлять SSH-действия агента через подписанное приложение Sallyport для macOS.

Поток отвечает на вопрос «какие байты клиент получил до сих пор?». Он не отвечает на вопрос «в каком состоянии удаленная команда оставила систему?». Ошибка возникает, когда удаленная программа печатает done до сброса последнего файла или сеть пропадает после завершения команды, но до получения клиентом кода выхода SSH.

Храните две записи:

  • stdout.log и stderr.log содержат диагностический вывод по мере его появления.
  • status.json содержит небольшую итоговую запись, которую обертка атомарно создает после выхода или обработки отмены.

Команда mv в обертке важна. Сначала запишите временный файл статуса в том же каталоге, затем переименуйте его. Читатель увидит либо полностью старый файл, либо полностью новый. Он не должен разобрать половину JSON и придумать результат.

У вывода тоже должны быть правила. Команда может сильно буферизовать вывод, когда stdout направлен в файл, а не в терминал. Если важен прогресс, пусть рабочая нагрузка пишет явный построчный статус в stderr или использует файл прогресса уровня приложения. Не решайте проблему буферизации выделением PTY для каждой команды. Это меняет поведение и может объединить stdout со stderr, что ухудшит аудит и анализ ошибок.

Различайте эти случаи в интерфейсе агента или журнале:

Удаленная записьСостояние потокаЗначение
finished, код выхода присутствуетзавершенОбертка наблюдала штатное завершение.
cancelled:TERMможет завершиться резкоОбертка начала отмену и остановила группу.
только cancelling:TERMотключенОчистка началась, но итоговая запись не была замечена. Нужна сверка.
только running, аренда действительнаотключенРабота может продолжаться. Не повторяйте запуск вслепую.
нет пригодной записиотключенСостояние неизвестно. Проверьте побочные эффекты до нового запуска.

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

Тестируйте деревья процессов, а не одну сонную оболочку

trap 'exit' TERM; sleep 600 это слабый тест отмены. Он показывает, что одна оболочка переднего плана получила один сигнал. Он не проверяет потомков, группы процессов, отложенную очистку, сохранность вывода или удаленную оболочку, исчезшую в неподходящий момент.

Используйте рабочую нагрузку, которая создает видимое дерево процессов и записывает каждый сигнал. Сохраните ее как worker.sh на временном Linux-хосте:

#!/usr/bin/env bash
set -Eeuo pipefail
run_dir=${1:?run directory required}

note() {
  printf '%s pid=%s pgid=%s %s\n' \
    "$(date +%s)" "$$" "$(ps -o pgid= -p $$ | tr -d ' ')" "$1" \
    >>"$run_dir/worker.log"
}

trap 'note TERM; exit 143' TERM
trap 'note INT; exit 130' INT
trap 'note HUP; exit 129' HUP

(
  trap 'note grandchild_TERM; exit 143' TERM
  trap 'note grandchild_HUP; exit 129' HUP
  while :; do
    note grandchild_tick
    sleep 1
  done
) &
grandchild=$!

note "started grandchild=$grandchild"
while :; do
  note parent_tick
  sleep 1
done

Запустите ее через обертку со случайным идентификатором. В другом SSH-сеансе проверьте дерево процессов и каталог запуска:

run_id=cancel-test-$(date +%s)
ssh host.example './remote-wrapper.sh '"$run_id"' ./worker.sh "'$HOME/.agent-runs/'"$run_id"'"'

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

Затем по очереди проверьте такие сценарии:

  1. Отправьте TERM PID обертки. Убедитесь, что и родитель, и внук записали завершение, а ps не находит процессов в записанном PGID.
  2. Отправьте TERM непосредственно PID рабочей нагрузки. Убедитесь, что безопасность ее дочерних процессов не предполагается автоматически. Этот тест объясняет, почему обертка обращается к группе.
  3. Убейте локальный SSH-клиент, не отправляя удаленный сигнал. Убедитесь, что рабочая нагрузка остается активной до истечения удаленной аренды. Если она останавливается сразу, запишите причину, например поведение PTY при закрытии, и не считайте результат универсальным.
  4. Отключитесь после записи оберткой cancelling:TERM, но до записи итогового статуса. Убедитесь, что сверка отличает неполное наблюдение от нового работающего задания.
  5. Запустите два процесса под одной учетной записью, отмените один и докажите, что другой остается жив. Так выявляются опасные вызовы pkill и очистка всей учетной записи.

Во время тестов используйте ps и pgrep -a -g "$pgid", затем повторите проверку после льготного периода. Сначала проверьте таблицу процессов, а уже потом удаленный файл статуса и журналы. Запись со статусом «отменено» при работающих worker-процессах означает ошибку супервизора, а не безобидное расхождение отчетов.

Сигналы завершения родителя помогают только внутри контролируемого worker-процесса

Не помещайте SSH-ключи в контекст
Sallyport передает учетные данные SSH только во время выполнения действия, поэтому ключ не попадает в контекст агента.

Linux предоставляет PR_SET_PDEATHSIG. С его помощью процесс просит ядро отправить ему сигнал, когда завершается поток, который его создал. Это полезно для небольшого собственного нативного помощника, который создает непосредственного потомка и хочет остановить его после собственной смерти. В обычных случаях настройка переживает execve, но руководство описывает важные исключения, включая смену учетных данных.

Сам по себе этот механизм не решает очистку удаленного агента.

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

Используйте его только как дополнительную защиту там, где дерево процессов принадлежит вам. Например, небольшой Linux-worker может настроить PR_SET_PDEATHSIG перед запуском одной контролируемой дочерней программы, а внешняя обертка по-прежнему владеет группой процессов и арендой. Так вы получаете два детектора отказа с разным охватом. Это не позволяет отказаться от границы группы или удаленной итоговой записи.

То же предупреждение относится к nohup, disown и setsid внутри рабочей нагрузки. Они полезны, когда человек намеренно хочет пережить терминал. Они несовместимы с обещанием, что отмена запуска агента остановит работу. Сделайте выбор явным при запуске.

Второй канал управления часто лучше, чем убийство первого

Уберите права на завершение процессов из агентов
Sallyport выполняет действие SSH через sp-ssh, а ключ SSH остается в зашифрованном хранилище.

Когда агент отменяет выполняющийся SSH-вызов, его собственный локальный контекст уже может завершаться. Если полагаться на этот исчезающий процесс в отправке последнего протокольного сигнала, возникают гонки. Отдельный процесс-супервизор должен владеть отменой и сверкой.

Один из рабочих вариантов выглядит так:

  1. Супервизор создает криптографически случайный идентификатор и вызывает удаленную обертку.
  2. Обертка записывает PID, PGID, время запуска и статус в каталог с этим идентификатором.
  3. Супервизор сохраняет идентификатор и удаленный хост до начала чтения вывода.
  4. При отмене супервизор открывает новое управляющее действие, читает запись запуска, проверяет ожидаемого владельца и возраст, а затем отправляет сигнал записанному PGID.
  5. Супервизор опрашивает итоговую запись, пока не увидит конечный статус или не достигнет срока отчетности.

Перед отправкой сигнала управляющее действие должно проверить запись запуска. Как минимум убедитесь, что каталог принадлежит ожидаемому пользователю, PID все еще существует, записанный PGID совпадает с ps, а время запуска соответствует запущенному процессу. Linux может переиспользовать PID. Старый файл PID и безусловный kill однажды приведут к завершению чужой работы.

Не прячьте команду очистки за общей инструкцией агента вроде «убей процесс моей предыдущей задачи». Агент должен получать непрозрачный идентификатор запуска. Супервизор преобразует его в строго ограниченное удаленное действие. Это также делает аудит понятным: проверяющий видит, что запуск 6c4... запросил отмену PGID 24182 на одном хосте, а не что агент сформировал произвольную команду завершения.

Для автономных агентов программирования Sallyport может выполнять SSH-действия, не раскрывая SSH-ключ агенту. Это отделяет хранение учетных данных от договора об отмене, которому по-прежнему нужны идентификатор запуска, проверка группы, удаленная аренда и итоговая запись.

Правильное решение о повторном запуске зависит от побочного эффекта

Команду, которая только читает файл, обычно можно повторить после неизвестного отключения. Команду, которая создает пользователя, применяет миграцию, меняет сертификат или начинает развертывание, повторять нельзя без проверки. SSH-уровень не знает, безопасно ли снова выполнить операцию.

Дайте удаленным командам с побочными эффектами токен идемпотентности, производный от идентификатора запуска. Удаленная программа должна сохранять токен вместе с результатом операции и возвращать существующий результат, если снова видит тот же токен. Если это невозможно, добавьте предварительный запрос, который определяет, произошло ли требуемое изменение.

Не заменяйте идемпотентность очисткой. Даже идеальный TERM может прийти после того, как удаленный API принял запрос, но до того, как команда вывела ответ. Даже KILL может прийти после фиксации транзакции базы данных. Очистка процессов отвечает на вопрос, продолжает ли worker выполнение. Она не откатывает внешние эффекты.

В системе управления храните три результата: завершено, отменено с подтвержденной очисткой и неизвестно. Неизвестность неудобна, но она дает следующему оператору нужную информацию: проверь удаленное состояние до нового действия. Это намного лучше зеленой кнопки повтора, основанной на надежде.

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

Вопросы и ответы

Как остановить удаленную SSH-команду, когда AI-агент отменяет ее?

Считайте отмену отдельным действием управления, а не просто завершением локального SSH-клиента. Отправьте сигнал записанной удаленной группе процессов, подождите ограниченное время очистки, затем проверьте итоговое состояние запуска по надежной записи. Если канал управления уже исчез, удаленная аренда или watchdog должны решить, когда остановить работу.

Убивает ли закрытие SSH-соединения удаленную команду?

Нет. Закрытие SSH-канала завершает канал, но не обещает, что сервер отправит SIGTERM команде или ее потомкам. PTY может изменить поведение из-за сигналов, связанных с закрытием терминала, но полагаться на это как на договор об очистке нельзя.

Как завершить дочерние процессы, запущенные удаленным shell-скриптом?

Используйте для каждого удаленного запуска отдельную группу или сессию процессов и отправляйте сигнал отрицательному PGID, например kill -TERM -- -12345. Перед этим проверьте PGID. Завершение только PID оболочки оставляет фоновые процессы, конвейеры и подпроцессы.

Стоит ли использовать setsid для удаленных команд агента?

Обычный вызов setsid создает для запущенной команды новую сессию и группу процессов, поэтому супервизор получает одну группу для завершения. Не используйте его для работы, которая должна пережить отмену. Проверьте итоговые SID и PGID через ps, потому что оболочки и менеджеры сервисов могут изменить дерево.

Как очистить процессы после разрыва SSH или сбоя сети?

У удаленного процесса должен быть максимальный срок работы или возобновляемая аренда, если оставлять его запущенным небезопасно или дорого. Аренда должна находиться на удаленном хосте, поскольку отключившемуся клиенту нельзя поручить сообщить о собственном исчезновении. Сигнал отмены быстро останавливает работу, а аренда обрабатывает случай, когда сигнал не пришел.

Можно ли доверять частичному выводу после разрыва SSH-соединения?

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

Достаточно ли PR_SET_PDEATHSIG для очистки удаленных процессов?

Это механизм только для Linux, который помогает процессу заметить завершение непосредственного родителя. PR_SET_PDEATHSIG автоматически не распространяется на внуков процесса, а смена привилегий может сбросить настройку. Используйте его как дополнительную защиту внутри специально созданного worker-процесса, но не как единственный способ остановки произвольных команд.

Стоит ли агенту выделять PTY для удаленных SSH-команд?

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

Что должно входить в аудит отмены SSH?

Записывайте случайный идентификатор запуска, данные удаленного хоста, начальные PID, PGID и SID, время запуска, запрошенную команду и итог. Защитите удаленную запись от других пользователей и делайте итоговое обновление статуса атомарным. Один PID недостаточен, потому что операционная система может переиспользовать PID.

Может ли агент безопасно выполнять опасные действия через SSH?

Не отправляйте ограниченную по времени SSH-команду и не называйте задачу отмененной только потому, что завершился локальный процесс. Требуйте идентификатор запуска, проверку группы процессов, срок очистки и итоговый удаленный статус. Sallyport может не раскрывать SSH-учетные данные агенту, но честность отмены по-прежнему зависит от конструкции удаленной команды.

Sallyport

Sallyport выполняет API-вызовы и SSH-команды за вашего ИИ-агента. Ключи остаются в локальном хранилище на вашем Mac; вы подтверждаете каждый запуск, и каждое действие попадает в запечатанный журнал.

© 2026 Sallyport · Открытый код по лицензии Apache-2.0 · Oleg Sotnikov