日頃使いのSSH(openssh)とメンテ用のサブSSH(dropbear)を併用する話（Ubuntuで）

# 背景

検証環境のリモートログイン整備色々の中でSSHサーバにも手を入れています。
しっかりOTP認証まで入れる、、のはいいのですが、１つ問題が。

OTP入れちゃうと、ansibleの接続がめんどくさいんですよね。。
そもそもOTPで使うgoogle-authenticator-libpamの設定で、パスワードとOTPの6桁をくっつけて入力する方法が取れそう（前にOpenVPNで調べたときの話。SSHでは未確認）ですので、--ask-passしてつどパスワード打ち込む方法でも行けそうではあるのですが。。めんどい。
また、逆に平時のリモートログインでその方法取るのもなんか気持ちが悪い。

ということで、１案として、メインのOTPリモート接続を受け付けるopensshと、ansible用に鍵認証のみで接続を受け付けるdropbearを共存させる方法を整理してみようと思います。

# そもそも。Matchオプションじゃ駄目なの？

実は、、Matchオプションでもやりたいことはできます。

sshd_configの標準雛形の末尾についているやつで、”Match User user01”とかしてディレクティブを作ると、そのユーザの認証のときだけパスワード使わせたりということができる、というあれです。

Matchオプションで個別アカウントや個別の接続元を指定して上書きできる設定は限られているのですが、

```
$ man sshd_config
...
             Only a subset of keywords may be used on the lines following a Match keyword.  Available keywords are
             AcceptEnv, AllowAgentForwarding, AllowGroups, AllowStreamLocalForwarding, AllowTcpForwarding,
             AllowUsers, AuthenticationMethods, AuthorizedKeysCommand, AuthorizedKeysCommandUser, AuthorizedKeysFile,
             AuthorizedPrincipalsCommand, AuthorizedPrincipalsCommandUser, AuthorizedPrincipalsFile, Banner,
             CASignatureAlgorithms, ChrootDirectory, ClientAliveCountMax, ClientAliveInterval, DenyGroups, DenyUsers,
             DisableForwarding, ExposeAuthInfo, ForceCommand, GatewayPorts, GSSAPIAuthentication,
             HostbasedAcceptedAlgorithms, HostbasedAuthentication, HostbasedUsesNameFromPacketOnly, IgnoreRhosts,
             Include, IPQoS, KbdInteractiveAuthentication, KerberosAuthentication, LogLevel, MaxAuthTries,
             MaxSessions, PasswordAuthentication, PermitEmptyPasswords, PermitListen, PermitOpen, PermitRootLogin,
             PermitTTY, PermitTunnel, PermitUserRC, PubkeyAcceptedAlgorithms, PubkeyAuthentication,
             PubkeyAuthOptions, RekeyLimit, RevokedKeys, SetEnv, StreamLocalBindMask, StreamLocalBindUnlink,
             TrustedUserCAKeys, X11DisplayOffset, X11Forwarding and X11UseLocalhost.
...
```

てなってます。

OTP認証を使う際はUsePAMを使うのですが、こいつはMatchオプションでは使えません。
ただし、AuthenticationMethodsの方は使えるので、まず全体の設定で"AuthenticationMethods publickey,keyboard-interactive"てしておいて、Matchの配下で
特定のユーザや接続元に対して"AuthenticationMethods publickey"と上書きすればOKです。

UsePAMでOTPが呼び出されるのですが、PAMが適用されるのはpasswordとkeyboard-interactiveの２つだけですので、publickeyだけをAtuthenticationMethodsで指定してやれば、特定のユーザ・接続元だけは鍵認証のみでの認証（今回ansible利用時に避けたいOTP適用）を避けることが可能です。

ただし、opensshの設定自体もansibleで管理する場合、間違った設定流し込んで悪さしてしまった時に面倒だったり、オペミスで設定間違えて編集して、想定外の接続元まで認証強度下げてしまったり、というリスクもあるっちゃあります。
（完全に個人の感想なのですが、認証周りであまり複雑な条件分岐させると変な脆弱性の影響受けるのではないかな、、という気持ち悪さもあり）

Matchオプション対応でいい気はしつつではあるのですが、せっかくなので、Dropbearを併設する方法をまとめてみようかなと思ったのが、今回の発端です。

# dropbearの導入

## インストール

すでにopensshは入ってる前提です。
導入自体は簡単なのですが。。

