Читать 6 мин

Раздельные SSH-credentials для безопасного доступа к production

Разделяйте SSH-credentials по риску development, staging и production, чтобы ограничить удалённый доступ, предотвратить утечки через agent и упростить отзыв.

Раздельные SSH-credentials для безопасного доступа к production

Один SSH-credential, который даёт доступ к development, staging и production, превращает небольшое удобство в широкие удалённые полномочия. Из-за него согласования тоже становятся формальностью: человек может одобрить безобидную задачу в staging, хотя тот же credential через несколько минут откроет сессию в production.

Разделяйте SSH-credentials по классам систем и по уровню риска. Затем отразите это разделение в конфигурации клиента, закрепите его на серверах и проверяйте запрещённые пути с такой же серьёзностью, как разрешённые. Я видел, как команды называли это контролем доступа, хотя всего лишь давали одному ключу три имени в ~/.ssh/config. Это не граница.

Один credential создаёт одну область отказа

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

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

Поэтому один credential с разными host alias ничего не решает. Такие записи выглядят аккуратно:

Host dev-db
  HostName dev-db.internal

Host prod-db
  HostName prod-db.internal

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

Раздельные credentials меняют результат. Украденный credential development должен завершаться ошибкой при обращении к staging и production. Процесс, которому разрешено использовать staging-credential, не должен иметь закрытого материала или возможности подписи, позволяющих пройти аутентификацию в production. Это разные меры контроля. Первая ограничивает последствия взлома. Вторая не даёт законному, но слишком широкому процессу выполнить неправильное действие.

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

Имен сред недостаточно для границы

Development, staging и production полезны как обозначения только тогда, когда за ними стоят разные решения об уровне доверия. Хост с именем staging может содержать данные, похожие на production, отправлять настоящие письма, хранить секрет подписи или подключаться к рабочему платёжному endpoint. И наоборот, production-хост для мониторинга может требовать меньше полномочий, чем администратор базы данных в staging.

Классифицируйте SSH-доступ по последствиям сессии, а не по префиксу имени хоста. Обычно я начинаю с четырёх вопросов:

  • Может ли этот аккаунт читать данные клиентов или регулируемые данные?
  • Может ли он изменить работающий сервис, развёртывание, firewall или запись DNS?
  • Может ли он получить другой credential или выдать себя за другой сервис?
  • Может ли он перейти к системам с большими полномочиями?

Если ответы различаются для разных хостов, должен различаться и scope credential. Так часто получаются более полезные группы, чем привычная модель из трёх сред: разработка приложений, тестовые системы с чувствительными данными, хосты выпуска, диагностика production только для чтения и администрирование production.

Отдельный Unix-аккаунт тоже часто нужен. Аккаунт deploy может владеть каталогом выпуска и принимать ограниченную команду. ops-read может просматривать журналы, не изменяя service units. admin может выполнять обслуживание по более строгому правилу согласования. Отдельные аккаунты дают серверу место для назначения прав, а журналам - понятного субъекта.

Не позволяйте разным именам аккаунтов создать ложное чувство безопасности. Если один открытый ключ указан для dev, deploy и admin на разных машинах, закрытый ключ всё равно остаётся credential между средами. Отдельные аккаунты и отдельные credentials решают разные части задачи.

Дайте каждому человеку и процессу собственную identity

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

Для человека создайте отдельные закрытые ключи для разрешённых областей. В именах указывайте scope, а не используйте расплывчатые варианты вроде id_ed25519_new или server-key-final.

mkdir -p ~/.ssh/identities
chmod 700 ~/.ssh/identities

ssh-keygen -t ed25519 \
  -f ~/.ssh/identities/id_ed25519_dev_alex \
  -C dev-alex

ssh-keygen -t ed25519 \
  -f ~/.ssh/identities/id_ed25519_stage_alex \
  -C stage-alex

ssh-keygen -t ed25519 \
  -f ~/.ssh/identities/id_ed25519_prod_alex \
  -C prod-alex

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

К автоматизации применяйте ту же дисциплину. Задача выпуска должна использовать credential, выданный этой задаче и этой среде, а не production-identity инженера, скопированную в хранилище секретов. Если доступ нужен нескольким задачам, выдайте им разные identities, если только у них не совпадают владелец, набор целей и полномочия команд. После инцидента credential должен позволять быстро ответить на вопрос: какой процесс им воспользовался?

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

Конфигурация клиента должна предотвращать утечку identity

OpenSSH использует identities из конфигурации и, если это не ограничить, identities, предложенные SSH-agent. Это удобно, пока в agent не оказывается production-identity и подключение, предназначенное для staging, не проходит с её помощью.

