Ｊ－Ｗｅｂで公的ＳＳＬ証明書の運用

# Ｊ－Ｗｅｂで公的ＳＳＬ証明書を運用してみる

ジュニパー社製ＳＲＸ３００上のＪＵＮＯＳ ２０．１Ｒにて公的[^備考１]なＳＳＬ証明書を設定してみた。通常この手のネットワーク機器の管理画面で使用するＳＳＬ証明書は、自己署名証明書を使用し、その運用もテキトーなケースが多い。
しかし（モチベーションについては後で書く）。

## はじめに
今回以下の装置、バージョンをターゲットに実験してみた。微妙な最新バージョン、メーカー推奨バージョンでないのはツッコミ不要で。

```
root@XXXXXXXXXXXX> show version
Hostname: XXXXXXXXXXXX
Model: srx300
Junos: 20.1R1.11
JUNOS Software Release [20.1R1.11]

root@XXXXXXXXXXXX> show chassis hardware
Hardware inventory:
Item             Version  Part number  Serial number     Description
Chassis                                XXXXXXXXXXXX      SRX300
Routing Engine   REV 0x08 650-077890   XXXXXXXXXXXX      RE-SRX300
FPC 0                                                    FPC
  PIC 0                                                  6xGE,2xGE SFP Base PIC
Power Supply 0
```

# 設定手順
以下の順序で設定を行う。

