InfraAcademy

InfraAcademy Blog

Permission denied (publickey) で入れない。ssh -v とサーバー側のログから、鍵認証が断られた理由を突き止める

| #Linux #ssh #トラブルシューティング
Linuxをブラウザで試してみる

Linux・ネットワーク・AWSを、環境構築なしで実践学習できます

この記事を共有

こんにちは、インフラエンジニアのryuです。

公開鍵をサーバーに登録した。手順書どおりにやったはずなのに、接続するとこう返ってくる。

$ ssh ryu@192.0.2.20
ryu@192.0.2.20: Permission denied (publickey).

このメッセージ、見覚えのある方は多いのではないでしょうか。

困るのは、このエラーが何も教えてくれないことです。鍵が違うのか、ファイルの権限が悪いのか、そもそもユーザー名を間違えているのか。どの原因でも、出てくる文字は同じです。

でも、これはSSHが意地悪をしているわけではありません。攻撃者に手がかりを与えないように、断った理由はクライアントに返さず、サーバー側のログにだけ残す設計になっているのです。

つまり、理由はちゃんとどこかに書かれています。今日は、その理由を3か所から読み出す手順を紹介します。

Permission denied (publickey) は、どこで起きているのか?

まず、鍵認証がどんな順番で進むのかを押さえておきます。流れが分かると、どこで止まったかを考えやすくなります。

鍵認証は、ざっくり言うと次のようなやり取りです。

順番 誰が 何をする
1 クライアント 手元にある鍵の中から、試す鍵を選ぶ
2 クライアント 「この公開鍵で入りたい」とサーバーに申し出る
3 サーバー そのユーザーの authorized_keys を読み、同じ公開鍵があるか探す
4 サーバー ファイルやディレクトリの権限が安全かを確かめる
5 クライアント 秘密鍵で署名して、本人であることを証明する

マンションのオートロックにたとえると分かりやすいかもしれません。

住人(クライアント)は鍵束から1本選んで差し込みます。管理人(サーバー)は、登録された鍵の台帳にその鍵があるかを確認します。ただし、台帳が誰でも書き換えられる場所に置かれていたら、管理人は台帳ごと信用しません。

Permission denied は、このどこかで断られたという結果だけを伝えています。どこで断られたかは、クライアントとサーバーの両方の記録を見て判断します。

見る場所は、次の3つです。

  1. クライアント側の ssh -v の出力(どの鍵を出したか)
  2. サーバー側の認証ログ(なぜ断ったか)
  3. サーバー側の sshd -T(実際に効いている設定は何か)

SSHの基本的な使い方や設定ファイルの場所に自信がない方は、先にsshコマンドの使い方SSHの設定方法を読んでおくと、この先がスムーズです。

クライアント側:ssh -v で「どの鍵を出したか」を見る

最初に見るのは手元です。-v を付けると、SSHが裏でやっていることを話してくれます。-vvv まで増やすと、さらに細かくなります。

ssh -v ryu@192.0.2.20

出力は長いですが、鍵認証に関係するのは後半のこのあたりです(環境によって細部は異なります)。

debug1: Authentications that can continue: publickey
debug1: Next authentication method: publickey
debug1: Offering public key: /home/ryu/.ssh/id_ed25519 ED25519 SHA256:xxxx
debug1: Authentications that can continue: publickey
debug1: No more authentication methods to try.
ryu@192.0.2.20: Permission denied (publickey).

読むべきは「Offering public key」の行

いちばん大事なのは Offering public key の行です。ここに、クライアントが実際にサーバーへ差し出した鍵のファイル名が出ます。

よくあるのが、サーバーに登録した鍵と、差し出している鍵が違うケースです。たとえば、サーバーには id_rsa.pub の中身を登録したのに、クライアントは id_ed25519 を出している。これでは台帳に載っていない鍵を差し込んでいるのと同じです。

鍵を明示したいときは、-i で指定します。

# 使う鍵を指定し、ssh-agent などの他の鍵は試さない
ssh -v -i ~/.ssh/work_ed25519 -o IdentitiesOnly=yes ryu@192.0.2.20

