Читать 8 мин

Как проверить SSH-помощник перед запуском

Разбираем, как проверить SSH-помощник в macOS: путь внутри пакета, подпись, владельца, идентичность файла и требование при запуске.

Как проверить SSH-помощник перед запуском

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

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

Определяйте помощник по запущенному пакету

Получайте путь к помощнику из пакета приложения, которое действительно запущено. Не берите его из PATH, текущего каталога, настроек или данных от агента. Ожидаемое расположение должно быть константой сборки. Шлюз, который ищет sp-ssh, уже разрешил окружению выбирать исполняемый код.

Apple описывает стандартные места для вложенного кода, включая Contents/MacOS и Contents/Helpers. Выберите одно место, храните там только код и останавливайте сборку, если помощник оказался где-то еще. Сначала подписывайте помощник, внешнее приложение подписывайте последним. Тогда внешняя подпись запечатает ссылку на вложенный код.

Foundation умеет находить вспомогательный исполняемый файл, но решение о доверии все равно должно проверить его точное отношение к главному пакету. Разрешите символические ссылки, нормализуйте оба URL и сравните компоненты пути, а не строковые префиксы. Проверка вроде candidate.path.hasPrefix(bundle.path) примет соседний путь /Applications/Good.app.backup и может ошибиться из-за регистра или нормализации. Кандидат должен точно совпасть с единственным ожидаемым URL внутри запущенного пакета.

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

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

Сначала откройте файл, затем проверьте его идентичность

Откройте кандидата с O_NOFOLLOW, оставьте дескриптор открытым и вызовите для него fstat. Порядок важен. Если сначала вызвать lstat, проверить результат, а потом выполнить open, другой процесс сможет заменить запись каталога между этими операциями.

Secure Coding Guide от Apple именно поэтому рекомендует операции с дескрипторами. После открытия руководство советует проверить тип, UID, GID, режим и число ссылок. Для исполняемого помощника предварительная проверка может выглядеть так:

#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>