В руководстве OpenSSH для ssh_config параметр IdentitiesOnly описан как ограничение, при котором для аутентификации открытым ключом используются только настроенные identity-файлы и сертификаты, даже если agent содержит другие identities. Установите его для каждого scoped alias. Для каждого alias укажите один явный identity-файл и не полагайтесь на порядок, в котором agent предлагает ключи.

Host dev-*
  User devops
  IdentityFile ~/.ssh/identities/id_ed25519_dev_alex
  IdentitiesOnly yes
  ForwardAgent no

Host stage-*
  User release
  IdentityFile ~/.ssh/identities/id_ed25519_stage_alex
  IdentitiesOnly yes
  ForwardAgent no

Host prod-*
  User admin
  IdentityFile ~/.ssh/identities/id_ed25519_prod_alex
  IdentitiesOnly yes
  ForwardAgent no

Используйте alias, по которому среду легко узнать в истории терминала. Например, prod-api-01 лучше, чем api-01, если и development, и production содержат API-хосты с похожими именами. Не прячьте цель за общим alias вроде server.

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

ssh -G prod-api-01 | grep -E '^(hostname|user|identityfile|identitiesonly|forwardagent) '

Вывод должен выглядеть примерно так:

hostname prod-api-01.internal
user admin
identitiesonly yes
forwardagent no
identityfile ~/.ssh/identities/id_ed25519_prod_alex

Путь на вашей машине может раскрываться иначе. Важно, чтобы production-alias разрешался только в production-identity, а identitiesonly имел значение yes.

Частая проблема возникает после запуска ssh-add для удобства. В agent оказывается несколько identities. Без IdentitiesOnly yes клиент перебирает их на хосте, пока одна не сработает. Серверы часто ограничивают число попыток аутентификации, поэтому появляются непонятные ошибки. Хуже того, широкий credential может незаметно обеспечить успешное подключение, и оператор не поймёт, что использовал неправильный scope.

Авторизация на сервере должна повторять разделение

Видите, какой процесс отправляет запрос
Перед использованием SSH карточка запроса показывает новый процесс и его издателя.

Раздельные закрытые ключи работают только тогда, когда каждый сервер принимает соответствующий открытый ключ и отвергает остальные. Помещайте development-ключ на development-хосты, staging-ключ на staging-хосты, а production-ключ только туда, где нужен доступ к production.

Правило очевидно, но плохой шаблон встречается постоянно при срочной настройке: кто-то копирует целиком файл authorized_keys на новый хост. В нём часто хранятся старые identities, ключи бывших подрядчиков, ключи развёртывания и общий ключ администратора. Новый production-хост наследует решения о доступе, которые никто не пересмотрел.

Составляйте список авторизации аккаунта исходя из его задачи. Для deploy-аккаунта используйте ограничения OpenSSH, подходящие для неинтерактивной передачи или операции развёртывания. В руководстве OpenSSH для authorized_keys описан параметр restrict: он отключает перенаправление портов, пересылку agent, X11 forwarding и выделение PTY, если другой параметр явно их не разрешает. Ограниченная запись для развёртывания может выглядеть так:

restrict,from="198.51.100.0/24" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... prod-release-job

Замените пример сети диапазоном, который действительно контролируете. Не добавляйте from= только потому, что он выглядит строго. Если задача запускается с меняющихся адресов или из пула hosted runner, неточное ограничение источника приведёт к сбою и заставит кого-то под давлением убрать все ограничения.

Используйте принудительные команды только для узкой автоматизации. Такая команда может не дать credential развёртывания получить shell, но превращается в обязательство по обслуживанию. Она должна проверять входные данные, безопасно выбирать пути и записывать запрос в журнал. Не применяйте её к аккаунту администратора, полагая, что теперь аккаунт безопасен: людям рано или поздно понадобится shell, и они начнут обходить ограничение.

Для интерактивного администрирования production обычно понятнее отдельный аккаунт с отдельным открытым ключом, чем сложный набор параметров authorized_keys. Ограничивайте аккаунт обычными правами хоста, записывайте действия sudo, если это применимо, и удаляйте открытый ключ, когда доступ больше не нужен.

SSH-сертификаты полезны только при узких claims

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

Но сертификаты не устраняют необходимость в разных центрах доверия. Сертификат с production-principal не должен одновременно давать доступ к development и staging только потому, что один оператор работает со всеми тремя средами. Выпускайте разные сертификаты, используйте разные principals или отдельные центры сертификации, если этого требуют границы администрирования и риска.

Протокол сертификатов OpenSSH различает пользовательские сертификаты, principals, интервалы действия и критические параметры. Это полезно только тогда, когда сервер проверяет principals, а правила выдачи остаются узкими. Сертификат, действующий для каждого аккаунта на каждом хосте, превращает закрытый ключ в широко доверенную identity предъявителя с датой истечения.

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

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

