SSH通信環境構築

<!-- 1 Intro ------------------------------------------------------------------->

# Introdaction
目的：
- UbuntuサーバにOpenSSH環境を構築する
- 証明書によるユーザ認証方式で構築する

※設定を行ったときの覚書
※以下ではサンプルとして適当な鍵を実際に作ったときの流れをまとめている。

<!-- 2 Env------------------------------------------------------------------->

# 環境
Remote Host: Ubuntu 20.04； Active directory参加；LAN内からの接続のみ可能）
Local Client: Ubuntu 20.04; wsl2 on windows10（LANへのVPN接続可能）
SSH -v: OpenSSH_8.2p1 Ubuntu-4ubuntu0.2, OpenSSL 1.1.1f  31 Mar 2020


<!-- 3 What is SSH----------------------------------------------------------->

# SSHとは
The secure shell (SSH)は，request for commentsで定められているプロトコルの総称である。
一番の特徴は，通信経路の暗号化である。
client-server間のネットワークを暗号化し，第三者による盗聴のリスクを排除する。
詳細は『OpenSSH [実践]入門』[[ref. 1](#references)]を参照されたし

<!-- 4 Server setting------------------------------------------------------->

# SSH基本設定

### Server setting
`openssh-server`パッケージをインストールする
今回は，`tasksel`経由で`OpenSSH server`をインストールしたが，もちろんaptで拾ってきてもよい。

```bash:RemoteHost
$ tasksel --list-task |grep SSH
u openssh-server	OpenSSH server
$ tasksel install openssh-server
$ ufw allow ssh #firewallでSSH通信を許可
```

<!-- 5. test connection------------------------------------------------------>
### Client setting
まずは接続できるかを確認する

```bash:LocalClient
$sudo apt install -y ssh-client
$ssh username@hostname
The authenticity of host 'RemoteHostName (IP address)' can't be established.
ECDSA key fingerprint is xxxxxxxxxxxxxxxxxxxxxx.
Are you sure you want to continue connecting (yes/no/[fingerprint])?yes #fingerprintがあっていればyesで継続
username@hostname's password:
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.8.0-55-generic x86_64)
・・・
```

初めて接続する際に，接続を試みているサーバが正しいかを確認するため，公開鍵の指紋を確認するように要求される。
fingerprintは，サーバ側で`ssh-keygen -lf /etc/ssh/keyname.pub`によって確認できる。
クライアントユーザは事前にfingerprintをサーバ管理者に聞いておく必要がある。
fingerprintに間違いがなければ接続を`yes`を打ち込み接続する。
これはクライアント側の`.ssh/known_hosts`に記録され，次回からは自動的に接続先が合っているかを確認してくれる

`hostname`の部分はipアドレスの指定でも可能。
接続は，`exit`で切断できる。

<!-- 6. Authentication ---------------------------------------------------->

# 証明書を利用した認証
認証局は公開鍵と秘密鍵のペアを１つもつ。
認証局は，その秘密鍵によってユーザの公開鍵へと署名を行う（i.e,証明書の発行）
クライアントが証明書を使用して認証を要求してきたとき，認証局はその公開鍵によって承認する。

設定において、２名がそれぞれの役割を果たす必要がある
・RemoteHost側のサーバ管理者 >>　認証局の設定と証明書の発行
・LocalClientのユーザ　>>　鍵ペアの作成と公開鍵の提出

<!-- 6-1. CA ---------------------------------------------------->
### Step-1. 認証方式の設定

SSHサーバの設定をおこなう。
`/etc/ssh/sshd_config`を変更することで，認証方式を変えることができる。

```bash:RemoteHost
$sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.org
$sudo vim /etc/ssh/sshd_config
```

```bash:/etc/ssh/sshd_config
#Port 22　　　# デフォルトは22。セキュリティ対策としてport変更するなら変える

# Authentication:
#AuthenticationMethods publickey #公開鍵認証にする場合
#公開鍵とパスワードの両方を認証方程式にする場合は以下の記述をし，
#"PasswordAuthentication"と"ChallengeResponse Authentication"をアクティブにする
AuthenticationMethods publickey,password publickey,keyboard-interactive 

PubkeyAuthentication yes   # 公開鍵認証のための設定

# 鍵認証のみにするなら以下二つはnoにしておく
PasswordAuthentication yes
ChallengeResponseAuthentication yes 

# サーバ管理的には証明書発行した方が楽なので，鍵の指定には認証局の鍵を使う
# cert-authorityオプションをつけた証明局の鍵を指定
AuthorizedKeysFile /etc/ssh/.ssh/ca-key.pub　

#　X11 forwardingしたいならactiveにしておく
X11Forwarding yes

PermitRootLogin no　　　　　# rootログインはリスクがあるため禁止にする

```

```console:RemoteHost
$sudo systemctl restart sshd
$sudo systemctl status sshd
 ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2021-06-14 18:36:16 JST; 19h ago
 ・・・
```

ポート番号を変更する場合は，

- WELL KNOWN ポート番号（0-1023）
- REGISTERED ポート番号（1024-49151）
- Dynamic/private ポート番号（49152-65535）

のうち，ユーザが基本的に自由に使えるDynamicポートから空いているものを指定すればよい。
xxxxポートが開いているかどうかは`lsof -i:xxxx`で確認できる。

なお，本当にポート番号を変える必要があるかどうかは環境による。
> SSHdを22番ポート以外でListenさせる理由は、リモートログインする基点が固定IPアドレスではないという状況下で22番ポートへの侵入を目的とするアタックを回避するという点でしょう。認証を破られない為という事ではなく、SSHdが接続拒否に使うCPUリソースの節約、ゴミのような認証失敗のログを減らすという観点に基づいてのポート番号変更です。

> VPN経由で内部からしかリモートログインできない、特定のIPアドレスからのみリモートログイン出来る、という場合はFirewallがしっかり22番ポートを守ってくれるので、わざわざほかのポート番号に変更する必要性はないかもしれません。

引用元：

http://blog.azumakuniyuki.org/2011/05/sshd-port-number-should-be-changed-to.html


`lastb`コマンドで不正ログイン（ログイン失敗）が確認されたら，ポート番号を変えることで対応の負荷対策にはなるかもしれない。

<!-- 6-2. Keygen -------------------------------------------------------->
### Step-2. 認証局の鍵ペア作成

鍵の作成は，`ssh-keygen`を使う

```bash:ssh-keygen
$ mkdir .ssh/keys
$ cd .ssh/keys
$ ssh-keygen -f ca-key -t ed25519
Generating public/private ed25519 key pair.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in ca-key.
Your public key has been saved in ca-key.pub.
The key fingerprint is:
SHA256:******************************************* username@remotehost
The key's randomart image is:
+--[ED25519 256]--+
|++               |
|+.o .            |
|+o . .           |
|+ o o     o      |
| + o .  So .     |
|  o + +.o..      |
| . = * =.E .     |
| o. XoB.o .      |
|Oo+O*B.o..       |
+----[SHA256]-----+
```

```bash:RemoteHost
$ ls -la .ssh/keys | grep ca-key
ca-key
ca-key.pub
```

という感じで`秘密鍵`と`公開鍵（.pub）`が作成された
認証局の鍵に関しては，パスフレーズを決めておいた方が良いだろう
鍵の暗号方式は、EdDSA（ED25519）が比較的新しく、パフォーマンスとセキュリティに優れるそう。 OpenSSH 6.5 以降であればED25519を使うことが望ましいようである。

署名用の公開鍵であることを示すために`cert-authority`オプションをつける。

https://man.openbsd.org/ssh-keygen

```console:RemoteHost
$ sed -i 's/./cert-authority &/' ca-key.pub
$ cat ca-key.pub
cert-authority ssh-ed25519 *******************************
```


次に、認証局の公開鍵を、上記[__Server setting__](#Server setting)の`/etc/ssh/sshd_config`で指定した場所に置く。
`AuthorizedKeysFile`で指定している公開鍵は、`/etc/ssh/.ssh/ca-key.pub`となっているため、今回の場合ではここにコピーするだけでよい。

```bash:RemoteHost
$ sudo cp ca-key.pub /etc/ssh/.ssh/
```

基本的なサーバのセッティングとしては以上で終わり。
ユーザがSSH通信を求めてきた際に、

1. ユーザの公開鍵を受け取る
2. 認証局の秘密鍵でユーザの公開鍵に署名した証明書を発行する
3. 証明書をユーザに渡す

という作業を行なっていくことになる


<!-- 6-3. Client Leyden ---------------------------------------------------->
### Step-3. クライアント側での認証鍵作成

SSH通信したいユーザ（LocalClient）が自分の鍵を作成する
方法は認証局の鍵を作るときと同一

```bash:LocalClient
$ ssh-keygen -f id_ecdsa_username -t ed25519 
```

重要なのはここからで、どうやって公開鍵をRemoteHostに渡すかが問題となる。
SSHの認証方式をパスワードにしていたらこの状態からsftpやssh-copy-idによりコピーが可能。
しかし、サーバ側が証明書認証のみを採用して運用されている状況ではこれらの方法は使えない。
メール、物理的に記憶媒体を手渡す、IP制限なりセキュリティ管理しているsambaサーバなり、色々検討する必要がある。
環境に合わせてセキュアな方法を選ぶ必要がある。


<!-- 6-4. Certificate ---------------------------------------------------->
### Step-4. 証明書の発行

前項でユーザが公開鍵をサーバに引き渡した後の処理となる。
つまりサーバ側には、

- 認証局の秘密鍵と公開鍵
- ユーザの公開鍵

がある状況となっている。
この認証局の秘密鍵を使って、ユーザの公開鍵に署名を行う。

```bash:RemoteHost
admin@funnel:~/.ssh/keys$ ssh-keygen -s ca-key -I "Key ID str" -n "usr" -V 20210401:20220331 -z 1001 id_ed25519_username.pub
Enter passphrase: #ca-keyのパスフレーズ
Signed user key id_ed25519_username-cert.pub: id "domain+usr1" serial 1001 for usr1 valid from 2020-04-01T00:00:00 to 2021-03-31T00:00:00
admin@funnel:~/.ssh/keys$ 
$
$ ls |grep id_
id_ed25519_username
id_ed25519_username.pub
id_ed25519_username-cert.pub

```

これで`-cert.pub`のついた証明書が発行された
ssh-keygenの署名におけるオプションは以下

| option | explanation 　　　　　　|
|:--|:--|
| -s | CA秘密鍵指定 |
| -I | Certificate：identity<br>署名するときにkey identityを指定する<br>sshの認証後に auth.log に記録が残る|
| -n | Principals：ユーザ，もしくはホスト名<br>ここで指定されたユーザでのみ有効　 |
| -z | Serial number：証明書執行時に必要となる |
| -V | Validity interval： 有効期限を決められる|

証明書の内容を確認する

```bash:RemoteHost
$ ssh-keygen -L -f id_ed25519_username.pub 
id_ed25519_username.pub:
        Type: ssh-ed25519-cert-v01@openssh.com user certificate
        Public key: ED25519-CERT SHA256:***************************
        Signing CA: ED25519 SHA256:*********************** (using ssh-ed25519)
        Key ID: "Key ID str"
        Serial: 1001
        Valid: from 2021-04-01T00:00:00 to 2022-03-31T00:00:00
        Principals: 
                username
        Critical Options: (none)
        Extensions: 
                permit-X11-forwarding
                permit-agent-forwarding
                permit-port-forwarding
                permit-pty
                permit-user-rc
```

サーバ管理者は，各ユーザの証明書を発行したのち，証明書と秘密鍵を渡しておく

<!-- 6-5. Connect with certificate ----------------------------------->
### Step-5. 接続 

クライアント側のユーザは、自分の秘密鍵と同じディレクトリに証明書を補完する。
ssh接続時には、秘密鍵を指定して接続を行う

```bash:LocalClient
$ ssh usr@remotehost -i .ssh/id_ed25519_username
・・・以下略
```

`-v`オプションでdebug messageを確認すると認証プロセスを確認できる。
X11 forwarding使いたいなら，`-X`オプションをつける。

以上の設定では、毎回パスフレーズを聞かれることになる（パスフレーズを空にして進めてきた場合は異なる）。
連続で何度もsshを行うようなシチュエーションがある場合、認証エージェント`ssh-agent`を使えばパスフレーズの認証を最初の１回のみに抑えられる。
起動時に毎回行う必要があるため、`.bashrc`に加えておくなど適宜調整を図れば良い

```bash:LocalClient
$ val $(ssh-agent) 
Agent pid ****

$ ssh-add .ssh/id_ed25519_username #秘密鍵の登録
Enter passphrase for /home/ubuntu/.ssh/id_ed25519_username:
Identity added: /home/ubuntu/.ssh/id_ed25519_username 

$ ssh-add -l #確認
*** SHA256:****************** username@remotehost

```

```console:LocalClient
# 鍵ペアをセットしているホストへパスフレーズ入力を省略して接続可能か確認
$ ssh username@remotehost 
・・・接続。以下略
```

```console:LocalClient
$ eval $(ssh-agent -k)  # SSH-Agent プロセスを終了
Agent pid **** killed
```
プロセス終了せずにログアウトするとプロセスが残ったままになる


# まとめ
サーバ管理者として行うのは以下の３点
1）各ユーザごとに証明書を発行
2）ユーザには証明書と秘密鍵を渡し，同じディレクトリで保管させる
3）ホストの鍵のfingerprintを渡す

ユーザが行うのは以下の３つ
1）秘密鍵と証明書を同じディレクトリに厳重に保管
2）接続時にはホスト鍵のfingerprintを確認する
3）接続時には秘密鍵を指定する

<!-- 7. Ref ---------------------------------------------------->
# References
以下は、sshサーバ構築のために参考にしたサイトや書籍

1. 『OpenSSH [実践]入門』川本安武　著, 技術評論社 < https://gihyo.jp/book/2014/978-4-7741-6807-4 >
2. SSHのCA認証 -Qiita< https://qiita.com/aat00000/items/a7973b104be9bfd3bb5c >
3. OpenSSH の 証明書認証 -Qiita< https://qiita.com/Hexa/items/79897f7df5ad59bb5ca1 >
4. SSHの公開鍵認証における良くある誤解の話 ーQiita< https://qiita.com/angel_p_57/items/2e3f3f8661de32a0d432 >
5. SSHdのポートは何番に変更すべきか < http://blog.azumakuniyuki.org/2011/05/sshd-port-number-should-be-changed-to.html>
6. OpenSSHの認証に証明書を使う方法 < https://support.conoha.jp/v/openssh/ >
7. SSHコマンドがだるすぎる、、、公開鍵認証&ssh configで楽にしましょう< https://takakisan.com/ssh-public-key-config/ >
8. OpenSSHの暗号化周りの設定について -Qiita < https://qiita.com/aqmr-kino/items/8c3306ea8022b0d5cbe4 >