SSH 鯖の公開鍵認証のキーファイル設定は ~/.ssh/ から始めないほうが良い

# 公開鍵認証ができない！！

先月, いつものように sshd の設定を感覚とフィーリングでやった[^1]ところ,
締め出しを食らってしまい物理的にログインしなければならないという事件が起きました。  
このときの知見を共有したいと思います。

:::note warn
私の環境では, ﾁｮｯﾄ古いバージョンである OpenSSH_7.2p2 が動いているので, この方法じゃ解決できないかもしれません。
多分 i386 環境を使用している方はこのバージョンで動いているかと思います[^2]。
:::

## TL;DR;

`/etc/ssh/sshd_config` の `AuthorizedKeysFile` 設定 を `.ssh/authorized_keys` にしたらログインできた。

```diff_ssh:/etc/ssh/sshd_config
# (中略)

# Authentication:

#LoginGraceTime 2m
#PermitRootLogin yes
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10

#RSAAuthentication yes
PubkeyAuthentication yes

# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
# but this is overridden so installations will only check .ssh/authorized_keys
- AuthorizedKeysFile    ~/.ssh/authorized_keys
+ AuthorizedKeysFile    .ssh/authorized_keys

```

#### 2023/03/18 追記 [コメント](https://qiita.com/pyg/items/a00a9eaba0eb627e51f6#comment-27071ee4e6d53e419533) by @angel_p_57 さん

