MAATRIX / Блог / Ротация SSH-ключей раз в полгода: процедура, после которой никто не теряет доступ

Ротация SSH-ключей раз в полгода: процедура, после которой никто не теряет доступ

MAATRIX

Список authorized_keys на боевом сервере обычно живёт годами без пересмотра: ключи добавляют, когда в команду приходит новый человек, и почти никогда не убирают, когда человек уходит или меняет ноутбук. Через пару лет там оказывается десяток строк, из которых половина принадлежит непонятно кому — бывшему подрядчику, старому CI-раннеру, ноутбуку, который уже сдали в сервис. Регулярная ротация не устраняет риск утечки ключа, но резко сокращает окно, в которое утёкший или забытый ключ вообще на что-то годен. Ниже — рабочая процедура на раз в полгода, которая не оставляет без доступа никого из тех, кому он реально нужен.

Зачем ротировать SSH-ключи, если ничего не взломали

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

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

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

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

С чего начать: инвентаризация, а не сразу смена ключей

Ошибка №1 — начинать ротацию с генерации новых ключей, не разобравшись, что вообще сейчас есть в системе. Сначала соберите реальную картину на каждом сервере:

# все ключи в authorized_keys с комментариями — обычно там email или имя
for f in /home/*/.ssh/authorized_keys /root/.ssh/authorized_keys; do
  [ -f "$f" ] && echo "=== $f ===" && cat -n "$f"
done

# отпечатки (fingerprints) всех ключей — удобно сверять с реестром, не с самой строкой
ssh-keygen -lf /root/.ssh/authorized_keys
ssh-keygen -lf /home/deploy/.ssh/authorized_keys

Дальше сверьте список с тем, кто должен иметь доступ по факту — по штату, по ролям, по действующим договорам с подрядчиками. Каждую строку в authorized_keys нужно уметь подписать: чей это ключ, зачем он тут, когда последний раз использовался. Если владельца установить не удаётся — это не повод для паники, но повод удалить строку прямо сейчас, не дожидаясь плановой ротации.

Заведите реестр — простую таблицу, отдельно от самих ключей (значения ключей туда не кладём, только метаданные):

ВладелецСервер / пользовательFingerprint (последние 8 симв.)Дата выдачиПлановая ротация
Иван П.prod-1..3, deploy...a1:b2:c32026-03-012026-09-01
Мария К.prod-1, root (по sudo)...d4:e5:f62026-03-012026-09-01
CI-раннерprod-1..3, deploy...11:22:332026-06-102026-12-10
Подрядчик (аудит)staging, readonly...99:88:772026-08-01истекает 2026-08-15

Если у вас уже есть документация доступов сотрудников, реестр ключей — её прямое продолжение, а не отдельная система учёта.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Процедура ротации: add → parallel → verify → revoke

Ключевая ошибка ручной ротации — удалить старый ключ сразу после того, как выдали новый, «чтобы не забыть». Именно в этот момент человек, который ещё не успел настроить новый ключ на своей машине (в отпуске, в дороге, просто не увидел уведомление), внезапно остаётся без доступа к серверу. Правильный порядок — строго в четыре этапа, и каждый следующий начинается только после подтверждения предыдущего.

  1. Добавить новый ключ, не трогая старый. Каждый пользователь генерирует новую пару и присылает публичный ключ ответственному за доступы. Новый ключ добавляется в authorized_keys второй строкой, старый остаётся на месте.
  2. Период параллельной работы. Оба ключа — старый и новый — валидны одновременно. Каждый человек переключает свои инструменты (~/.ssh/config, агент, CI-секреты) на новый ключ и в течение оговорённого срока (обычно 1-2 недели) хотя бы раз заходит новым ключом на каждый сервер, к которому у него есть доступ.
  3. Проверить, что все перешли. Ответственный сверяет по логам sshd, что новым ключом уже логинились со всех серверов и от всех пользователей из реестра, а не полагается на устные подтверждения «да, я переключился».
  4. Отключить старый ключ. Только после подтверждённого перехода строка со старым ключом удаляется из authorized_keys на всех серверах, где она была, а в реестре фиксируется дата и факт отзыва.

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

Практика: добавление нового ключа сразу на весь парк серверов

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

# 1. Генерируем новую пару, привязывая комментарий к дате ротации —
#    это сильно упрощает инвентаризацию через полгода
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_2026h2 -C "ivan-2026-09"

# 2. Список серверов, где нужен доступ этого пользователя
hosts="prod-1 prod-2 prod-3 backup-1 bastion"

# 3. Добавляем новый публичный ключ, СТАРЫЙ НЕ ТРОГАЕМ
for h in $hosts; do
  ssh-copy-id -i ~/.ssh/id_ed25519_2026h2.pub deploy@$h
done

# 4. Проверяем вход именно новым ключом, явно указав его,
#    а не полагаясь на ssh-agent, который может подсунуть старый
for h in $hosts; do
  echo "=== $h ==="
  ssh -i ~/.ssh/id_ed25519_2026h2 -o IdentitiesOnly=yes deploy@$h "whoami && hostname"
done

Флаг -o IdentitiesOnly=yes здесь важен: без него ssh-агент может молча подставить старый ключ, если он всё ещё загружен, и проверка ничего не покажет — вы решите, что перешли на новый ключ, хотя фактически продолжаете ходить старым.

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

# на Ubuntu/Debian — journalctl, на системах со старым syslog — /var/log/auth.log
journalctl -u ssh --since "-14 days" | grep "Accepted publickey"

# сверяем, каким конкретно ключом заходили — по отпечатку в конце строки
journalctl -u ssh --since "-14 days" | grep "Accepted publickey" | awk '{print $NF, $0}' | sort -u

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

Сколько держать параллельный период и как закрывать ротацию

Универсального числа нет, но два ориентира работают в большинстве команд:

  • 1-2 недели для активных пользователей, которые заходят на сервер ежедневно или через день — этого достаточно, чтобы каждый успел переключиться в рабочем порядке;
  • до 30 дней для ролей с редким доступом — резервный админ, который логинится раз в месяц для проверки, или подрядчик с ограниченным доступом на техобслуживание.

Финальное отключение старого ключа — не разовое действие «удалил и забыл», а короткий чек-лист:

  1. По логам подтверждено, что за весь параллельный период старым ключом никто не заходил ни на один сервер из реестра.
  2. Каждый пользователь письменно (в трекере, чате, тикете — где угодно, лишь бы была запись) подтвердил, что новый ключ настроен и используется как основной.
  3. Старая строка удаляется из authorized_keys на всех серверах разом — не в разнобой, чтобы не оставить забытый хост с работающим старым ключом:
old_fingerprint_comment="ivan-2026-03"   # комментарий старого ключа, по которому его легко найти

for h in $hosts; do
  ssh admin@$h "sed -i.bak '/$old_fingerprint_comment/d' /home/deploy/.ssh/authorized_keys"
done

Флаг .bak у sed -i сохраняет резервную копию файла на сервере перед правкой — если что-то пойдёт не так (например, комментарий совпал не с той строкой), у вас есть исходный файл для отката за секунды, а не восстановление из бэкапа всей системы.

  1. В реестре доступов проставляется дата отзыва старого ключа и статус — закрыто. Отдельно стоит проверить root-доступ: если кому-то из пользователей когда-то по ошибке добавили публичный ключ и в /root/.ssh/authorized_keys, а не только в свою учётку с sudo, старую строку нужно убрать и там — это отдельный файл, который легко забыть при ротации.

Типичные ошибки ротации ключей

  • Удалить старый ключ раньше, чем все перешли на новый. Самая частая и самая болезненная ошибка — ответственный за ротацию оптимистично предполагает, что раз прошла неделя, значит все уже переключились, и чистит authorized_keys без проверки логов. В результате кто-то из команды (обычно тот, кто был в отпуске или не читал чат) внезапно не может зайти на прод в самый неподходящий момент.
  • Забыть один сервер из парка. Ключ обновили на трёх боевых серверах, а про резервный backup-1 или bastion, через который вообще идёт весь доступ, забыли. Итог — либо доступ пропадает именно там, либо (хуже) старый ключ там остаётся рабочим бессрочно, потому что про этот сервер забывают снова при следующей ротации.
  • Обновить только личный authorized_keys, не тронув root. Если раньше доступ выдавали неаккуратно и один и тот же публичный ключ оказался в нескольких местах — у пользователя и в root — при ротации меняют обычно только «основной» файл, а root-копию не глазами не видят и она продолжает жить годами.
  • Ротировать ключи людей, но не ключи CI/CD и автоматизации. Раннер, который деплоит по SSH, тоже ходит по ключу — и этот ключ нередко выпущен один раз при настройке пайплайна три года назад и с тех пор не менялся, потому что «это же не человек, риска нет». Риск ровно такой же: ключ CI-раннера при утечке даёт доступ к продакшену без всякого MFA.
  • Не проверять реальное использование, полагаться на устные подтверждения. «Да, я вроде переключился» — не основание для отзыва старого ключа. Основание — строчка в логе sshd, показывающая вход новым отпечатком с конкретной машины.
  • Хранить единственную копию нового приватного ключа без passphrase. Ротация ключа без passphrase просто переносит ту же проблему на новый файл — если ноутбук снова потеряется, всё повторится через полгода. Ключ стоит защищать парольной фразой и хранить приватную часть в менеджере ключей (ssh-add с ограниченным временем жизни в агенте), а не голым файлом в ~/.ssh.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Раз в полгода — это обязательный срок, или можно реже?

Полгода — рабочий ориентир для команды среднего размера с несколькими серверами и несколькими людьми, имеющими доступ. Для одного человека с одним ключом на личный сервер можно ротировать реже, раз в год; для широкого доступа с большим числом людей (5+) имеет смысл сократить интервал до квартала.

Что делать, если сотрудник не может переключиться до дедлайна параллельного периода — например, в длительном отпуске?

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

Нужно ли ротировать ключ, если сервер доступен только через VPN и по IP из белого списка?

Дополнительные слои защиты снижают риск, но не отменяют смысл ротации — VPN и firewall защищают от постороннего подключения, а не от ситуации, когда легитимный, но утёкший ключ используется изнутри разрешённой сети.

Как ротировать ключ, если единственный способ попасть на сервер — тот же самый SSH-ключ, который надо менять (нет консольного доступа)?

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

Что делать с ключами, для которых так и не нашёлся владелец при инвентаризации?

Не ждать плановой ротации — удалить сразу после того, как убедились, что ключ не используется ни одним активным процессом или человеком (проверьте по логам за последние несколько месяцев), и завести инцидент в трекере, чтобы разобраться, откуда он взялся.

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

Это отдельный вид доступа, живёт по своим правилам (обычно логин-пароль плюс 2FA, а не ключ), но принцип тот же — регулярный пересмотр списка людей с доступом и отзыв у тех, кому он больше не нужен, стоит держать в том же реестре и на том же полугодовом ритме, для единообразия.

Обсудить статью, задать вопрос или начать новую тему

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

Перейти в сообщество →