IdentitiesOnly=yes を付けているのには理由があります。ssh-agent にたくさん鍵を読み込んでいると、関係ない鍵を次々に試してしまい、サーバー側の試行回数の上限(MaxAuthTries)に先に達することがあるからです。その場合は Too many authentication failures というエラーになります。

ここで鍵のフィンガープリントも照合しておきます。Offering public key の行に出ている SHA256:... と、登録した公開鍵のフィンガープリントが一致するかを確かめます。

# 手元の鍵のフィンガープリント
ssh-keygen -lf ~/.ssh/work_ed25519.pub

# サーバーの authorized_keys に登録されている鍵のフィンガープリント(サーバーで実行)
ssh-keygen -lf ~/.ssh/authorized_keys

秘密鍵の権限で、クライアントが先に止まることもある

-v の出力に、次のような警告が出ていたら、原因はサーバーではなく手元にあります。

@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
Permissions 0644 for '/home/ryu/.ssh/work_ed25519' are too open.

秘密鍵が他人にも読める権限になっているので、SSHクライアントがその鍵を使うのを拒否しています。chmod 600 で自分だけが読める状態に直します。

サーバー側:認証ログで「なぜ断ったか」を読む

手元で正しい鍵を出していると確認できたら、次はサーバー側です。サーバーに別の経路(コンソールや、すでに開いている別のSSHセッション)で入れるなら、認証ログを見ます。

ログの場所はディストリビューションによって違います。

ディストリビューション ファイル journalctl のユニット名
Ubuntu / Debian /var/log/auth.log ssh
RHEL / Rocky / AlmaLinux /var/log/secure sshd

接続を試しながら、別のターミナルでログを流しておくと原因の行を見つけやすくなります。

# Ubuntu の例(RHEL系なら -u sshd)
sudo journalctl -u ssh -f

journalctl の読み方に慣れていない方は、systemdの言い分をsystemctlとjournalctlで聞くで基本を解説しています。

よく出るメッセージと、その意味

ログに出る代表的なメッセージを並べておきます。文言はバージョンによって多少異なります。

ログに出る文言(例) 意味 見直す場所
Invalid user xxx from ... そのユーザーがサーバーに存在しない 接続時のユーザー名
Authentication refused: bad ownership or modes for directory /home/ryu/.ssh 権限や所有者が安全でない ~/.ssh やホームの権限
Connection closed by authenticating user ryu ... [preauth] 認証の途中で終わった(鍵が一致しなかった等) 登録した公開鍵の中身
User ryu not allowed because ... AllowUsers などの設定で拒否 sshd_config

ユーザー名の間違いは、意外と多い原因です。クラウドのイメージでは、初期ユーザーが ubuntuec2-user のように決まっていて、自分の名前では入れないことがあります。

権限は「書き込めるのは本人だけ」が原則

2行目の bad ownership or modes は、sshd_configStrictModes という設定が働いた結果です。

man ページでは、StrictModes について次のように説明されています。

ログインを受け付ける前に、ユーザーのファイルとホームディレクトリのモードと所有者を確認するかどうかを指定する。初心者がうっかりディレクトリやファイルを誰でも書き込める状態にしてしまうことがあるため、通常は有効にしておくのが望ましい。デフォルトは yes。(sshd_config(5) より要約)

先ほどのたとえで言えば、台帳を誰でも書き換えられる場所に置いていたので、管理人が台帳を信用しなかった、という状況です。

目安になる権限は次のとおりです。

# サーバー側で、ログインしたいユーザーとして実行
chmod 755 ~                     # ホームは本人以外が書き込めないこと(700 でも可)
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
ls -ld ~ ~/.ssh ~/.ssh/authorized_keys

見落としやすいのが所有者です。root で authorized_keys を作ってしまうと、権限の数字が正しくても所有者が root のままになっています。その場合は chown でユーザーに戻します。

sudo chown -R ryu:ryu /home/ryu/.ssh