Пересылка agent пересекает невидимую границу

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

Поэтому пересылка особенно опасна для production-сессии, которая сначала попадает на jump host. Если jump host скомпрометирован или от имени этого аккаунта работает недоверенный процесс, он может запрашивать подписи у каждой identity, предложенной вашим agent. Так появляется боковой доступ, которого не было в исходном плане подключения.

Оставьте в конфигурации клиента такую настройку по умолчанию:

Host *
  ForwardAgent no

Затем добавляйте только одно явное исключение, если его требует поддерживаемый процесс. Перед этим проверьте, нельзя ли использовать ProxyJump, отдельный credential bastion или выполнить операцию локально. ProxyJump передаёт SSH-соединение через промежуточный хост, но не открывает этому хосту ваш локальный agent тем же способом.

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

Scope согласования должен совпадать со scope credential

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

Согласование, разрешающее новому процессу использовать SSH-credential, должно указывать среду credential и процесс, который его запросил. Запрос с текстом «запрошена команда ssh» заставляет человека делать слишком много выводов по слишком малому объёму информации.

Здесь команды часто смешивают два разных контроля. Scope credential отвечает на вопрос, где identity может аутентифицироваться. Scope согласования отвечает на вопрос, какой работающий процесс может использовать эту identity. Нужны оба. Отдельные production-credentials не дают процессу development случайно попасть в production, а согласование процесса не позволяет неизвестному локальному процессу воспользоваться доступным production-credential.

Согласование каждой команды кажется безопаснее, и после неприятного инцидента его часто предлагают. В обычном обслуживании это плохо работает: повторяющиеся запросы приучают операторов одобрять их, не читая. Подтверждение каждого использования оставляйте для credentials, само применение которых требует решения человека, например для важной identity администрирования production. Для обычной работы выдавайте авторизацию на сессию, которая заканчивается вместе с процессом, и сохраняйте узкий серверный scope credential.

Sallyport хранит SSH-credentials в зашифрованном хранилище и может требовать согласование для нового процесса агента или подтверждение каждого использования выбранного credential. Это не заменяет серверную авторизацию, но делает границу процесса явной и не передаёт credential agent.

К AI coding agent нужно относиться строже, чем к интерактивному терминалу: он способен быстро выполнить множество команд и может последовать вредной инструкции. По умолчанию дайте ему доступ к development. Если нужен staging, используйте identity только для staging и отдельный одобренный запуск. Доступ к production рассматривайте как отдельную операцию с указанной целью и узким назначением.

Сбой обычно начинается с безобидного исключения

Представьте команду с одним SSH-ключом ops, который принимают хосты development, staging и production. Инженер загружает этот ключ в SSH-agent на время обслуживания production. Позже локальный помощник сборки подключается по SSH к staging, чтобы собрать журналы.

У помощника нет явной настройки IdentityFile, а IdentitiesOnly не задан. OpenSSH перебирает identities из agent. Общий ключ ops подходит, потому что staging его принимает. Теперь у помощника есть staging-сессия, а identity этой сессии также работает в production.

Следует вторая ошибка. На staging-хосте включена пересылка agent, потому что месяц назад кому-то понадобилось разовое подключение. Процесс на этом хосте может запрашивать подписи через пересланный agent. Он попадает на production-хост с той же identity ops. Исходный инженер одобрил сессию обслуживания production, но несвязанный помощник и staging-хост унаследовали её полномочия.

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

Разделённая схема разрывает цепочку сразу в нескольких местах. Помощник использует только staging-credential. Production отвергает его. Пересылка agent отключена. Production-identity доступна лишь явно одобренному production-процессу. Каждая из этих мер помогает сама по себе, а вместе они заставляют неправильное подключение завершиться на раннем этапе.

Ротация и аварийный отзыв требуют отработанного порядка

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

Ротация работает, когда вы сначала добавляете замену, проверяете точный маршрут, а затем удаляете старую identity. Ротация production часто завершается сбоем, если проверяют только ноутбук, а настоящий deployer подключается с другого аккаунта, из другой сети или через другой runner автоматизации.

Для обычной ротации используйте такой порядок:

  1. Создайте credential для замены с тем же узким scope, что и у старого.
  2. Добавьте его открытый ключ или авторизацию сертификата нужному аккаунту и на нужные хосты.
  3. Проверьте настоящую команду из настоящего процесса, включая jump host.
  4. Удалите старую авторизацию и убедитесь, что старый credential теперь не работает.
  5. Запишите fingerprint замены, владельца, scope и дату удаления в реестр доступа.

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

