Короткий ответ

Скомпрометированный ключ отзывают везде, где он в authorized_keys, authorized_keys2, cloud-init, gitlab deploy keys, панелях и агентах. Сгенерируйте новую пару на безопасной машине, разложите новый pubkey пользователю admin, удалите старую строку по fingerprint, проверьте Accepted publickey в journal. Не «поменяйте пароль UNIX» вместо отзыва ключа: SSH при PubkeyAuthentication пароль не спрашивает. Не оставляйте старый ключ «на ноутбуке в отпуске».

Хост host.example (10.0.20.10). Параллельно закройте root SSH, если он был открыт: root login.

Симптомы и как отличить

  • Accepted publickey for admin from 203.0.113.5 в нерабочее время;
  • в authorized_keys комментарий ivan-old-laptop или ключ без комментария;
  • один и тот же SHA256:… на десятке серверов;
  • приватный ключ лежит в ~/Downloads, в тикете, в ansible-vault «на время».
СигналНе компрометация ключаКуда
Failed publickey затем парольботыпарольный SSH
Свой IP, свой ноутнормааудит last
Уволенный UNIX-аккаунт ещё активенучётканеиспользуемые учётки
Подозрение на rootkitшире IRне только keys

Возможные причины

  1. Ноутбук украден без disk encryption / без пароля агента.
  2. Ключ без passphrase в CI, в Docker image, в .env.
  3. Копия id_rsa в backup NAS world-readable.
  4. Агент ssh-agent проброшен на скомпрометированный hop (ForwardAgent yes).
  5. Старый ключ сотрудника не сняли с admin (общая учётка).
  6. AuthorizedPrincipals / сертификаты SSH CA: отозвали не тот serial.

Диагностика

1. Какие ключи пускают admin

