SSH Permission denied (publickey): настоящее решение
SSH permission denied (publickey) значит, что все предложенные ключи отклонены. Четыре реальные причины, проверки через ssh -vvv, которые найдут вашу, и исправления.
В 02:10 деплой умер на строке, которую я набирал тысячу раз: ssh deploy@staging → Permission denied (publickey). Ключ был в порядке. Не в порядке было предложение.
TL;DR: Permission denied (publickey) значит, что каждый ключ, предложенный клиентом, отклонён сервером — это вердикт переговоров, а не опечатка в пароле. Диагностика: ssh -vvv (какие ключи предлагались) и серверный лог journalctl -u ssh (какие ключи отклонены и почему). Дальше исправляйте одну из четырёх причин: не тот пользователь, ключа нет в authorized_keys, слишком открытые права или ключ ssh-rsa, который отвергает OpenSSH 8.8+. ssh-copy-id предотвращает большинство повторов.
$ ssh -T [email protected]
[email protected]: Permission denied (publickey).
Ошибка — это вердикт, а не подсказка: отвергнуто всё, что было в предложении.
Почему SSH пишет «Permission denied (publickey)»?
Аутентификация по публичному ключу — это переговоры в формате «предложил — отверг». Клиент предлагает каждый ключ, который находит: идентичности из агента, из вашего ~/.ssh/config, имена по умолчанию (id_ed25519, id_rsa) и всё, что передано через -i. Сверяет каждый вариант с ~/.ssh/authorized_keys целевого аккаунта. Если ничего не совпало, а вход по паролю отключён или исчерпан, клиент печатает строку, которую каждый devops-инженер знает наизусть.
Для отладки важны два факта. Первый: в сообщении не сказано, под каким пользователем вы стучались — в половине случаев отличный ключ лежит в authorized_keys не того аккаунта. Второй: сервер уже сказал, почему — sshd логирует каждое отклонённое предложение. Тот самый стектрейс, который никто не заказывал, но нужен всем, — это -vvv.
Как узнать, какой ключ SSH предлагает на самом деле?
ssh -vvv deploy@staging 2>&1 | grep -iE "offering|identity|denied|authentications"
# debug1: Offering public key: /home/you/.ssh/id_ed25519 RSA-SHA256 (explicit)
# debug1: Authentications that can continue: publickey
# deploy@staging: Permission denied (publickey).
Читаем три строки: Offering public key (что предложил клиент), Authentications that can continue (что сервер ещё принимает — если password нет, запроса пароля не будет никогда) и финальный вердикт. Если ваш ключ в списке предложений так и не появился, проблема на стороне клиента: неверный путь -i, забытый IdentityFile в ~/.ssh/config или агент (ssh-add -l), который первым отвечает устаревшей идентичностью.
Какие четыре причины реальные?
| Причина | Признак | Исправление |
|---|---|---|
| Не тот пользователь | Ключ работает для root@host, не работает для deploy@host |
Ключ должен лежать в ~/.ssh/authorized_keys того самого пользователя |
| Ключ не установлен | В логе сервера Failed publickey на каждое предложение |
ssh-copy-id user@host |
| Права слишком открыты | Клиент: WARNING: UNPROTECTED PRIVATE KEY FILE — ключ пропускается, не предлагается |
chmod 700 ~/.ssh; chmod 600 ~/.ssh/* |
Старый ssh-rsa |
Сервер на OpenSSH 8.8+; старый RSA-ключ не принимается никогда | Перегенерировать в ed25519 или обновить ключ |
Причину с правами стоит разобрать отдельно: OpenSSH требует её жёстко. Приватный ключ, который читают группа или остальные, отвергается самим клиентом и молча выпадает из предложения. Исправление — две команды:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519 ~/.ssh/authorized_keys
Причина ssh-rsa ловит старые CI-образы и Raspberry Pi: OpenSSH 8.8 (релиз 2021-10-01, openssh.com/txt/release-8.8) отключила подписи ssh-rsa — вариант на SHA-1 — по умолчанию. Из release notes: "the ssh-rsa signature scheme is ... disabled by default". RSA-ключ 2018 года не сломан — его формат подписи просто больше не «в ходу». Генерация ключа ed25519 (ssh-keygen -t ed25519) — прочный ответ, а облачные консоли дают последовательную консоль на случай, если вы отрежете себе доступ посреди починки.
Если sshd отказывается стартовать, пока вы правите его конфиг — это другая охота, см. systemd service not starting.
Как узнать, почему сервер отклонил ключ?
Серверный лог — честный свидетель. В Ubuntu (systemd) sshd логирует вердикт по каждому предложению:
journalctl -u ssh -n 50 --no-pager | grep -iE "publickey|denied|accepted"
# sshd[4421]: Failed publickey for deploy from 203.0.113.7 port 51422 ssh2: RSA SHA256:...
# sshd[4421]: Accepted publickey for deploy from 203.0.113.7 port 51422 ssh2: ED25519 SHA256:...
Failed publickey с незнакомым отпечатком — клиент предложил не тот ключ, что вы думали; назад к -vvv. Failed publickey с вашим отпечатком — ключ верный, но не установлен для этого аккаунта, либо неверные права на home на стороне сервера (то же правило 700/600 для серверных ~/.ssh и ~/.ssh/authorized_keys).
Как правильно установить свой ключ?
ssh-copy-id deploy@staging
# Number of key(s) added: 1
ssh -o BatchMode=yes deploy@staging 'echo ok' # неинтерактивная проверка
ssh-copy-id добавляет ваш публичный ключ в authorized_keys целевого аккаунта с разумными правами — команда существует потому, что ручная вставка в authorized_keys ровно так же часто спотыкается о лишний перевод строки или отсутствующий каталог. Проверка через BatchMode — настоящая: она отключает все запросы, поэтому успех означает, что вход принёс один лишь ключ. Когда ключ работает, scp и rsync наследуют ту же аутентификацию бесплатно — выбор между ними вопрос полосы пропускания, а не учётных данных (rsync vs scp).
Триаж за 30 секунд
ssh -vvv user@host 2>&1 | grep -i offering # 1. какие ключи предлагались?
journalctl -u ssh -n 50 | grep -i publickey # 2. почему отклонены?
ls -ld ~/.ssh ~/.ssh/authorized_keys # 3. 700 / 600?
ssh-copy-id user@host # 4. установить, проверить через BatchMode
Permission denied (publickey)— итог переговоров, а не запрос пароля: сервер отверг каждый ключ из предложения, и его лог уже знает почему.
Четыре команды по порядку — и сбой перестаёт быть загадкой: одна из них каждый раз называет виновника. У меня это была устаревшая идентичность агента, отвечавшая раньше правильного ключа — ssh-add -d, и в 02:31 деплой снова позеленел.