私的サーバー構築日誌：SSH公開鍵認証・公開鍵を集中管理

# 能書き

[私的サーバー構築日誌：仕切り直し](https://qiita.com/katz_engineer/items/1f21de2bd9072c260ae6)からの自宅サーバー構築の続きです。

[以前](https://qiita.com/katz_engineer/items/ca5bc58cf5f92196584b)も同じネタを記事にしましたが、今回はちょっと詳細を変えています。

# 目標

sshを、パスワードではなく公開鍵方式で接続するようにします。

その公開鍵は```$HOME/.ssh```に保管するのではなく、一ファイルに格納して集中管理する方式を試みます。

# 参考文献

- [自宅サーバー構築譚：SSH 公開鍵認証・公開鍵を集中管理](https://qiita.com/katz_engineer/items/ca5bc58cf5f92196584b)
- [OpenSSH で全ユーザの公開鍵を1ファイルで管理する - meta's blog - The Power To Serve](https://w.vmeta.jp/tdiary/20170829.html)
- [LDAPSEARCH - ユーザコマンド(セクション1) - OpenLDAP 2.3 MAN ページ](http://www5f.biglobe.ne.jp/~inachi/openldap/man23/man1/ldapsearch.1.html)
- [OpenLDAP - SSH公開鍵を登録してログインするまでの手順 - Qiita](https://qiita.com/makaaso-tech/items/d9353b7e3128966d607f)
- [LDAPによるSSH公開鍵認証を行いたい - teratail](https://teratail.com/questions/283183)
- [2017年版 SSH公開鍵認証で使用する秘密鍵ペアの作り方 - Qiita](https://qiita.com/wnoguchi/items/a72a042bb8159c35d056)

# サーバーの準備

## スナップショット

スナップショットだいじ。超だいじ。

```console:サーバー
sudo zfs snapshot tank/root/ubuntu@$(date +%Y%m%d_%H%M%S)_before_ssh_authkey
```

## SSH サーバー設定

公開鍵の置き場所を決めます。公開される鍵ですし、どこでも良いんですが。今回は```/opt/ssh/publickeys.list```としました。

```console:サーバー
sudo mkdir /opt/ssh
```

```console:サーバー
sudo touch /opt/ssh/publickeys.list
```

```publickeys.list```の中身はタブ区切りのテキストファイルとします。下記2つの項目をタブ文字で区切って1行とします。
1. ユーザー名
1. 公開鍵

sshd が公開鍵を取得する為のシェルスクリプトを作成します。これは自作しました。

```console:サーバー
sudo mkdir /opt/ssh/bin
```

```console:サーバー
cat <<"___" | sudo tee /opt/ssh/bin/ssh_authorized_key.sh >/dev/null
#!/bin/bash
look -t $'\t' $1$'\t' /opt/ssh/publickeys.list | cut -f2
___
```

```console:サーバー
sudo chmod +x /opt/ssh/bin/ssh_authorized_key.sh
```

そして Linux の伝統に則り```/usr/local/bin```にシンボリックリンクを張ります。

```console:サーバー
sudo ln -s /opt/ssh/bin/ssh_authorized_key.sh /usr/local/bin/ssh_authorized_key.sh
```

このシェルスクリプト実行専用のユーザーアカウントが必要との事。

```console:サーバー
sudo useradd -s /usr/sbin/nologin -M sshauthkey
```

設定ファイルを調整して、作成したシェルスクリプトを呼び出すようにします。
同時にパスワード認証を禁止しときます。

```console:サーバー
cat <<___ | sudo tee /etc/ssh/sshd_config.d/ssh_authorized_key.conf >/dev/null
AuthorizedKeysCommand /usr/local/bin/ssh_authorized_key.sh
AuthorizedKeysCommandUser sshauthkey
AuthorizedKeysFile none
PasswordAuthentication no
___
```

sshdを再起動します。

```console:サーバー
sudo systemctl restart sshd
```

# SSH接続用の鍵

ここからは、クライアントマシンとサーバーマシンの操作が少し入り組んできます。

## ユーザーの選定

当然ながら、接続するユーザーはログイン可能でなければなりません。

ここまで手順通りに操作しているなら、```admin```ユーザーが使える対象になります。

## 秘密鍵と公開鍵を生成

SSH接続の為の鍵を生成します。暗号の種類は、昨今の情勢を鑑みて楕円曲線暗号Ed25519にします。

なお```ssh-keygen```コマンドはWindowsも持っています。このコマンドはWindowsでも、コマンドプロンプトでそのまま実行できます。良い時代になりましたな。

```console:クライアント
ssh-keygen -t ed25519 -P ""
```

鍵ペアは```$HOME/.ssh```ディレクトリに作成されて、それに関して問合せがありますので、何も入力せずにEnterキーを押しましょう。

```console:クライアント
>ssh-keygen -t ed25519 -P ""
Generating public/private ed25519 key pair.
Enter file in which to save the key (C:\Users\admin/.ssh/id_ed25519):
Your identification has been saved in C:\Users\admin/.ssh/id_ed25519.
Your public key has been saved in C:\Users\admin/.ssh/id_ed25519.pub.
The key fingerprint is:
SHA256:RoiCRkTvjynwV3bHno8QenQxoteawl3zjUDcbrV6vk4 admin@client
The key's randomart image is:
+--[ED25519 256]--+
|o+               |
|...  . . . .     |
|..... . o = . .  |
|. ..   o = + . . |
|.  .  + S O o .  |
|..  ++ B O * +   |
| ..o..+ * o + E  |
|  ..   o . o +   |
|          . ..+. |
+----[SHA256]-----+
```

## 公開鍵をサーバーへ送付

[前回確立した手順](https://qiita.com/katz_engineer/items/fc02e92d1315d6a51a7c)でもって、公開鍵をサーバーへ送付します。サーバー名をprimary.home、ユーザー名を```admin```とした場合、下記のように操作します。

Windowsは```sftp```コマンドも持っています。ただ、```cd```コマンドのオプションが異なりますので、そこは適宜修正して下さい。

```console:クライアント(Linux)
cd ~/.ssh
```

```console:クライアント(Windows10)
cd %USERPROFILE%\.ssh
```

```console:クライアント
sftp transporter@primary.home
```

```console:クライアント sftp
cd publickey
mkdir admin
cd admin
put id_ed25519.pub
exit
```

## サーバーで公開鍵一覧表に追加

送付された公開鍵をサーバー側で受け取ります。具体的には、公開鍵一覧表に追加してから並べ替えます。

```console:サーバー
KEYOWNER=admin
```

```console:サーバー
cat <(echo -n $KEYOWNER$'\t') /srv/transport/publickey/$KEYOWNER/id_ed25519.pub | sudo tee -a /opt/ssh/publickeys.list >/dev/null
```

```console:サーバー
sudo sort -u /opt/ssh/publickeys.list -o /tmp/publickeys.list
```

```console:サーバー
sudo mv /tmp/publickeys.list /opt/ssh/publickeys.list
```

処理が済んだ印として、サーバー側の責任で公開鍵を削除しておきましょう。

```console:サーバー
sudo rm /srv/transport/publickey/$KEYOWNER/id_ed25519.pub
```

```console:サーバー
sudo rmdir /srv/transport/publickey/$KEYOWNER
```

# 動作確認

動作を確認します。ssh 接続時にパスワード入力要求が無ければ成功です。

```console:クライアント
ssh admin@primary.home
```

# 仕舞い

上手くいったら```/etc```を Subversion コミットしておきます。
まずは今回の差分を確認。

```console:サーバー
sudo svn st /etc
```

今回はユーザー追加などもあったので、下記のようになりました。

```console:サーバー
$ sudo svn st /etc
M       /etc/group
M       /etc/gshadow
M       /etc/passwd
M       /etc/shadow
?       /etc/ssh/sshd_config.d/ssh_authorized_key.conf
M       /etc/subgid
M       /etc/subuid
M       /etc/zfs/zpool.cache
```

問題無いようなのでコミットします。

```console:サーバー
sudo svn st /etc | grep "^?" | cut -b9- | xargs -I{} sudo find {} -type f -or -type d -or -type l | xargs -t sudo svn add
```

```console:サーバー
sudo svn ci /etc -m"ssh: public key authentication, prohibit password"
```

これで、パスワード認証ではなくて公開鍵認証方式で ssh 接続できるようになりました。その鍵は一箇所で集中管理します。