Перейти к содержимому

SSH-сертификаты вместо authorized_keys

authorized_keys — это классика, но когда серверов больше десятка, начинается хаос. Добавление нового разработчика превращается в ручное скачивание ключей и раскладку по десяткам машин. SSH-сертификаты решают проблему: один CA-ключ подписывает все публичные ключи, и ни один authorized_keys не нужен.

Зачем authorized_keys — это боль

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

  • отдельный шаг деплоя ключей при онбординге;
  • единая точка отзыва отсутствует — удаление из authorized_keys делается вручную на каждом хосте;
  • ротация ключей затрагивает все машины;
  • нет ограничения по времени действия ключа.

SSH-сертификат подписывает публичный ключ пользователя или хоста центральным CA-ключом. Серверу достаточно доверять этому CA — сам ключ подкладывать не нужно.

Как работают SSH-сертификаты

Два участника: CA (Certificate Authority) и подписываемый ключ. CA — это обычная SSH-пара ed25519 или rsa. Подпись создаётся командой ssh-keygen -s <ca_private> -I <identifier> <key.pub> и порождает файл <key-cert.pub>.

Серверу достаточно двух вещей: публичный ключ CA в TrustedUserCAKeys (для пользователей) или HostCertificate (для хостов). Аутентификация проходит, если подпись валидна и сертификат не просрочен.

Примечание

Сертификат не заменяет ключ. Ключ по-прежнему необходим — он подписывается CA. Сертификат добавляет метаданные: время жизни, principals, расширения.

Генерация CA-ключей

ssh-keygen -t ed25519 -f /etc/ssh/ca_user -C "CA for user certificates"
ssh-keygen -t ed25519 -f /etc/ssh/ca_host -C "CA for host certificates"
ФлагЗначение
-t ed25519Тип ключа, ed25519 рекомендован RFC 8709
-fПуть к файлу ключа
-CКомментарий, удобен для идентификации CA

CA-ключи хранятся в защищённом месте — ideally на отдельной машине или в HSM. Приватный ключ CA никогда не должен попадать на целевые серверы.

Подпись user-сертификата: one-liner

ssh-keygen -s /etc/ssh/ca_user \
  -I "john@devops" \
  -n ubuntu,deploy \
  -V +52w \
  -z 1 \
  ~/.ssh/id_ed25519.pub
ФлагЗначение
-s ca_privateПриватный ключ CA
-I identifierСтрока-идентификатор в логах
-n principalsСписок principals через запятую
-V +52wСрок действия: 52 недели от now
-z serialСерийный номер, полезен для аудита

Результат — файл id_ed25519-cert.pub рядом с ключом. Пользователь подкладывает оба файла на машину, с которой работает. Подпись для другого сотрудника — та же команда с другим ключом и CA.

Подсказка

Формат +52w поддерживает суффиксы h (часы), d (дни), w (недели). +1d — сутки, -1d — вчера (сертификат уже истёк).

Подпись host-сертификатов

На каждом сервере создаётся пара host-ключей (если ещё нет):

ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N ""

Подпись сертификата — на машине с CA:

ssh-keygen -s /etc/ssh/ca_host \
  -I "prod-web-01" \
  -h \
  -n prod-web-01,10.0.1.5 \
  -V +52w \
  /etc/ssh/ssh_host_ed25519_key.pub

Флаг -h превращает подпись в host-сертификат. -n содержит hostname и IP, которые клиент будет сверять при подключении.

Сертификат кладётся рядом с host-ключом:

cp /tmp/ssh_host_ed25519_key-cert.pub /etc/ssh/ssh_host_ed25519_key-cert.pub

sshd_config: CertFile, TrustedUserCAKeys, HostCertificate

Конфигурация sshd на целевом сервере:

# /etc/ssh/sshd_config.d/certs.conf

# Аутентификация по сертификатам
PubkeyAuthentication yes

# CA для пользовательских сертификатов
TrustedUserCAKeys /etc/ssh/ca_user.pub

# Путь к host-сертификату
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub

# Необязательно: файл principals для конкретного пользователя
# AuthorizedPrincipalsFile /etc/ssh/%u.principals

После изменения конфига — проверка и перезагрузка:

sshd -t && systemctl reload sshd
Предупреждение

TrustedUserCAKeys принимает именно публичный ключ CA, не сертификат. Сертификат нужен только для host-ключей.

Ограничение principals и from

При подписи можно задать from — ограничение по IP-адресу источника. Либо в момент подписи:

ssh-keygen -s /etc/ssh/ca_user \
  -I "jenkins@ci" \
  -n deploy \
  -O source-address=10.8.0.0/16 \
  ~/.ssh/id_ed25519.pub

Либо через from в AuthorizedPrincipalsFile на сервере:

# /etc/ssh/deploy.principals
deploy from="10.8.0.0/16"

В сертификате может быть несколько principals — sshd проверит, есть ли хотя бы один совпадающий с AuthorizedPrincipalsFile. Это позволяет выдавать сертификат с ubuntu,deploy,admin и раздавать доступ через разные принципалы на разных хостах.

Время жизни и ротация

TTL задаётся при подписи. Рекомендации из практики:

РольСрокПричина
CI/CD, автоматика24–72 часаКлючи в pipelines живут недолго
Разработчики1–6 месяцевБаланс между безопасностью и удобством
Хосты12 месяцевHost-сертификат привязан к hostname/IP

Ротация — новый сертификат с новым -z serial. Старый автоматически перестаёт работать после истечения. Единая точка отзыва не нужна: истёкший сертификат невалиден, CA самодостаточен.

Fingerprints: проблема host-key

Классический known_hosts хранит fingerprint host-ключа. Host-сертификат ломает эту схему: fingerprint в known_hosts не совпадает с сертификатом. Есть два пути:

Первый — перейти на ssh_known_hosts с ssh-keyscan:

ssh-keyscan -t ed25519 prod-web-01 >> /etc/ssh/ssh_known_hosts

Второй — включить HostKeyAlgorithms с сертификатами, тогда fingerprint игнорируется:

Host prod-web-01
    HostKeyAlgorithms ssh-ed25519-cert-v01@openssh.com
    UpdateHostKeys ask

При первом подключении ssh предложит принять сертификат, добавит его в known_hosts и больше спрашивать не будет.

Troubleshooting

Ошибки разбираются по шагам:

# Проверить, что sshd принял конфиг
sshd -t

# Посмотреть логи аутентификации
journalctl -u sshd -f

# На клиенте —verbose
ssh -vvv user@host

В выводе -vvv смотрите строки Certificateهو и Authentications that can continue. Если видите no matching identity — сертификат не найден рядом с ключом. certificate refused — CA не доверен или principal не совпал.

Проверить содержимое сертификата:

ssh-keygen -Lf ~/.ssh/id_ed25519-cert.pub

Вывод покажет Valid: from ... to ..., Principals:, Serial:. Это первое, что стоит глянуть, если что-то не работает.

Примечание

Ошибка too many authentication failures при наличии сертификата обычно означает, что ssh перебирает все ключи до сертификата. Добавьте -o PubkeyAuthentication=no перед явным указанием ключа, или уберите лишние ключи из .ssh.

SSH-сертификаты убирают необходимость раскладки ключей по хостам. Один CA, подпись с TTL, principals для разграничения доступа. Если в инфраструктуре больше 5–10 серверов — это уже не luxury, а необходимость.