権限の数字の読み方はLinuxのファイル権限、所有者の変更はchownコマンドの使い方で詳しく解説しています。

authorized_keys の中身が崩れていないか

権限が正しくても、ファイルの中身が崩れていれば鍵は一致しません。意外と多いのが、公開鍵をコピーして貼り付けたときの事故です。

公開鍵は、ssh-ed25519 AAAA... コメント という形で1つの鍵が必ず1行に収まっている必要があります。ターミナルやチャットからコピーしたときに途中で改行が入ったり、先頭の数文字が欠けたりすると、sshd はその行を鍵として読めません。

手で貼るより、クライアント側から ssh-copy-id を使うほうが安全です。パスワード認証がまだ使える段階なら、次のように登録できます。

# 公開鍵をサーバーの authorized_keys に追記してくれる
ssh-copy-id -i ~/.ssh/work_ed25519.pub ryu@192.0.2.20

すでに貼ってしまった場合は、ssh-keygen -lf ~/.ssh/authorized_keys を実行してみてください。行が壊れていれば、フィンガープリントを表示できずにエラーになるので、崩れに気づけます。

RHEL系でSELinuxが有効な場合、権限も所有者も正しいのに断られることがあります。~/.ssh を手作業でコピーしたときなどに、SELinuxのラベル(コンテキスト)が合っていないケースです。その場合は restorecon -Rv ~/.ssh でラベルを戻すと解決することがあります。

sshd -T で「実際に効いている設定」を確かめる

権限もログも問題なさそうなのに入れない。そんなときは、sshd の設定そのものを疑います。