[sshd_config(5) のマニュアル](https://nxmnpg.lemoda.net/ja/5/sshd_config#:~:text=%E3%81%8C%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82-,AuthorizedKeysFile,-(authorized_keys%20%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB))曰く,

> AuthorizedKeysFile のファイル名中に %T が含まれている場合、その部分は接続の間 別のものに置換されます。%% は '%' 1 文字に置換されます。 %h は認証しようとしているユーザのホームディレクトリに置換され、 %u はそのユーザのユーザ名に置換されます。この後、 その絶対パスあるいはユーザのホームディレクトリからの相対パスが AuthorizedKeysFile に渡されます。デフォルトでの値は ".ssh/authorized_keys" となっています。

らしい。つまり,
+ AurhorizedKeysFile は、ホームディレクトリからの相対パスで指定できる。
+ ユーザにごとに設定値をカスタマイズするために, `%h` （ホームディレクトリの絶対パス）や `%u` (ユーザ名) 等のプレースホルダが使える。

このことより, `AuthorizedKeysFile %h/.ssh/authorized_keys` でもいいというわけだ。


## 前提

|項目/パッケージ|バージョン| 備考|
|:------------|:--------|:----|
| Ubuntu      | 16.04   | i386環境で試しています[^2]。 |
| OpenSSH     | 7.2p2   |     |
| ssh         | 8.9p1 | クライアントの方です。 |

当環境では, 鍵は Yubikey の PGP 機能を使って格納されており, ssh-agent の代わりに gpg=agent を使って鍵管理している。

## やりたいこと

SSH で公開鍵認証を使って鯖にログインしたい。

## 何が起きたか

` Permission denied (publickey).` と表示され、公開鍵認証ができない。また、パスワード認証を切っているため, 鯖から締め出しを食らった。


## 試したこと

### SSH(クライアント) の詳細なログを取ってみる

`ssh` コマンドでは, オプション `-v` を使うと詳細なログが取れる。今回の場合は, debug3 までほしいので `-vvv` を使って実行する。
実行結果は以下の通りである。
```ssh:ssh peyang@192.168.0.37 -vvv
OpenSSH_for_Windows_8.9p1, LibreSSL 3.0.2
# (中略. 鍵交換や, ホストキーについてやり取りしている.)
debug1: get_agent_identities: agent returned 1 keys
debug1: Will attempt key: (カード番号) ED25519 SHA256:(キーのハッシュ) agent
# (中略. 他のアルゴリズムのキーを試そうとしている.)
debug2: pubkey_prepare: done
debug3: send packet: type 5
debug3: receive packet: type 7
debug1: SSH2_MSG_EXT_INFO received
debug1: kex_input_ext_info: server-sig-algs=<rsa-sha2-256,rsa-sha2-512>
debug3: receive packet: type 6
debug2: service_accept: ssh-userauth
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug3: send packet: type 50
debug3: receive packet: type 51
# このあたりから認証のログだろう。
debug1: Authentications that can continue: publickey
debug3: start over, passed a different list publickey
debug3: preferred publickey,keyboard-interactive,password
debug3: authmethod_lookup publickey
debug3: remaining preferred: keyboard-interactive,password
debug3: authmethod_is_enabled publickey
debug1: Next authentication method: publickey
debug1: Offering public key: (カード番号) ED25519 SHA256:(キーのハッシュ) agent
debug3: send packet: type 50
debug2: we sent a publickey packet, wait for reply
debug3: receive packet: type 51
# (中略. 他のアルゴリズムのキーを試している模様)
debug1: No more authentication methods to try.
peyang@192.168.0.37: Permission denied (publickey).
```

キーの読み取りエラーや, 送信エラー等は特に起きていないようである。
このログを見る限り, クライアント側には問題がないため, サーバ側の問題だと推測した。  

### SSH(サーバ)の詳細なログを取ってみる

#### 下準備：サーバの `sshd_config` を書き換える

まず, 検証用にログインするため, パスワード認証を有効にした。そのあとに, `LogLevel` を `DEBUG` にし, デバッグログを取得できるようにした。

```diff_ssh:/etc/ssh/sshd_config
# (中略)

# Logging
# obsoletes QuietMode and FascistLogging
#SyslogFacility AUTH
- #LogLevel INFO
+ Loglevel DEBUG

# (中略)

# Authentication:

# To disable tunneled clear text passwords, change to no here!
- #PasswordAuthentication yes
+ PasswordAuthentication yes
#PermitEmptyPasswords no

# Change to no to disable s/key passwords
ChallengeResponseAuthentication no
```

変更が終わったら, sshd を再起動する

```shell
$ sudo systemctl restart sshd
```

#### ログを `tail -f` する

SSH の認証ログ `/var/log/auth.log` を垂れ流しするようにする。
そのあとに, クライアントからログインを試みる。

```shell
$ tail -f /var/log/auth.log
(中略..) sshd[12912]: debug1: session_input_channel_req: session 0 req pty-req
debug1: Allocating pty.
debug1: session_new: session 0
debug1: SELinux support disabled
debug1: session_pty_req: session 0 alloc /dev/pts/0
debug1: server_input_channel_req: channel 0 request shell reply 1
debug1: session_by_channel: session 0 channel 0
debug1: session_input_channel_req: session 0 req shell
Starting session: shell on pts/0 for peyang from 192.168.0.236 port 27577 id 0
debug1: Setting controlling tty using TIOCSCTTY.
# ここで, クライアントからログインを試みる。
Connection from 192.168.0.236 port 27833 on 192.168.0.37 port 22
debug1: Client protocol version 2.0; client software version OpenSSH_for_Windows_8.9
# (中略 preauth やキー交換が走っている。)
debug1: userauth_pubkey: test whether pkalg/pkblob are acceptable for ED25519 SHA256:(キーのハッシュ) [preauth]
debug1: temporarily_use_uid: 1000/1000 (e=0/0)
#                               ↓↓↓ ここテストにでます。
debug1: trying public key file /root/.ssh/authorized_keys  
debug1: Could not open authorized keys '/root/.ssh/authorized_keys': Permission denied
#                                        ↑↑↑ ここテストにでます。
debug1: restore_uid: 0/0
Failed publickey for peyang from 192.168.0.236 port 27833 ssh2: ED25519 SHA256:(キーのハッシュ)
debug1: userauth-request for user peyang service ssh-connection method publickey [preauth]
debug1: attempt 1 failures 1 [preauth]
Connection reset by 192.168.0.236 port 27833 [preauth]
debug1: do_cleanup [preauth]
debug1: monitor_read_log: child log fd closed
debug1: do_cleanup
debug1: Killing privsep child 12931
debug1: audit_event: unhandled event 12
```

はい。何故か, `authorized_keys` の格納場所が `/root/.ssh/authorized_keys` になっていることがわかる。(原因は謎)
当然, `/root/.ssh` や `/root/.ssh/authorized_keys` は存在しないため, 開けないのでエラーになる。

が, 当時の私はここを読み飛ばしてたため, 解決から遠回りをすることになった。
ログを軽く読み飛ばしてはいけない(戒め)

### 権限を見直してみる(結局関係なかった)

以前, `authorized_keys` や `~/.ssh/` が他グループに所属していたり, 他のユーザが書き込みできると認証ができない旨の記事を見た。
関係ないとは思ったが, 一応確認してみる。

```bash
peyang@Peyantu:~/.ssh$ ll
total 4
-rw------- 1 peyang peyang 186 Feb 14 00:12 authorized_keys

peyang@Peyantu:~/.ssh$ cd ../
peyang@Peyantu:~$ls -la
toltal (128)
# (中略)
drwx------ 2 peyang peyang  4096 Feb 14 00:16 .ssh

```

適切な権限は, `.ssh/authorized_keys` が 600, `.ssh` が 700 のため, 健全な権限である。

### サーバ側の `AuthorizedKeys` のパスを変更してみる

ここで当時の私は, 上記の ssh が `/root/.ssh` のキーを参照している問題に気づく。
一通り [ChatGPT](https://chat.openai.com) に聞いてみたり, [Perplexity](https://www.perplexity.ai/) や [Google](https://google.com)で検索してみたが, 有力な情報は得られなかった。
長年の経験(笑)から、`AuthorizedKeys` 設定がおかしいだろうと思い, 適切なパスに変えることにした(つまりあてずっぽう)

いろいろ試したところ, `~/.ssh/authorized_keys` を `.ssh/authorized_keys` にしたところ動作した。

```diff_ssh:/etc/ssh/sshd_config
# (中略)

# Authentication:

#LoginGraceTime 2m
#PermitRootLogin yes
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10

#RSAAuthentication yes
PubkeyAuthentication yes

# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
# but this is overridden so installations will only check .ssh/authorized_keys
- AuthorizedKeysFile    ~/.ssh/authorized_keys
+ AuthorizedKeysFile    .ssh/authorized_keys

#AuthorizedPrincipalsFile none

#AuthorizedKeysCommand none
#AuthorizedKeysCommandUser nobody

# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
#RhostsRSAAuthentication no
# similar for protocol version 2
#HostbasedAuthentication no
# Change to yes if you don't trust ~/.ssh/known_hosts for
# RhostsRSAAuthentication and HostbasedAuthentication
#IgnoreUserKnownHosts no
# Don't read the user's ~/.rhosts and ~/.shosts files
#IgnoreRhosts yes
```

## おわりに

今回の直接的な原因としては, `AuthorizedKeysFile` 設定がおかしかったことが挙げられる.
だが, そもそもこれが起きた理由は, 全くもって[マニュアル](https://linux.die.net/man/5/sshd_config#:~:text=information%20on%20patterns.-,AuthorizedKeysFile,-Specifies%20the%20file)を見なかった私にある。

`sshd_config` をみると, `AuthorizedKeysFile` 設定の下の項目である `IgnoreUserKnownHosts` のコメントに, `~/.ssh/known_hosts` と書かれてある。
私は, `~/` をつけることが慣習かと思い, 今回の問題となった設定も `~/.ssh/authorized_keys` としてしまった.
これが今回の問題のすべての元凶だと思う。

今後は, 横着をしないで[マニュアル](https://linux.die.net/man/5/sshd_config#:~:text=information%20on%20patterns.-,AuthorizedKeysFile,-Specifies%20the%20file)をしっかり見ようと思った.

[^1]: 私は非常に怠惰なため, 繰り返しやることはマニュアルを見ないでやってしまう癖がある。
[^2]: ハードオフで500円で買ったマシンである。