```
$ sudo apt install dropbear

$ sudo systemctl status dropbear
_ dropbear.service - LSB: Lightweight SSH server
     Loaded: loaded (/etc/init.d/dropbear; generated)
     Active: active (exited) since Fri 2023-01-13 10:23:12 UTC; 14min ago
       Docs: man:systemd-sysv-generator(8)
    Process: 2131 ExecStart=/etc/init.d/dropbear start (code=exited, status=0/SUCCESS)
        CPU: 4ms

Jan 13 10:23:12 test systemd[1]: Starting LSB: Lightweight SSH server...
Jan 13 10:23:12 test dropbear[2131]: Starting Dropbear SSH server: [abort] NO_START is not set to zero in /etc/default/dropbear
Jan 13 10:23:12 testn systemd[1]: Started LSB: Lightweight SSH server.
```

起動しているように見えるのですが、起動してません。
よくよく２つ目のログを見ると「[abort] NO_START is not set to zero in /etc/default/dropbear」と書いてあります。

で、指定されているファイルを見てみると。

```
$ cat /etc/default/dropbear 
# disabled because OpenSSH is installed
# change to NO_START=0 to enable Dropbear
NO_START=1
# the TCP port that Dropbear listens on
DROPBEAR_PORT=22
```

OpenSSHがすでに入っているから、スタートさせないフラグを立てとくよ、とあります。
試しに手元のコンテナでopensshが入っていない状態でdropbearを入れてみたところ、

```
# cat /etc/default/dropbear 
# the TCP port that Dropbear listens on
DROPBEAR_PORT=22
```

はい。NO_STARTの記載はありません。
インストール時にopensshの有無を見て設定を付け足してるみたいですね。

さておき。
opensshがすでに入っている場合は、上記のフラグを外し、openssh側で22番ポートを使っているようならポートも変えてやれば、起動できるようになります。

```
$ cat /etc/default/dropbear 
# disabled because OpenSSH is installed
# change to NO_START=0 to enable Dropbear
NO_START=0
# the TCP port that Dropbear listens on
DROPBEAR_PORT=10022
...

$ sudo sytemctl restart dropbear
     Loaded: loaded (/etc/init.d/dropbear; generated)
     Active: active (running) since Fri 2023-01-13 10:51:44 UTC; 18s ago
       Docs: man:systemd-sysv-generator(8)
    Process: 3385 ExecStart=/etc/init.d/dropbear start (code=exited, status=0/SUCCESS)
      Tasks: 1 (limit: 4575)
     Memory: 320.0K
        CPU: 5ms
     CGroup: /system.slice/dropbear.service
             └─3390 /usr/sbin/dropbear -p 10022 -W 65536

Jan 13 10:51:44 test systemd[1]: dropbear.service: Deactivated successfully.
Jan 13 10:51:44 test systemd[1]: Stopped LSB: Lightweight SSH server.
Jan 13 10:51:44 test systemd[1]: Starting LSB: Lightweight SSH server...
Jan 13 10:51:44 test dropbear[3389]: Failed loading /etc/dropbear/dropbear_dss_host_key
Jan 13 10:51:44 test dropbear[3385]: Starting Dropbear SSH server: dropbear.
Jan 13 10:51:44 test systemd[1]: Started LSB: Lightweight SSH server.
Jan 13 10:51:44 test dropbear[3390]: Running in background
```

## 設定

さて、設定です。
正直初期設定でやりたいことはできるのですが、rootログインが禁止されているかなどなど懸案はありますので、ちゃんと調べて置きます。

### そもそもどこに設定ファイルがあるの？

/etc/dropbear/には設定ファイルはありません。
ディレクトリはあるのですが、サーバ証明書など置くディレクトリになってます。

で、設定がどこにあるかというと、先程にも編集した/etc/default/dropbearです。
ちなみにdropbearがsystemctlからどのように呼び出されているかというと、

```
$ systemctl show -P FragmentPath dropbear
/run/systemd/generator.late/dropbear.service

$ cat /run/systemd/generator.late/dropbear.service | grep ExecStart
ExecStart=/etc/init.d/dropbear start

$ cat /etc/init.d/dropbear
#!/bin/sh
...
DEFAULTCFG=/etc/default/dropbear
...
test ! -r $DEFAULTCFG || . $DEFAULTCFG
...
```

と、こんな連鎖になってます。

### いざ、設定

さて、設定方法なのですが、man dropbear.confの・ようなものは見つかりませんでした。
man dropbearとするとコマンドで実行する際の引数ごとの説明だけ書いてあります。