Аварийный отзыв отличается от обычной ротации. Если production-credential мог раскрыться, немедленно удалите его открытый ключ или прекратите принимать сертификат, а затем замените credential. Не ждите планового окна: атакующий не будет соблюдать ваш календарь. Цена этого решения - возможный сбой работы, поэтому узкие credentials так важны: отзыв production-identity развёртывания не должен останавливать development.

Проверяйте запрещённый доступ и изучайте свидетельства

Разделение доступа завершено только тогда, когда неправильный credential не работает и вы можете объяснить почему. После каждого существенного изменения проводите намеренную отрицательную проверку: используйте development-identity против production-хоста и убедитесь, что аутентификация открытым ключом завершается ошибкой. Затем проверьте правильный credential и изучите имя аккаунта и цель в записи подключения.

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

ssh -vvv -o IdentitiesOnly=yes \
  -i ~/.ssh/identities/id_ed25519_dev_alex \
  [email protected]

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

Журналы сервера должны позволять ответить, кто прошёл аутентификацию, какой аккаунт использовал, какой fingerprint открытого ключа сервер принял и откуда пришло соединение. Если несколько людей или процессов используют одну identity, журнал позже не восстановит недостающую атрибуцию.

Проверяйте дрейф границ: development-ключ, добавленный в production-аккаунт, старый credential развёртывания, который всё ещё принимается, production-ключ, загруженный в универсальный agent, или правило wildcard, переопределяющее IdentitiesOnly. Такие изменения часто появляются как временные исправления. Временный SSH-доступ имеет свойство переживать чрезвычайную ситуацию, которая его создала.

Начните с инвентаризации всех открытых ключей, принимаемых на production-хостах, и подпишите каждый по владельцу, процессу, назначению и scope. Запись, которую нельзя описать всеми четырьмя характеристиками, не должна оставаться авторизованной.

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

Нужны ли разные SSH-ключи для development и production?

Используйте отдельные учётные данные, если среды связаны с разными последствиями. В development допустимы эксперименты, а доступ к production может изменить системы для клиентов, раскрыть регулируемые данные или прервать работу сервиса. Если один credential подходит для всех сред, его компрометация несёт риск самой чувствительной из них.

Нужен ли для production отдельный Unix-аккаунт помимо отдельного SSH-credential?

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

Допустимы ли общие SSH-ключи для небольшой команды?

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

Безопасна ли пересылка SSH-agent для доступа к production?

Нет. Через пересылку SSH-agent удалённый процесс может запросить подпись любой загруженной identity, хотя файл закрытого ключа так и не попадёт на удалённый хост. Оставьте ForwardAgent no настройкой по умолчанию и используйте временную, строго ограниченную альтернативу только тогда, когда второе подключение действительно необходимо.

Могут ли SSH-сертификаты заменить отдельные SSH-credentials?

SSH-сертификаты могут упростить выдачу и замену credentials, но не устраняют необходимость в границах. Выпускайте разные сертификаты или principals для development, staging и production, а правила авторизации центра сертификации разделяйте. Один слишком широкий principal создаст тот же широкий доступ в другом формате.

Что предотвращает IdentitiesOnly yes?

IdentitiesOnly yes указывает OpenSSH использовать identities, явно настроенные для этого хоста, вместо попытки применить всё, что загружено в SSH-agent. Это предотвращает случайный вход с более широким credential и ошибки сервера из-за слишком большого числа попыток. Правила авторизации на сервере эта настройка не заменяет.

Как заменить SSH-credentials и не потерять доступ?

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

Как CI или AI-агенту подключаться к production по SSH?

Обычно deploy-credential должен использовать отдельный аккаунт только с теми командами и путями, которые нужны развёртыванию. Если задача это позволяет, отключите интерактивные оболочки, перенаправление портов и пересылку agent. Не используйте для автоматизации credential администратора: автоматизация не сможет безопасно обращаться с такими правами.

Как убедиться, что разделение SSH-доступа действительно работает?

Проверьте итоговую конфигурацию клиента командой ssh -G host-alias, а затем изучите файлы авторизации сервера или централизованные записи identities. После этого намеренно попробуйте подключиться к другой среде и убедитесь, что аутентификация завершается ошибкой. Непроверенная граница остаётся лишь соглашением в именах.

Нужно ли требовать подтверждение человека для каждой SSH-команды?

Запрос перед каждой командой быстро создаёт усталость от подтверждений, и люди начинают одобрять то, что не успели оценить. Запрашивайте согласие, когда новый процесс получает диапазон действия credential, а для credentials с доступом к production используйте отдельные правила. В запросе должны быть указаны процесс и целевая authority, а не просто сказано, что будет запущен SSH.

Sallyport

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

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