cloud-init で実施する SSH サーバー設定まとめ

今まで Ubuntu をヘッドレスインストールする時は、ネットワーク設定だけ済ませて、デフォルトの Ubuntu ユーザーでパスワード認証でいったんログインしてから `/etc/ssh/sshd_config` 書き換え……ということをやっていたのですが、 Raspberry Pi も増えてきてさすがに面倒になったので自動化してみました。

## 環境

* Raspberry Pi 4B
* Ubuntu 20.04

## 対象読者

* cloud-init を使って SSH 設定を自動化したい人
  * (ユーザー作成)
  * 公開鍵を設定 (~/.ssh/authorized_keys)
  * SSH のパスワード認証を禁止 (PasswordAuthentication)
  * SSH の root ログインを禁止 (PermitRootLogin)
  * SSH のポートを変更

## 前提

### cloud-init とは

cloud-init はマルチディストリビューション対応のクラウドインスタンスの初期化のツールです。名前の通りクラウドの VM を立ち上げるのに作られたものですが、 Raspberry Pi など非クラウド環境でも普通に使えます。

ファイルの先頭に `#cloud-config` をつけるのがお約束。

https://cloudinit.readthedocs.io/en/latest/

### Ubuntu におけるネットワーク設定

ヘッドレスインストール時に Wi-Fi を使用して接続したい場合や、固定 IP を指定したい場合は network-config ファイルを修正します。有線 LAN を使用して DHCP でつなぐ場合は必要ありません。

ちなみに筆者は有線 LAN + ルーターの DHCP サーバーで IP を固定しています（MAC アドレスが必要なので初回起動時は普通に DHCP の払い出し一覧から探してます :innocent: ）

形式は一般的な netplan の yaml なのでここでは説明を割愛します。

### cloud-init によるユーザー作成

ubuntu では ubuntu という名前のユーザーがデフォルトで作成されますが、今回は ubuntu ユーザーを作らずに、別のユーザーを作ります[^1]。筆者の場合はこんな感じの yaml になりました。

passwd に指定する値はパスワードそのものではなくパスワードのハッシュ値です。`mkpasswd --method=SHA-512 --rounds=4096` のコマンドを打ち、パスワードを入力して出力された値を貼れば大丈夫です。

注意点としては passwd を指定する時は `lock_passwd: false` にすること（デフォルトは true）。そうしないとパスワードでログインできません[^2]。

```yaml:user-data
users:
- name: <ユーザー名>
  ssh_import_id: gh:<GitHub のユーザー名>
  lock_passwd: false
  passwd: $6$rounds=4096$sdAekclQbd/D6WW$6aeu3zScOXRQvFJFZRNs0n15e8bdd8SmQricBMudkvsskijM6OSzOdJ2OLG7L1aiqrVnkV88fSa4w3Gohlaaa.
  shell: /bin/bash
  sudo: ALL=(ALL) NOPASSWD:ALL
  uid: 1000
```

参考
[Users and Groups](https://cloudinit.readthedocs.io/en/latest/topics/modules.html#users-and-groups)
[Cloud config examples](https://cloudinit.readthedocs.io/en/latest/topics/examples.html)

[^1]: `- default` を `users` のリストに入れるとデフォルトの ubuntu ユーザーも作れます 

[^2]: https://askubuntu.com/questions/1365637/autoinstall-user-datas-users-section-does-not-work-at-all

## SSH 設定

前置きが長くなりましたが本題に入ります。

### 公開鍵の設定

SSH でログインするための公開鍵を上で作成したユーザーの `~/.ssh/authorized_keys` に設定します。鍵の設定方法は2つあります。

1. GitHub / Launchpad から取得する
2. 公開鍵を直接書く

1 の場合は

```yaml:user-data
users:
- name: <ユーザー名>
  ssh_import_id: gh:<GitHub のユーザー名>
  (以下略)
```

2 の場合は

```yaml:user-data
users:
- name: <ユーザー名>
  ssh_authorized_keys:
    - <ssh pub key 1>
    - <ssh pub key 2>
  (以下略)
```

のように書きます。

### パスワード認証の禁止

SSH パスワード認証を禁止するには `ssh_pwauth: no` と書くだけで OK です。

```yaml:user-data
ssh_pwauth: no
```

### root ログインを禁止

`disable_root` という項目はありますが、これは `/etc/ssh/sshd_config` の PermitRootLogin を変更するものではありません。 [SSH Module](https://cloudinit.readthedocs.io/en/latest/topics/modules.html#ssh) の `ssh_authorized_keys` の項目を指定した場合に、その公開鍵について root ログインを制限するように設定するもののようです（ソースコードから確認、未検証）。

今回は `/etc/ssh/sshd_config` を書き換えたいので `runcmd` を使います。

```yaml:user-data
runcmd:
- sed -i -e 's/^#PermitRootLogin prohibit-password/PermitRootLogin no/' /etc/ssh/sshd_config
- systemctl reload ssh
```

### SSH のポート変更

ポート変更も Module は用意されていないので `runcmd` で `/etc/ssh/sshd_config` を書き換えます。

```yaml:user-data
runcmd:
- sed -i -e 's/^#Port 22/Port 22222/' /etc/ssh/sshd_config
- systemctl reload ssh
```

## まとめ

上記の設定をひとつにまとめると以下になります。

```yaml:user-data
#cloud-config

# This is the user-data configuration file for cloud-init. By default this sets
# up an initial user called "ubuntu" with password "ubuntu", which must be
# changed at first login. However, many additional actions can be initiated on
# first boot from this file. The cloud-init documentation has more details:
#
# https://cloudinit.readthedocs.io/

# Set Passwords
# https://cloudinit.readthedocs.io/en/latest/topics/modules.html#set-passwords
ssh_pwauth: no

# Users and Groups
# https://cloudinit.readthedocs.io/en/latest/topics/modules.html#users-and-groups
users:
- name: <ユーザー名>
  ssh_import_id: gh:<GitHub のユーザー名>
  lock_passwd: false
  passwd: $6$rounds=4096$sdAekclQbd/D6WW$6aeu3zScOXRQvFJFZRNs0n15e8bdd8SmQricBMudkvsskijM6OSzOdJ2OLG7L1aiqrVnkV88fSa4w3Gohlaaa.
  shell: /bin/bash
  sudo: ALL=(ALL) NOPASSWD:ALL
  uid: 1000

# Runcmd
# https://cloudinit.readthedocs.io/en/latest/topics/modules.html#runcmd
runcmd:
- sed -i -e 's/^#Port 22/Port 14022/' /etc/ssh/sshd_config
- sed -i -e 's/^#PermitRootLogin prohibit-password/PermitRootLogin no/' /etc/ssh/sshd_config
- systemctl reload ssh
```

## その他

今回は SSH に関係する項目をまとめましたが、他にもいろいろ module があるので、気になる人は見てみてください。

https://cloudinit.readthedocs.io/en/latest/topics/modules.html

### 参考文献

https://cloudinit.readthedocs.io/en/latest/topics/modules.html

https://cloudinit.readthedocs.io/en/latest/topics/examples.html

https://www.digitalocean.com/community/tutorials/how-to-use-cloud-config-for-your-initial-server-setup