まぁ、そのmanと/etc/init.d/dropbearの中身を見れば/etc/daemon/dropbearのどこをどんな感じに設定すれば何ができるかがわかる、って感じですかね。

気をつけないといけないのは以下でしょうか。

#### 安全のためにつけるもの
-w     Disallow root logins.
-s     Disable password logins.
-g     Disable password logins for root.
-j     Disable local port forwarding.
-k     Disable remote port forwarding.

#### 安全のためにつけないもの
-a     Allow remote hosts to connect to forwarded ports.

#### 初期値のままでいい気がするのでつけないもの
-T max_authentication_attempts. If unspecified the default is 10 (MAX_AUTH_TRIES)

#### 構成に合わせてつけるもの
-p [address:]port
-c forced_command

#### 不勉強でよくわかってません！
-W windowsize
-K timeout_seconds  If no response i received for 3 consecutive keepalives the connection will be closed.
-I idle_timeout

#### 設定の書き方
で、これらの設定ですが、/etc/default/dropbear上は専用の変数があるものと、DRPOBEAR_EXTRA_ARGSにまとめて設定になるものがあるようです。

- DROPBEAR_PORT: -p
- DROPBEAR_RECEIVE_WINDOW: -w
- DRPOBEAR_EXTRA_ARGS: 上記記載のこれ以外

ちなみにディフォルトだと、標準では-pと-Wだけ値を入れているみたいです。

```
$ ps u -C dropbear
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root        5138  0.0  0.0   4792   200 ?        Ss   11:35   0:00 /usr/sbin/dropbear -p 10022 -W 65536
```

上記まとめて、こんな設定で行こうかなと思います。

```
$ cat /etc/default/dropbear
# disabled because OpenSSH is installed
# change to NO_START=0 to enable Dropbear
NO_START=0
# the TCP port that Dropbear listens on
DROPBEAR_PORT=10.0.0.64:10022

# any additional arguments for Dropbear
DROPBEAR_EXTRA_ARGS="-w -s -g -j -k" 
...
```

### sftpの有効化、、はできない。

上記まで設定して試しにansibleで接続してみたのですが、、失敗。

```
[WARNING]: sftp transfer mechanism failed on [10.0.0.64]. Use ANSIBLE_DEBUG=1 to see detailed information[WARNING]: scp transfer mechanism failed on [10.0.0.64]. Use ANSIBLE_DEBUG=1 to see detailed information
fatal: [test]: UNREACHABLE! => {"changed": false, "msg": "Data could not be sent to remote host \"10.0.0.64\". Make sure this host can be reached over ssh: ", "unreachable": true}
```

dropbear標準だと、sftpは扱えないようです。
どうやコンパイル時に手を入れてopenssh-sftp-serverを取り込む方法があるらしいのですが、コンパイルしてパッケージ管理できなくなるのもなぁ、、
ということで、逆にansibleのansible.cfg側でsftpを使わずにscpを使う用に設定することで対応します。

```
$ cat ./ansible.cfg
...
[ssh_connection]
ssh_args=
scp_if_ssh=True
...
```

## opensshとの使い分け

必須なのは、特定の接続元のみからの接続を許す。
理想的には、特定のユーザへのログインのみ許す、dropbear側で使うアカウントとopensshで使うアカウントを分ける、でしょうか。

前者は、dropbear自体でやることではなくて、サーバ側のufwでやることなので問題なし。
opensshとポートを分けておくので、dropbear側のポートの接続制限を厳しくすればいいだけ。

後者は、、難しいそうですね。
opensshには使ったことはないのですが、AllowUserという設定があってそこでログインできるユーザを指定できます。あるいはpam側でpam_accessを使えばできそうです。
一方、dropbearにはそういう概念なし。
なので、dropbear側からはどのユーザアカウントにも証明書認証で入れるようになります。

openssh側でldap認証させるようにしてアカウント情報を外出しすれば実装できるのかな、、そこまで探すのめんどくさいのでやってません。

まぁ、今回はdropbearはansibleで設定いじるときしか使わない前提で考えていますから、平時はサービス停止しておいて、メンテのときだけdropbearを起動して使う、くらいが安全でしょうか。

# まとめ

日頃使い用に認証を固めたSSH（openssh）と、メンテ用に鍵認証だけでOKにしたdropbearを併用するノウハウについて整理しました。
最終的にはopensshのmatchオプションだけでいいやになるかもですが、手札は大いに越したことはなかろということで。