int inspect_helper(const char *path, uid_t expected_uid, struct stat *snapshot) {
    int fd = open(path, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
    if (fd < 0) return -1;

    struct stat st;
    if (fstat(fd, &st) != 0 ||
        !S_ISREG(st.st_mode) ||
        st.st_uid != expected_uid ||
        (st.st_mode & (S_IWGRP | S_IWOTH)) != 0 ||
        (st.st_mode & S_IXUSR) == 0 ||
        st.st_nlink != 1) {
        int saved = errno ? errno : EPERM;
        close(fd);
        errno = saved;
        return -1;
    }

    *snapshot = st;
    return fd;
}

Значение expected_uid задает модель установки, его нельзя брать из самого файла. При установке под управлением системы владельцем может быть root. Приложение для одного пользователя вполне может принадлежать этому пользователю. Не требуйте UID 0 только потому, что root кажется надежным. Apple также предупреждает, что путь может перейти на другую смонтированную файловую систему, где один лишь владелец сообщает меньше, чем часто думают разработчики.

Правило о числе ссылок нужно принять осознанно. У обычного исполняемого файла внутри пакета обычно одна жесткая ссылка, поэтому другое значение разумно отклонять. Если система упаковки намеренно создает жесткие ссылки, зафиксируйте это и проверяйте ожидаемое число. Если убрать проверку после неожиданного результата сборки, останется другое имя, через которое можно менять тот же inode.

Сохраняйте дескриптор и снимок stat до завершения запуска. Они не делают запуск по пути атомарным, но помогают обнаружить замену, записать устройство и inode и объяснить отказ, не открывая повторно имя под контролем злоумышленника.

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

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

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

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

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

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

Проверяйте идентичность, а не только валидность подписи

Валидная подпись показывает, что подписанный код по-прежнему соответствует этой подписи. Она не подтверждает, что код подписала ваша команда или что файл и есть ваш помощник. В macOS код может иметь ad hoc подпись, а другой разработчик может выпустить код с полностью валидной подписью.

Используйте Code Signing Services с явным требованием, в котором указаны ожидаемые идентификатор подписи и команда для конкретного канала распространения. В Apple TN3127 разница сформулирована четко: идентификатор подписи выбирает подписант, удостоверение подписи содержит сертификат и закрытый ключ, а designated requirement определяет, какой код считается тем же самым между версиями. Сравнение отображаемой строки Authority годится для диагностики, но не для границы безопасности продукта.

Создайте SecStaticCode для абсолютного URL помощника, скомпилируйте или загрузите требование и вызовите SecStaticCodeCheckValidityWithErrors. Добавьте kSecCSStrictValidate и kSecCSCheckAllArchitectures. Apple указывает, что по умолчанию может проверяться только нативная архитектура универсального бинарного файла. Проверка всех срезов не позволит другой или поврежденной подписи спрятаться в непроверенной архитектуре.

Не используйте здесь kSecCSBasicValidateOnly. Этот флаг пропускает проверку главного исполняемого файла и ресурсов, лишая проверку целостности смысла. Не доверяйте и собственному designated requirement помощника без сравнения с требованием, которым управляет шлюз. Самоописание не дает разрешения.

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

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

Статическая проверка не закрывает гонку запуска

Запускайте через встроенный канал
Помощник sp-ssh без состояния выполняет SSH-действия, не сохраняя ключи хранилища.

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

Сбой укладывается в четыре действия:

  1. Шлюз разрешает /Applications/Example.app/Contents/Helpers/runner и проверяет файл A.
  2. Конкурирующий процесс переименовывает файл B в этот путь.
  3. Шлюз просит Process или posix_spawn выполнить путь.
  4. Ядро открывает B, потому что вызов получил имя, а не сохраненный дескриптор A.

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

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

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

Свяжите требование с созданием процесса

В системах с LightweightCodeRequirements задайте Process.launchRequirement до вызова run. Apple сообщает, что система не запускает процесс и создает отчет о сбое, если исполняемый файл не удовлетворяет LaunchCodeRequirement. Решающее определение идентичности становится частью создания процесса, когда выбранный путь уже не может отделиться от решения.

Основная настройка Swift короткая:

import Foundation
import LightweightCodeRequirements

func configuredProcess(helper: URL, team: String, identifier: String) throws -> Process {
    let requirement = try LaunchCodeRequirement.allOf {
        ValidationCategory(.developerID)
        TeamIdentifier(team)
        SigningIdentifier(identifier)
    }

    let process = Process()
    process.executableURL = helper
    process.launchRequirement = requirement
    return process
}

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

Применяйте требование к помощнику Mach-O, а не к shell-обертке. Apple отмечает, что для скрипта с shebang требование проверяет интерпретатор. Подтверждение того, что /bin/bash подписан Apple, ничего не говорит о байтах скрипта. Перенесите логику в подписанный исполняемый код либо считайте скрипт запечатанными данными, которые читает доверенный код, не запуская их как привилегированный исполнитель.

Включайте API по доступности SDK и сохраняйте статическую проверку для диагностики. На старых целевых системах POSIX_SPAWN_START_SUSPENDED может создать дочерний процесс приостановленным до выполнения пользовательского кода. Получите его динамический SecCode по PID, проверьте требование, затем продолжите или завершите процесс. Такой запасной путь сложнее: проверяйте каждый результат, исключите путаницу PID, закрывайте лишние дескрипторы и не отправляйте команду или учетные данные до успеха. Если модель угроз не допускает эту сложность, требуйте версию системы с поддержкой launch requirement.

Запускайте с узким контрактом процесса

Оставьте зашифрованный след действий
Каждый вызов попадает в Activity через зашифрованный журнал с цепочкой хешей.

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

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

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

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

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

Поместите проверку внутрь авторизации

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

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

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

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

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

Параллельные запуски не должны делить изменяемую конфигурацию. У каждой попытки должны быть собственные неизменяемые URL, ссылка на требование, аргументы, окружение, дескрипторы, срок и идентификатор аудита. Если один поток меняет глобальный шаблон Process, пока другой вызывает run, одобренный запрос может попасть не тому файлу или не в то окружение, даже когда оба файла валидны. Синхронизируйте короткий переход, который расходует разрешение и запускает настроенный процесс, а не весь срок SSH-соединения.

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

Такой порядок дает понятный журнал. Одна попытка может содержать request_received, candidate_preflight_passed, authorization_granted, launch_requirement_passed, request_released и итог. Пропавшие переходы сразу видны. Если запись перескакивает от одобрения к коду SSH, невозможно понять, получил ли ожидаемый исполнитель команду.

Проверяйте фактически запущенный дочерний процесс

Закройте хранилище перед запуском
Когда аппаратно защищенное хранилище Sallyport закрыто, все действия отклоняются.

Контроль при запуске должен быть решающим барьером, но наблюдение после запуска ловит ошибки интеграции и дает стабильную идентичность процесса. После успешного run получите PID и динамический SecCode запущенного процесса. До подключения парсера или передачи чувствительных данных проверьте его по соответствующему требованию процесса.

Различие Apple между SecStaticCode и SecCode здесь полезно. Статический объект описывает код на диске и сам по себе не связан с выполняемым кодом. Динамический объект представляет код, загруженный в процесс. Проверки отвечают на разные вопросы: статическая показывает целостность установленного кандидата, динамическая подтверждает идентичность, которую macOS присвоила текущему дочернему процессу.

Поиск только по PID имеет острые углы. PID переиспользуются, а короткий процесс может завершиться между run, поиском и проверкой. Невозможность получить или проверить объект считайте ошибкой запуска. Если IPC API предоставляет audit token, используйте эту более сильную ссылку вместо PID из сообщения. Не доверяйте дочернему процессу сообщать собственный PID, идентификатор подписи или путь.

Проверка после старта не должна быть единственной на платформе, где дочерний процесс сначала получает CPU. Подмененный процесс может действовать между spawn и проверкой. На старых системах приостановка до пользовательского кода уменьшает окно, но реализация должна продолжать только проверенный PID и завершать его при любой ошибке. Launch requirement проще, поскольку система отклоняет несовпадение до выполнения.

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

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

У идентичности должен быть срок действия. Если помощник выполняет другую программу через exec, подтвержденная идентичность не переходит к новому образу. Не стройте схему, где подписанная обертка проходит проверку, а затем вызывает произвольный ssh через PATH. Если переход exec нужен, конечному файлу требуются собственное требование и фиксированный путь, либо доверенный помощник должен сам выполнить протокол. Подпись запускающего файла ничего не говорит о программе, которую он выберет позже.

Следите и за библиотеками с конфигурацией. Главный исполняемый файл может удовлетворять требованию, а опасные переменные или доступные для записи пути поиска изменят его поведение. Подписывайте зависимости, включайте подходящие параметры Hardened Runtime, применяйте Library Validation, если она совместима, и удаляйте пути поиска из окружения. Конфигурация должна приходить как проверенный шлюзом ввод, а не находиться помощником среди dotfile домашнего каталога.

Наконец, проверяйте утверждения телеметрии. В тесте гонки статический снимок должен определить A, контроль запуска должен запустить A или отказать B, а динамическая проверка должна совпасть с результатом. Если журнал сообщает об успехе A, хотя без записи выполнялся B, модель доказательств все еще следует за именем, а не за процессом.

Отказывайте безопасно, не ломая обновления

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

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

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

Тестируйте замену, а не только корректные подписи. Набор выпуска должен покрывать символическую ссылку вместо помощника, вторую жесткую ссылку, каталоги с записью для группы, ad hoc подписанную замену, правильно подписанный файл с неверным идентификатором, поврежденный ненативный срез, подмену между preflight и запуском и обновление во время работы приложения. Тестовая пауза после статической проверки делает гонку воспроизводимой.

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

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

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

Почему недостаточно проверить путь к SSH-помощнику?

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

Стоит ли приложению macOS искать помощник через PATH?

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

Какие метаданные файла нужно проверить?

После открытия с O_NOFOLLOW вызовите fstat и подтвердите обычный тип файла, владельца, права на запись и число жестких ссылок. Сохраните устройство и inode для сравнения и журнала.

Доказывает ли валидная подпись, что помощник принадлежит мне?

Нет. Она подтверждает соответствие подписи, но macOS может запускать код другого разработчика или с ad hoc подписью. Задайте требование для ожидаемых идентификатора, команды и категории распространения.

Зачем проверять все архитектуры универсального помощника?

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

Устраняет ли открытый дескриптор гонку замены?

Он стабилизирует проверяемый файл, но Process и posix_spawn все равно выбирают исполняемый файл по пути. Используйте дескриптор для снимков, а идентичность связывайте с процессом требованием запуска.

Что добавляет Process.launchRequirement?

Он просит систему оценить LaunchCodeRequirement во время запуска. При несовпадении процесс не выполняется, поэтому разрыв после статической проверки закрывается.

Полезны ли владелец и права при наличии подписи?

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

Нужно ли кешировать успешную проверку помощника?

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

Как шлюз должен реагировать на ошибку проверки?

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

Sallyport

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

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