ここで落とし穴になるのが、設定ファイルが1枚ではないことです。最近のディストリビューションでは、/etc/ssh/sshd_config の中で /etc/ssh/sshd_config.d/*.conf を読み込むようになっていることが多くあります。

しかも sshd_config では、多くの項目で最初に読んだ値が使われます。sshd_config.d の中のファイルで先に PubkeyAuthentication no と書かれていたら、本体のファイルで yes と書いても効きません。

そこで、ファイルを目で追うのではなく、sshd 自身に「最終的にこう解釈した」という結果を出してもらいます。

# root 権限で、実際に有効な設定を出力する
sudo sshd -T | grep -iE 'pubkeyauthentication|authorizedkeysfile|strictmodes|allowusers|pubkeyacceptedalgorithms'

確認したい項目

出力の中で、特に見ておきたい項目を表にしておきます。

項目 見るポイント
pubkeyauthentication yes になっているか
authorizedkeysfile 鍵を登録したファイルのパスと一致しているか
strictmodes yes なら前章の権限チェックが働く
allowusers / allowgroups 設定されていれば、自分が含まれているか
pubkeyacceptedalgorithms 自分の鍵の種類が含まれているか

最後の項目は、古い鍵を使い続けている環境で効いてきます。OpenSSH 8.8 からは、SHA-1 を使う RSA 署名(ssh-rsa)がデフォルトで無効になりました。多くの場合は自動的に SHA-256/512 の署名が使われるので問題になりませんが、古いクライアントやライブラリ経由で接続していると、ここで断られることがあります。

新しく鍵を作るなら、ssh-keygen -t ed25519 で Ed25519 の鍵にしておくのが無難です。

それでも分からないときは、sshd をデバッグモードで動かす

ログに手がかりが少ないときは、sshd を別のポートで、デバッグモードで一時的に起動する方法があります。-d を付けると、sshd はフォアグラウンドで動き、認証の過程を細かく画面に出してくれます。

# サーバー側:普段の sshd とは別に、2222番ポートでデバッグ起動(1回接続すると終了する)
sudo /usr/sbin/sshd -d -p 2222

# クライアント側:そのポートへ接続してみる
ssh -v -p 2222 ryu@192.0.2.20

クライアントの -v とサーバーの -d を並べて見ると、どちらが何を言って、どこで話がかみ合わなくなったのかが一目で分かります。電話の両側の通話記録を並べて読むようなものです。

ただし、2222番ポートをファイアウォールで開ける必要があるうえ、本番のサーバーで無闇に試すものではありません。検証環境で使うか、作業後に必ずポートを閉じるようにしてください。

なお、設定を直したら sudo sshd -t で文法チェックをしてから再起動します。書き間違えたまま再起動すると、sshd が起動せず、リモートから二度と入れなくなることがあるからです。作業中は、既存のSSHセッションを1本閉じずに残しておくと安心です。

切り分けの順番をまとめておく

ここまでの内容を、実際に詰まったときの順番に並べ直しておきます。上から順に見ていけば、たいていどこかで原因に当たります。

順番には意味があります。上のほうは、サーバーに入れなくても自分の手元だけで確かめられることです。下に行くほど、サーバーへの別の入り口や管理者の権限が必要になります。

チームで作業しているなら、下の段階に進む前に、ここまでの確認結果をまとめて管理者に渡すと話が早くなります。「ssh -v ではこの鍵を出していて、フィンガープリントは登録したものと一致している」と伝えられれば、相手はサーバー側だけを見ればよくなるからです。

順番 見る場所 確認すること
1 接続コマンド ユーザー名・IPアドレスは正しいか
2 ssh -v どの鍵を出しているか、登録した鍵と一致するか
3 ssh -v 秘密鍵の権限の警告が出ていないか
4 サーバーの認証ログ 断った理由の行は何か
5 サーバーの権限 ホーム・~/.sshauthorized_keys の権限と所有者
6 sshd -T 鍵認証が有効か、パスやアルゴリズムは合っているか

ちなみに、Permission denied が出ているということは、少なくともサーバーまでは通信が届いているということです。ファイアウォールで止められているなら、Connection timed outConnection refused のように別のエラーになります。そちらの切り分けはfirewalld・nftables・iptablesの関係を整理するが参考になります。

また、サーバーを作り直したあとに出る REMOTE HOST IDENTIFICATION HAS CHANGED は、鍵認証ではなくホスト鍵の話です。こちらはSSH接続時のREMOTE HOST IDENTIFICATION HAS CHANGEDの対処方法で扱っています。

InfraAcademyで、SSHの土台を固める

SSHのトラブルは、SSHだけの知識では解けないことが多いです。今日の内容だけでも、ファイルの権限、所有者、systemd のログ、設定ファイルの読み込み順と、Linux の基本がいくつも絡んでいました。

逆に言えば、Linux の基礎がつながっていると、初めて見るエラーでも「どこを見ればいいか」の見当がつくようになります。

InfraAcademy のLinuxロードマップでは、権限やユーザー管理、サービスとログの扱いといった基礎を、手を動かしながら順番に学べるようにしています。点で覚えた知識を線でつなぎ直したい方は、ぜひのぞいてみてください。

まとめ

Permission denied (publickey) は、原因を教えてくれないエラーです。でも、理由はサーバー側にちゃんと記録されています。

  • クライアントの ssh -v で、差し出している鍵を確かめる
  • サーバーの認証ログで、断った理由の行を読む
  • 権限と所有者は「書き込めるのは本人だけ」を守る
  • sshd -T で、実際に効いている設定を確認する

エラーメッセージだけを見て検索するより、この3か所を順に読むほうが、結果的にずっと早く解決します。次に詰まったときは、まず -v を付けるところから始めてみてください。

参考記事

Next Action

記事で読んだ内容を、講座で実装してみましょう

InfraAcademyでは、ブラウザ上でLinuxやネットワークの実践環境を使いながら学習できます。無料で始められる講座から、学習の流れを試せます。

この記事を書いた人

ryu

InfraAcademy運営 / エンジニア

エンジニア歴10年。Linux、ネットワーク、クラウドを中心に、実務で役立つインフラ技術を初心者にもわかりやすく解説しています。

X: @ryu63614894

Related

関連記事

ブログ一覧へ

Roadmap

まずはこの4講座から

ログインすれば無料で始められる講座です。気になったテーマから手を動かして学べます。

講座一覧を見る