Коротко. С authorized_keys на каждом сервере перечислены все открытые ключи, которым разрешён вход, и доступ действует, пока кто-нибудь не удалит строку. С сертификатами OpenSSH серверы доверяют одному центру сертификации (CA) — CA пользователей (TrustedUserCAKeys). Ключ каждого пользователя подписывается командой ssh-keygen -s, которая добавляет к нему идентификатор (-I), учётные записи, под которыми с этим ключом можно войти (принципалы, -n), и срок действия (-V). Клиенты доверяют одному CA хостов (@cert-authority в known_hosts), а не принимают отпечаток каждого хоста при первом подключении. С короткоживущими сертификатами доступ ушедшего из компании человека заканчивается сам, а остальное берут на себя списки отзыва ключей (RevokedKeys). Цена — теперь вы обслуживаете собственный CA и должны защищать его ключ.
Чего не умеет authorized_keys
~/.ssh/authorized_keys прост и не требует ничего обслуживать, поэтому он и используется повсюду. Его ограничения проявляются, когда людей и серверов становится больше:
- Доступ бессрочный. Ключ, добавленный для подрядчика в 2023 году, работает и в 2026 году на каждом сервере, куда его скопировали, если никто его не удалил. (Для каждой строки можно задать
expiry-time=, но кто-то должен это сделать.) - Нет идентификатора владельца. Строка — это ключ и комментарий. Кому он принадлежит (человеку, команде или заданию), записано в электронной таблице, если вообще где-то записано.
- Распространение — ваша забота. Чтобы добавить или удалить человека, нужно изменить каждый сервер: через систему управления конфигурацией или вручную.
- Ключам хостов доверяют при первом подключении (trust on first use). Каждый клиент принимает отпечаток каждого сервера при первом подключении, а после пересборки сервера все клиенты выдают
REMOTE HOST IDENTIFICATION HAS CHANGED.
Сертификаты OpenSSH, доступные с OpenSSH 5.4 (2010), решают все четыре проблемы. Это не X.509: у OpenSSH собственный формат — открытый ключ с идентификационными данными и ограничениями, подписанный ключом CA, а ключ CA — обычный SSH-ключ. В 2025 году описание формата переехало из файла PROTOCOL.certkeys в исходном коде OpenSSH в Internet-Draft, который теперь называется draft-ietf-sshm-cert и разрабатывается в рабочей группе IETF SSHM.
Два CA — два направления
Нужны два отдельных ключа подписи, потому что они отвечают на разные вопросы:
- CA пользователей отвечает на вопрос «может ли этот ключ войти под этой учётной записью?». Ему доверяют серверы.
- CA хостов отвечает на вопрос «действительно ли это
web01?». Ему доверяют клиенты.
$ ssh-keygen -q -t ed25519 -f user_ca -C "Example user CA" -N ""
$ ssh-keygen -q -t ed25519 -f host_ca -C "Example host CA" -N ""
-N "" здесь только для простоты стенда. Настоящий ключ CA должен храниться в HSM, на аппаратном токене или в службе подписи (так умеют механизм секретов SSH в HashiCorp Vault, smallstep, Teleport и аналоги у облачных провайдеров). Тот, у кого есть ключ CA пользователей, может войти от имени любого, кого допускают серверы.
Сертификаты пользователей
Подпишите существующий открытый ключ Алисы на 8 часов для учётных записей alice и deploy:
$ ssh-keygen -s user_ca -I alice@example.test -n alice,deploy -V -5m:+8h -z 1001 id_ed25519.pub
Signed user key /home/alice-laptop/id_ed25519-cert.pub: id "alice@example.test" serial 1001 for alice,deploy valid from 2026-10-20T13:14:08 to 2026-10-20T21:19:08
-I— это идентификатор ключа (key ID). sshd записывает его в журнал при каждом входе, поэтому указывайте в нём человека или задание, а не имя хоста.-nперечисляет принципалы — имена, под которыми может действовать этот сертификат. В руководстве ssh-keygen сказано, что сертификаты без принципалов «действительны для всех пользователей или хостов», но sshd 10.0 отклоняет такой сертификат пользователя, если доверие задано черезTrustedUserCAKeys(Certificate lacks principal list). Всегда задавайте-n.-V -5m:+8hзадаёт срок действия с момента 5 минут назад (запас на расхождение часов) до момента через 8 часов.-zзадаёт серийный номер, на который можно сослаться при отзыве.
ssh-keygen -L показывает, что было подписано:
/home/alice-laptop/id_ed25519-cert.pub:
Type: ssh-ed25519-cert-v01@openssh.com user certificate
Public key: ED25519-CERT SHA256:9Mr4hhu4+c6LnVoj+n8tVj4AKD/AP037BQ5pyEAv07s
Signing CA: ED25519 SHA256:aG9VR0NrG4he7YusGsggg/Gt3WviRbg3Anbfz4HJe60 (using ssh-ed25519)
Key ID: "alice@example.test"
Serial: 1001
Valid: from 2026-10-20T13:14:08 to 2026-10-20T21:19:08
Principals:
alice
deploy
Critical Options: (none)
Extensions:
permit-X11-forwarding
permit-agent-forwarding
permit-port-forwarding
permit-pty
permit-user-rc
По умолчанию сертификат пользователя даёт все пять расширений. -O clear убирает их, после чего можно вернуть только нужные, например -O clear -O permit-pty. Критические опции (critical options) ограничивают сертификат. force-command подменяет любую команду, которую запрашивает клиент, а source-address ограничивает адреса, с которых сертификатом можно пользоваться. С source-address=10.0.0.0/8 вход с 127.0.0.1 отклоняется. Если адрес источника разрешён, принудительная команда выполняется независимо от того, что запросил клиент:
Critical Options:
force-command echo backup only
source-address 10.0.0.0/8
$ ssh deploy@web01.example.test
deploy@web01.example.test: Permission denied (publickey).
cert: Authentication tried for deploy with valid certificate but not from a permitted source address (127.0.0.1).
Refused by certificate options
...
$ ssh deploy@web01.example.test "rm -rf /tmp/anything"
backup only
Starting session: forced-command (key-option) 'echo backup only' for deploy from 127.0.0.1 port 48976 id 0
Клиент ssh находит сертификат автоматически, если тот лежит рядом с закрытым ключом под именем id_ed25519-cert.pub. Иначе укажите путь к нему в CertificateFile. ssh-add загружает в агент и ключ, и сертификат.
Настройка sshd
Вся настройка на стороне сервера занимает несколько строк. Этот подключаемый файл конфигурации прошёл проверку sshd -t, и с ним выполнены все тесты на этой странице:
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
TrustedUserCAKeys /etc/ssh/user_ca.pub
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u
RevokedKeys /etc/ssh/revoked_keys
PasswordAuthentication no
LogLevel VERBOSE
TrustedUserCAKeysсодержит один или несколько открытых ключей CA. Во время ротации CA укажите оба.AuthorizedPrincipalsFileсопоставляет учётные записи с принципалами. Здесь файл/etc/ssh/auth_principals/deployсодержит единственную строкуdeploy, поэтому войти какdeployмогут только сертификаты, в которых указан принципалdeploy. Без этого параметра sshd требует, чтобы среди принципалов было само имя учётной записи. Сопоставление гибче: принципал вродеoncallможно разрешить для многих учётных записей, аAuthorizedPrincipalsCommandможет запрашивать сопоставление в службе каталогов.RevokedKeysуказывает на список отзыва (см. ниже).LogLevel VERBOSEзаписывает в журнал идентификатор ключа и серийный номер сертификата при каждом входе. Такого журнала аудита уauthorized_keysникогда не было.
Вход с сертификатом Алисы глазами клиента и сервера:
Server host certificate: ssh-ed25519-cert-v01@openssh.com SHA256:IuzvI4QsLmSx1+6p3yrvlRx2wdl+LUCY1zXqSzssLAc, serial 0 ID "web01" CA ssh-ed25519 SHA256:+iie/YHPulJReHN2GQ8UvrorBVrtayD2T1MGxeg/QCQ valid from 2026-10-20T13:14:08 to 2027-10-19T13:19:08
Host 'web01.example.test' is known and matches the ED25519-CERT host certificate.
Offering public key: /home/alice-laptop/id_ed25519 ED25519-CERT SHA256:9Mr4hhu4+c6LnVoj+n8tVj4AKD/AP037BQ5pyEAv07s explicit
Server accepts key: /home/alice-laptop/id_ed25519 ED25519-CERT SHA256:9Mr4hhu4+c6LnVoj+n8tVj4AKD/AP037BQ5pyEAv07s explicit
Authenticated to web01.example.test ([127.0.0.1]:22) using "publickey".
Accepted publickey for deploy from 127.0.0.1 port 57432 ssh2: ED25519-CERT SHA256:9Mr4hhu4+c6LnVoj+n8tVj4AKD/AP037BQ5pyEAv07s ID alice@example.test (serial 1001) CA ED25519 SHA256:aG9VR0NrG4he7YusGsggg/Gt3WviRbg3Anbfz4HJe60
Если редактировать sshd_config нельзя, отдельная учётная запись может доверять CA через собственный authorized_keys. Параметр principals= работает так же, как файл принципалов:
cert-authority,principals="deploy-team" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK63...
$ ssh deploy@web01.example.test 'echo logged in as $(whoami)'
logged in as deploy
Сертификаты хостов
Подпишите ключ хоста каждого сервера с помощью CA хостов. С -h получается сертификат хоста, а принципалы в нём — это имена, по которым к серверу подключаются клиенты:
$ ssh-keygen -s host_ca -I web01 -h -n web01.example.test,web01 -V -5m:+52w /etc/ssh/ssh_host_ed25519_key.pub
Signed host key /etc/ssh/ssh_host_ed25519_key-cert.pub: id "web01" serial 0 for web01.example.test,web01 valid from 2026-10-20T13:14:08 to 2027-10-19T13:19:08
Затем укажите полученный файл в HostCertificate (как в файле sshd выше) и вместо отпечатка для каждого хоста добавьте клиентам одну строку в known_hosts (или /etc/ssh/ssh_known_hosts):
@cert-authority *.example.test ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBtSsqoqxwcl31DTJg3kTPsv9eLuuw5gU1BcvYybODlu Example host CA
После этого клиенты доверяют новым и пересобранным серверам с первого подключения и без запроса подтверждения, если в сертификате сервера указано имя, по которому подключился клиент. Именно это последнее условие обычно и застаёт врасплох. Если подключиться по имени, которого нет среди принципалов, клиент переходит к обычным ключам хостов, а при строгой проверке прерывает подключение:
$ ssh deploy@db01.example.test
Certificate invalid: name is not a listed principal
No ED25519 host key is known for db01.example.test and you have requested strict checking.
Host key verification failed.
Перечислите все имена, по которым подключаются клиенты, в том числе короткие имена, CNAME-записи, IP-адреса (если к серверу подключаются по IP) и имена балансировщиков нагрузки. ssh-keyscan -c получает сертификат сервера для анализа, а строки @revoked в known_hosts отклоняют конкретный ключ хоста или CA.
Как выглядят ошибки
При проблемах с сертификатом пользователя клиент всегда выдаёт только Permission denied (publickey). Причина записана в журнале сервера при LogLevel VERBOSE:
| Ситуация | Журнал sshd (OpenSSH 10.0p2) |
|---|---|
| Учётной записи нет среди принципалов сертификата (с учётом сопоставления) | Certificate does not contain an authorized principal |
Сертификат выпущен без -n, AuthorizedPrincipalsFile не задан | Certificate lacks principal list |
Истёк срок действия, заданный в -V | Certificate invalid: expired |
| Серийный номер или ключ есть в KRL | Authentication key ED25519-CERT SHA256:... revoked by file /etc/ssh/revoked_keys |
source-address не совпадает | cert: Authentication tried for deploy with valid certificate but not from a permitted source address (127.0.0.1). |
Две ситуации похожи на проблемы с сертификатами, но к ним не относятся:
- Расхождение часов. Срок действия проверяется по часам сервера. Выпускайте сертификаты, действительные с момента на несколько минут раньше текущего (
-V -5m:...), и синхронизируйте часы серверов по NTP. - Штрафы за повторные неудачи. Начиная с OpenSSH 9.8, sshd штрафует адреса, с которых аутентификация раз за разом не проходит (
PerSourcePenalties, включён по умолчанию). Когда мы проверяли сертификаты с одной машины, после неудачной попытки следующее подключение было сброшено ещё до аутентификации:
srclimit_penalise: 127.0.0.1/32: activating ipv4 penalty of 19 seconds for penalty: failed authentication
$ ssh deploy@web01.example.test
Connection closed by 127.0.0.1 port 22
drop connection #0 from [127.0.0.1]:55080 on [127.0.0.1]:22 penalty: failed authentication
Если ssh сообщает Connection closed сразу после серии неудач, дождитесь окончания штрафа, прежде чем снова менять настройки.
Отзыв через KRL
Главный механизм отзыва — короткий срок действия: сертификат на 8 часов истекает сам. Для остальных случаев в OpenSSH есть списки отзыва ключей (Key Revocation Lists, KRL; появились в OpenSSH 6.2) — компактный двоичный формат, который позволяет отзывать по серийному номеру, идентификатору ключа или самому ключу. Отзыв сертификата Алисы с серийным номером 1001:
$ printf 'serial: 1001\n' > /tmp/krl-spec
$ ssh-keygen -k -f /etc/ssh/revoked_keys -s user_ca.pub /tmp/krl-spec
Revoking from /tmp/krl-spec
$ ssh-keygen -Q -f /etc/ssh/revoked_keys id_ed25519-cert.pub
/home/alice-laptop/id_ed25519-cert.pub (alice@laptop): REVOKED
sshd читает RevokedKeys при каждой аутентификации, поэтому перезапуск не нужен: следующий же вход будет отклонён. Добавьте -u, чтобы обновить существующий KRL, а не заменить его. Распространяйте KRL теми же инструментами, что и ключ CA. Сервер с устаревшим KRL примет и то, что уже отозвано, если не знает об этом. Это ещё одна причина делать сертификаты короткоживущими, а KRL считать страховкой на оставшиеся часы.
Сравнение
| authorized_keys | Сертификаты | |
|---|---|---|
| Выдача доступа | Скопировать ключ на каждый сервер | Один раз подписать ключ |
| Отзыв доступа | Удалить ключ на каждом сервере | Дождаться истечения срока действия или добавить в KRL |
| Срок действия | Необязателен, задаётся для строки (expiry-time=) | Есть у каждого сертификата |
| Идентификатор в журнале | Отпечаток ключа | Идентификатор ключа и серийный номер |
| Ограничения | from=, command=, restrict для каждой строки | source-address, force-command, расширения для каждого сертификата |
| Доверие хостам | Отпечаток для каждого хоста (TOFU) | Одна строка @cert-authority |
| Главный секрет | Единого нет | Закрытый ключ CA |
| Что нужно обслуживать | Ничего | Выпуск, защита CA, распространение KRL |
Ограничения для отдельных строк в authorized_keys действительно работают, это проверено на том же сервере:
restrict,from="10.0.0.0/8",command="echo backup only" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...
$ ssh deploy@web01.example.test id
deploy@web01.example.test: Permission denied (publickey).
/home/deploy/.ssh/authorized_keys:1: Authentication tried for deploy with correct key but not from a permitted host (host=127.0.0.1, ip=127.0.0.1, required=10.0.0.0/8).
expiry-time="20261001" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...
$ ssh deploy@web01.example.test id
deploy@web01.example.test: Permission denied (publickey).
Для нескольких серверов и людей authorized_keys с restrict и expiry-time вполне подходит. Сертификаты окупаются, когда приход и уход людей и пересборка хостов случаются каждую неделю, или когда нужно знать, кто вошёл, а не только каким ключом.
Переход с authorized_keys
Переключать всё в один день не нужно. sshd проверяет authorized_keys и доверенные CA параллельно, поэтому во время перехода работают оба способа входа:
- Сначала настройте доверие к CA. Разверните на всех серверах
TrustedUserCAKeys, файлы принципалов и пустой KRL, аauthorized_keysне трогайте. - Выпустите сертификаты первой команде и дайте ей поработать с ними несколько недель. Теперь каждый вход появляется в журнале с
ID ... (serial ...). - Найдите тех, кто остался на старых ключах. Вход через
authorized_keysзаписывается в журнал без идентификатора сертификата, напримерAccepted publickey for deploy from 127.0.0.1 port 58770 ssh2: ED25519 SHA256:tT5X32Wds5InYaLSh7mV+QRqAtxA4KXwlKV3hkf0YkI. Обычно это автоматизация: раннеры CI, задания резервного копирования, мониторинг. Выдайте каждому из них собственный короткоживущий или строго ограниченный сертификат. - Отключите файлы ключей параметром
AuthorizedKeysFile none, когда легитимных входов через них не останется. Сохраните аварийный путь доступа, например доступ через консоль или отдельный аварийный CA, который хранится офлайн, на тот день, когда служба подписи не работает. - Сторону хостов переводите отдельно. Подпишите ключи хостов и добавьте строку
@cert-authorityв глобальный файл клиентов/etc/ssh/ssh_known_hosts. Старые записиknown_hostsдля отдельных хостов можно оставить. Они работают, пока не сменятся ключи хостов.
Эксплуатация на практике
- Выпускайте сертификаты через поставщика удостоверений (identity provider, IdP). Служба подписи проверяет вход пользователя через SSO (OIDC) и возвращает сертификат с его принципалами на несколько часов. С ключом CA никто не работает напрямую, а после ухода человека из компании его доступ по SSH прекращается к следующему дню без каких-либо действий на серверах.
- По возможности храните ключ пользователя на аппаратном устройстве.
ssh-keygen -t ed25519-skсоздаёт ключ, привязанный к аутентификатору FIDO2. Сертификаты работают с ним так же, как с любым другим ключом. - Проводите ротацию CA с перекрытием. Добавьте новый CA пользователей второй строкой в
TrustedUserCAKeysили новый CA хостов второй строкой@cert-authority. Переключите выпуск сертификатов на новый CA, затем удалите старый. - Следите и за стороной хостов. Переподписывайте ключи хостов до истечения срока действия их сертификатов. Если сертификат хоста истёк, клиенты переходят к обычным ключам хостов, которых у них обычно нет:
Certificate invalid: expired
No ED25519 host key is known for web01.example.test and you have requested strict checking.
Host key verification failed.
Чтобы разобрать файл ключа или сертификата, ничего не запуская, откройте инспектор SSH-ключей: он декодирует ключи OpenSSH и показывает их тип, размер и отпечаток. Аудит SSH-сервера проверяет, что предлагает сервер, включая алгоритмы ключей хоста и сертификатов и постквантовый обмен ключами.
Источники
- Справочные страницы OpenSSH: ssh-keygen(1) (разделы CERTIFICATES и KEY REVOCATION LISTS), sshd_config(5) (
TrustedUserCAKeys,AuthorizedPrincipalsFile,RevokedKeys,HostCertificate,PerSourcePenalties), sshd(8) (форматы authorized_keys и known_hosts). - draft-ietf-sshm-cert: SSH Certificate Format (преемник
PROTOCOL.certkeysиз OpenSSH). PROTOCOL.krl: формат KRL. - Примечания к выпускам OpenSSH: 5.4 (сертификаты), 6.2 (KRL), 9.8 (
PerSourcePenalties).
Весь вывод на этой странице получен в OpenSSH 10.0p2 (пакеты Debian 13) в debian:trixie-20261005-slim 20 октября 2026 года с тестовыми ключами, сгенерированными в контейнере.