sudo awk '{print}' /home/admin/.ssh/authorized_keys
sudo ssh-keygen -lf /home/admin/.ssh/authorized_keys
sudo ls -la /home/admin/.ssh/
sudo grep -Rni . /root/.ssh/authorized_keys /home/*/.ssh/authorized_keys 2>/dev/null

Режимы: каталог 700, файл 600, владелец admin. 644 на ключах — sshd может игнорировать файл (StrictModes).

2. Чем входили недавно

sudo journalctl -u ssh --since '30 days ago' --no-pager | grep 'Accepted publickey'
lastlog -u admin
last admin

OpenSSH может логировать fingerprint в Accepted publickey for admin … ssh2: ED25519 SHA256:… — зависит от версии и LogLevel. На 22.04/24.04 при LogLevel VERBOSE чаще виден SHA256. Если только IP — сверяйте по времени с известными hop.

3. Где ещё лежит тот же pubkey

Ищите строку или fingerprint по инвентарю (ansible, gitlab, панели облака, jump host.example). Один хост — не инцидент закрыт.

# на jump-хосте
grep -F 'AAAAC3NzaC1lZDI1NTE5AAAA' /home/admin/.ssh/authorized_keys /etc/ssh/authorized_keys 2>/dev/null || true

Подставьте свой blob, не пример. Не публикуйте украденный приватный ключ в заявке — только fingerprint и comment.

4. Сертификаты CA

sudo sshd -T | grep -Ei 'trustedusercakeys|authorizedprincipals|pubkeyacceptedalgorithms'
sudo ls /etc/ssh/  | grep -i ca

Если вход по SSH-сертификату, удаление строки из authorized_keys недостаточно — нужен KRL (revoked_keys) или перевыпуск CA. Не путайте с обычным pubkey.

Решение

Сценарий A. Обычный pubkey admin на одном хосте

  1. С доверенной машины сгенерируйте новую пару (ed25519, passphrase):
ssh-keygen -t ed25519 -a 64 -f ~/.ssh/admin-host-example-2026 -C 'admin@host.example-2026-09'
  1. Добавьте новый pubkey до удаления старого (консоль открыта):
# на сервере, вставив новую строку
sudo -u admin mkdir -p /home/admin/.ssh
sudo -u admin chmod 700 /home/admin/.ssh
printf '%s\n' 'ssh-ed25519 AAAA... admin@host.example-2026-09' | sudo tee -a /home/admin/.ssh/authorized_keys
sudo chown admin:admin /home/admin/.ssh/authorized_keys
sudo chmod 600 /home/admin/.ssh/authorized_keys
  1. Проверьте вход новым ключом во втором окне.

  2. Удалите старую строку целиком. Не комментируйте «на неделю».

sudo -u admin ssh-keygen -lf /home/admin/.ssh/authorized_keys
  1. На рабочей станции: уберите старый ключ из агента и с диска (после backup в сейф, если нужен forensic copy офлайн).
ssh-add -d ~/.ssh/id_ed25519 2>/dev/null || true
ssh-add -l

Сценарий B. Один ключ на парк

Отзыв — массовая замена authorized_keys и deploy keys. Инвентаризация: ansible grep, cloud metadata ssh-keys, GitHub/GitLab, панели Proxmox, сетевые ОС. Порядок: jump-хост первым, иначе потеряете вход в сегмент.

Не переиспользуйте новый ключ снова на 40 хостов, если политика — per-host или per-user. Минимум: per-person, не shared admin на всех с одним ключом. Shared admin + один ключ = один инцидент на парк.

Сценарий C. ForwardAgent и hop

Если подозреваете проброс агента:

# клиентский конфиг: выключить
# ForwardAgent no

На серверах не держите AllowAgentForwarding yes без нужды (sshd -T | grep allowagentforwarding). Скомпрометированный hop с форвардом = использование ключа без кражи файла.

Сценарий D. Ключ в git

Считайте его сгоревшим независимо от «репозиторий private». История git хранит blob. Ротация + git filter не отменяет отзыв на серверах. Проверьте CI variables.

После отзыва имеет смысл сузить SSH: sshd_config и аудит пользователей.

Как проверить, что проблема устранена

  • Fingerprint старого ключа отсутствует во всех authorized_keys инвентаря.
  • ssh -i old_key admin@10.0.20.10Permission denied.
  • ssh -i new_key admin@10.0.20.10 → код 0.
  • В journal нет Accepted со старым SHA256 (если логируется).
  • Deploy keys в GitLab/панелях обновлены.
  • ssh-add -l на рабочих станциях админов не держит старый ключ.

Если не помогло

  • Вход старым ключом жив: AuthorizedKeysFile указывает на несколько путей (.ssh/authorized_keys .ssh/authorized_keys2), cloud-init при буте дописывает из metadata.
  • Match User admin читает ключи из /etc/ssh/authorized_keys/%u.
  • AuthorizedKeysCommand (LDAP/sss) — правьте каталог, не файл.
  • Сертификат CA не отозван: нужен KRL, не только keys.
  • Пароль UNIX сменили, ключ жив — ожидаемо.
  • Подозрение, что украли новый ключ тем же каналом (тот же незашифрованный ноут) — сначала диск/агент, потом очередная пара.

Профилактика

  • Passphrase на ключе, диск LUKS/BitLocker, таймаут агента.
  • ForwardAgent no по умолчанию; -A только точечно.
  • Именные ключи, комментарий с ФИО и годом, не admin.
  • Инвентарь pubkey (fingerprint → человек → хосты).
  • Отзыв в чеклисте увольнения вместе с UNIX: неиспользуемые учётки.
  • Не коммитить id_ed25519 «даже в private».

FAQ

Нужно ли менять пароль admin?

Да как слой, нет как замена отзыва ключа. При ключах пароль UNIX нужен для sudo/консоли, не для SSH.

Можно ли добавить from=«IP» вместо отзыва?

from="10.0.20.0/24" сужает украденный ключ, не лечит компрометацию. После кражи всё равно ротация.

Что делать с ключом в ssh-agent на телефоне?

Считайте отдельной копией. Удалите identity, перевыпустите. Агент с разблокированным ключом = ключ без passphrase на время сессии.

Host key сервера украден?

Это другая история (MITM/known_hosts). Ротируйте host key (ssh-keygen -A осознанно, рассылка нового FP). Не путайте с user key.

FIDO2/sk-ключи лучше?

Да против кражи файла: без токена приватник не уезжает. Это не отменяет authorized_keys hygiene и отзыв serial/credential при потере токена.