1. [鍵作成](#鍵作成)
2. [ＣＳＲ作成](#ｃｓｒ作成)
3. [署名（公的ＣＡへの申請）](#署名公的ｃａへの申請)
4. [署名済み公開鍵の取り込み](#署名済み公開鍵の取り込み)
5. [Ｊ－Ｗｅｂへの設定](#ｊｗｅｂへの設定)

一連の作業は原則「オペレーションモード」で実施し、一部「シェルモード」や「コンフィギュレーションモード」で作業を実施する。

## 鍵作成
```
request security pki generate-key-pair certificate-id 証明書ＩＤ type ecdsa size 256
```

- ここではＰＲＩＭＥ２５６ｖ１証明書を作成してみる（`type ecdsa size 256`）。
- ＲＳＡ２０４８ビット証明書を作成したい場合は `type rsa size 2048` と指定する。
- `type` には `rsa` と `ecdsa` が指定可能[^備考２]
- `size` には `type` に応じて以下の通りの指定が可能。
    - `type rsa` の場合 `1024`、`2048`、`4096`。
    - `type ecdsa` の場合 `256`、`384`、`521`。
- `type` と `size` を指定して作成させる鍵についての詳細はここでは説明しない[^備考３]。

※ここで「証明書ＩＤ」を決めて置き換える必要がある。たとえば具体的に「`j-web`」みたいな指定をする。

## ＣＳＲ作成
```
request security pki generate-certificate-request certificate-id 署名書ＩＤ domain-name コモンネーム subject "CN=コモンネーム"
```

- 作成された鍵からＣＳＲを作成する。ＣＳＲに必要なパラメータを `domain-name` と `subject` で指定する。
- 本例での `subject` の指定は [Let's Encrypt](https://letsencrypt.org/ja/) 認証局を使用する前提で記述した。
- 他の認証局を使用する場合は、他の認証局が要求するディスティングイッシュ名を記述する（`C` や `ST`、`O` など）。
- 「コモンネーム」については `subject` と `domain-name` で独立したＦＱＤＮを指定できる。
- これらパラメータの柔軟性を調べた限りでは、コモンネームと同一にしておく必要がある（別名の運用不可）。
- これは今時のブラウザの振る舞いとして `subject` で指定されるコモンネームでは無く、ＳＡＮｓ（Subject Alter Names）で指定されるＦＱＤＮをもって、[評価される](https://www.ipentec.com/document/windows-chrime-error-net-err-cert-common-name-invalid-using-ssl-certificate-signed-with-local-ca)からである。
- よってＦＱＤＮを別々に設定することもできるが、事実上 `domain-name` での指定しか有効にならないことになる。

## 署名（公的ＣＡへの申請）
ここでは具体的な操作は取り扱わない。先のコマンドを実行した際に表示されるＣＳＲを認証局に申請する。

## 署名済み公開鍵の取り込み
この作業にあたり認証局から発行された署名済み公開鍵をローカルにファイル保存しておく。`file copy` を使用する他、シェルモードで落ちて、`cat` や `vi` コマンド駆使してファイルを作成しておく。

```
request security pki local-certificate load certificate-id 証明書ＩＤ filename 証明書ファイル名
```

## Ｊ－Ｗｅｂへの設定
コンフィグレーションモードで以下の設定を行う。

```
set system services web-management https pki-local-certificate 証明書ＩＤ
commit
```

設定するだけなら以上である。おおよそ十数秒程度で設定が反映される。

## 証明書の確認

一連の手続きは `show configuration` ではその存在を確認できない。そのためバックアップし忘れる場合があるので注意が必要である。

- ＣＳＲの一覧 `show security pki certificate-request`
- 証明書の一覧 `show security pki local-certificate`

## 証明書の削除

コンフィグレーションモードにて、下記のようにして、証明書ＩＤが参照されなくなったことが前提であるが、

```
delete system services web-management https pki-local-certificate 証明書ＩＤ
  または
set system services web-management https pki-local-certificate 新しい証明書ＩＤ
```

下記の手順で証明書の削除が可能である。

```
clear security pki local-certificate   certificate-id 証明書ＩＤ
clear security pki certificate-request certificate-id 証明書ＩＤ
clear security pki key-pair            certificate-id 証明書ＩＤ
```

# 動作確認

## 設定事項の確認
動作確認に当たって、以下の設定がなされていること。

- Ｊ－Ｗｅｂ用インターフェースにＩＰアドレスが設定されていること。
- そのインターフェースが特定ゾーンに所属していること。
- 特定ゾーンには下記の設定がなされていること。

```
set security zones security-zone 特定ゾーン host-inbound-traffic system-services https

```

この条件は[ＳＲＸ３００の初期設定](https://www.juniper.net/documentation/en_US/release-independent/junos/information-products/topic-collections/hardware/srx-series/srx300/530-081623-srx300-how-to-set-up.pdf)によれば、ポートラベル `0/1` または `0/6` からアクセスした場合の振る舞いとなる。

あるいはＤＨＣＰクライアントが稼働しているネットワーク上で、ポートラベル `0/0` または `0/7` に接続した場合でも同じである（デフォルトで外部からＪ－Ｗｅｂにアクセス可能な点に注意）。

## 通信確認

- ブラウザでアクセスしてみる。
- `curl` 等のコマンドを使用してアクセスしてみる（失敗する可能性は高い、後述）。
- [testssl.sh](https://testssl.sh/)で評価してみる。

# 本設定の性質と注意点

- 設定を見ての通り、中間証明書の設定を行っていない。これの設置方法を見つけることができなかった。
- よって `curl` コマンドで通信エラーが発生する。`curl` コマンドであれば `-k` オプションを指定して逃げる。
- これはＵｎｉｘ系ＯＳではルート証明書のみを保持し、自身では中間証明書を持たないためである。
- ブラウザ、特にＷｉｎｄｏｗｓ環境では正常に通信できる。これはＷｉｎｄｏｗｓではルート証明書以外にもいくらか中間証明書を保持しているからである。
- `testssl.sh` を実行するとわかるが、いくらか不親切[^備考４]な点がある。

## 対応プロトコル
対応プロトコルはＴＬＳ１．２のみ、となる。それ以前のプロトコルが無効になってる点について高く評価したいが、ＴＬＳ１．３に未対応な点については今後に期待する。
今（２０２０年）時点は仕方ないかな。

## 暗号スイート
暗号スイートの設定方法が見付からなかった。以下の暗号スイートに対応しているのでおおよそ問題無いが、もう少しなんとかしたいところ。

- `ECDHE-ECDSA-AES256-GCM-SHA384`
- `ECDHE-ECDSA-AES256-SHA384`
- `ECDHE-ECDSA-AES128-GCM-SHA256`
- `ECDHE-ECDSA-AES128-SHA256`

またサーバー優先オーダーでないのも、もどかしい。

## 鍵交換アルゴリズム
鍵交換アルゴリズムはＥＣＤＨＥのみであるため高めに評価したいが、今ひとつ感漂うパラメータが選択されてる。いささか癖が強すぎるので、整理したいところ[^備考５]。

- `sect283k1`
- `sect283r1`
- `sect409k1`
- `sect409r1`
- `sect571k1`
- `sect571r1`
- `secp256k1`
- `prime256v1`

## その他
クリティカルではないが以下の点が気になる。

- クライアント側セキュア再ネゴシエーションが有効である。
- ＯＣＳＰスタンプリングに対応してない。
- 暗号スイートを見ての通りＣＢＣモードに対応している。

# ベリファイ

インストールした証明書が正しいかの検証（ベリファイ）は、ルート証明書、中間証明書をインストールすることで可能になる。
この手順自体の評価を見定めていない[^備考６]ため、取り急ぎメモ程度の位置付けとなる。

ここでは [Let's Encrypt](https://letsencrypt.org/ja/) 認証局での事例を参考に設定してみる。[ルートおよび中間証明書](https://letsencrypt.org/ja/certificates/)は下記より取得する。

- [Let’s Encrypt Authority X3 (IdenTrust cross-signed)](https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem.txt)
- [identrust.com TrustID X3 Root](https://letsencrypt.org/certs/trustid-x3-root.pem.txt)

※[Chain of Trust](https://letsencrypt.org/ja/certificates/)を見ると、どの証明書を選択すべきか判断しかねる話ではあるが、これを説明すると非常に長くなるので、ここは上記決め打ちで。
例えば今回の選択は中間証明書というよりはクロスルート証明書じゃないかーというツッコミはあるが…（ごにょごにょ）。

【メモ】ＣＡプロファイル名については以下のように決めた。

- ルート証明書 `dst-root-ca-x3`
- 中間証明書 `lets-encrypt-x3`

`ca-identity` で指定する内容について意図が理解できなかったので、とりあえずコモンネームを指定してみた。何を指定すべきか指標が見当たらなかったので、ここはテキトーに決めたということで。

## ルート証明書のＣＡプロファイルの設定
コンフィグレーションモードにて下記の設定を行う。

```
set security pki ca-profile dst-root-ca-x3 ca-identity "DST Root CA X3"
set security pki ca-profile dst-root-ca-x3 revocation-check disable
commit
```

その後オペレーションモードでルート証明書を取り込む。

```
request security pki ca-certificate load ca-profile dst-root-ca-x3 filename ルート証明書ファイル名
```

`Do you want to load this CA certificate ? [yes,no] (no)` と聞いてくるので `yes` と答える。

## 中間証明書のＣＡプロファイルの設定
コンフィグレーションモードにて下記の設定を行う。

```
set security pki ca-profile lets-encrypt-x3 ca-identity "Let's Encrypt Authority X3"
set security pki ca-profile lets-encrypt-x3 revocation-check disable
commit
```

その後オペレーションモードで中間証明書を取り込む。

```
request security pki ca-certificate load ca-profile lets-encrypt-x3 filename 中間証明書ファイル名
```

`Do you want to load this CA certificate ? [yes,no] (no)` と聞いてくるので `yes` と答える。

## 実際にベリファイしてみる
上記準備ができた状態で、以後、下記コマンドでベリファイできるようになる。

```
root@XXXXXXXXXXXX> request security pki ca-certificate verify ca-profile dst-root-ca-x3
CA certificate dst-root-ca-x3 verified successfully

root@XXXXXXXXXXXX> request security pki ca-certificate verify ca-profile lets-encrypt-x3
CA certificate lets-encrypt-x3 verified successfully

root@XXXXXXXXXXXX> request security pki local-certificate verify certificate-id 証明書ＩＤ
Local certificate 証明書ＩＤ verification success
```

## より厳密なベリファイについて
上記ベリファイについて、証明書が失効してないかをオンラインで確認する機能が提供されている。その切り替えは下記の通り実施すればよい。

```
delete security pki ca-profile ＣＡプロファイル名 revocation-check disable
set security pki ca-profile ＣＡプロファイル名 revocation-check ocsp
commit
```

手元で確認すると上記設定でのベリファイに失敗する。

```
root@XXXXXXXXXXXX> request security pki local-certificate verify certificate-id 証明書ＩＤ
Error: CRL download failed for certificate サブジェクト
```

原因について `set security pki traceoptions flag all` を有効にして調べたところ、下記のメッセージを残すのみで、ＣＲＬがデータベースになかった理由は不明であった。

```
Cert-Chain-Val> Checking for revocation
Cert-Chain-Val> revocation 1
ldapRetrieveCRL
check_local_crl Error: CRL not found in the database. ret = 0
check_local_crl return rc = -1
ldapStart Revocation check failed for Certificate サブジェクト with serial number 証明書のシリアル番号
Cert-Chain-Val> exit (67190821)
ldapNotify_func: err_id<67190821>
ldapIdleCleanup <154><144><124><235>
```

筆者限界につき、`revocation-check disabled` としてる。

# よくある質問とその答え

## Ｑ．中間証明書の設置方法として、署名済み公開鍵をフルチェインさせてやればいいのでは？
Ａ．フォーマットエラーで取り込めません。

## Ｑ．証明書の更新はどうすればいいんでしょうか？
Ａ．新しい証明書ＩＤ（例えば `j-web-2` など）を発行して手続きを行い、下記の手順で切り替えてやります。

```
set system services web-management https pki-local-certificate 新しい証明書ＩＤ
commit
```

## Ｑ．自動更新したいな。
Ａ．ピキピキ♪
- っ ＳＣＥＰ（Simple Certificate Enrollment Protocol）
- っ ＣＭＰｖ２（Certificate Management Protocol version 2）

課題が多くて自動更新については調査してないけど、[Go SCEP server](https://github.com/micromdm/scep)辺り試してみるかな、という程度で。どうＵＲＬを指定するだとか、保存されるディレクトリはどこか、とか、実際の更新手順はどうなってるのか、といったドキュメントが無いのがまた…。

## Ｑ．どうしてここまでやるの？ 何が問題かわからない。
Ａ．めざせ [SSL Server Test](https://www.ssllabs.com/ssltest/) でＡ評価。今時モダンなＳＳＬサーバーの設定関する指標は [Security/Server Side TLS](https://wiki.mozilla.org/Security/Server_Side_TLS)にて紹介されているので、頑張って設定しよう。

# 参考文献

- [J-Web for SRX Series Documentation](https://www.juniper.net/documentation/product/en_US/j-web-srx-series/)
- [Secure Web Access for Remote Management](https://www.juniper.net/documentation/en_US/junos/topics/topic-map/secure-web-access-remote-management.html)
- [Public Key Infrastructure (PKI)](https://apps.juniper.net/feature-explorer/parent-feature-info.html?pFKey=1186&pFName=Public%2BKey%20Infrastructure%20(PKI)) ※機能別、機種と対応したバージョンの表

[^備考１]: 認証局で発行された…という意味で。
[^備考２]: `dsa`も指定できるが現在は無価値。
[^備考３]: なぜ `type ecdsa size 256` が `prime256v1` なのか…等。
[^備考４]: 不適切とまでは言わないが…言いたくないが…なんとかしたいところ。
[^備考５]: 実質 `prime256v1` しか選択されない。
[^備考６]: できたところで何がうれしいか不明のため。