Vault認証用のTLSクライアント証明書を失効させる

## はじめに
今年、わたしはTLS証明書をおもらししました。
Vault認証用のTLSクライアント証明書を、うっかりGitHubのPrivate RepositoryにPushしてしまったのです。
本来、GitHubでは管理しない用途のSecretだったので、さて、困ったぞ。どうやって失効すればよいのやら、ということで、作成方法・検証方法含め整理しました。
今後、おもらしした時の参考になれば幸いです。

## TLSクライアント証明書によるVault認証

公式ドキュメントはこちらになります。https://www.vaultproject.io/docs/auth/cert
VaultでTLSクライアント証明書認証を有効化し、クライアント証明書を登録することで、TLSクライアント証明書による認証ができるようになります。
失効に関しては、Vault 0.4からRevocation Checkingの機能(CRL)がサポートされています。
CRLのSerial NumberとTLS証明書のSerial Numberが一致する場合、認証を拒否するという仕組みです。

CRLは、Certificate Revocation List(証明書失効リスト)の略です。
検索しても、ふわっとした情報しかヒットしないので、詳しく知りたい方は、1次ソースである[RFC5280](https://datatracker.ietf.org/doc/html/rfc5280)を参照してください。

## TLSクライアント証明書の失効手順

Vault PKIにCRLを登録することで、TLSクライアント証明書認証に使っているTLS証明書を失効することができます。
失効手順は以下となります。

1. 失効する証明書のSerial Numberを確認し、ファイルに出力する
1. Serial Numberを含むCRLファイルを作成する
1. Vault PKIにCRLを登録する

具体的な手順を紹介していきます。

### 実行環境

今回の実行環境は以下となります。

- MacOS Big Sur
- cfssl 1.6.1
- vault cli v1.8.3
- openssl LibreSSL 2.8.3
- python 2.7

### 1. 失効する証明書のSerial Numberを確認し、ファイルに出力する

まず、CRLに登録する対象の証明書(以下の例ではclient.pemファイルを対象)のSerial Numberを確認します。

```
$ cat client.pem | openssl x509 -noout -text -in /dev/stdin

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            XX:XX:.......:XX   <---- この値です
    Signature Algorithm: ecdsa-with-SHA256
        Issuer: CN=FOO Root CA
        Validity
            Not Before: Mar  8 07:51:00 2021 GMT
            Not After : Mar  8 07:51:00 2022 GMT
```

CRLファイル作成時に`cfssl`を利用しています。`cfssl`は`hex`形式の`Serial Number`をサポートしていないため、`decimal`に変換します。 以下では、`hex`から`decimal`の変換の際、`bash`で変換しようとすると桁があふれ失敗したため、`python`を利用しました。

```
$ python -c "print(int(\"`openssl x509 -in client.pem -noout -serial | awk -F '=' '{print $2}'`\", 16))" > serial.txt
```

Serial Numberを`decimal`に変換し、`serial.txt`として保存しました。

### 2. Serial Numberを含むCRLファイルを作成する

CRLファイルは、証明書を発行したCAによって署名されています。 そのため、TLSクライアント証明書認証のためにPKIのルートCAのCertとKeyが必要です。
ここでは、それぞれ`ca.pem`と`ca-key.pem`とします。

`cfssl`を利用し、`Serial Number`を持ったCRLファイル`crl.pem`を作成します。

```
$ cfssl gencrl serial.txt ca.pem ca-key.pem | base64 -D | openssl crl -inform DER -out crl.pem
```

Vaultに登録する前に、作成したCRLファイルを確認し、`openssl x509 -in client.pem -noout -serial`と同じ`Serial Number`が登録されているか確認します。

```
$ cat crl.pem | openssl crl -text
Certificate Revocation List (CRL):
        Version 2 (0x1)
    Signature Algorithm: ecdsa-with-SHA256
        Issuer: /CN=FOO Root CA
        Last Update: Oct  1 11:38:59 2021 GMT
        Next Update: Oct  8 11:38:59 2021 GMT
        CRL extensions:
            X509v3 Authority Key Identifier:
                keyid:00:1F:D4:28:97:1F:E7:6B:2E:5D:44:FE:35:68:03:84:B0:C9:22:9E

Revoked Certificates:
    Serial Number: XXXXXXXXXXXXXXXXX  <------ ここを確認
        Revocation Date: Oct  1 11:38:59 2021 GMT
    Signature Algorithm: ecdsa-with-SHA256
         30:44:02:20:5f:6f:f3:fa:c5:dc:4e:23:04:9e:36:6e:32:f9:
```

### 3. VaultにCRLを登録する

VaultにCRLを登録します。 CRLは任意の名前で登録することができます。
以下の例では、日時にしました。
また、今回は`/auth/cert/`をPKIのパスとしています。
このパスはご利用のVaultの設定、Policyに依存するので、このパスは必要に応じて変更してください。
cf. https://www.vaultproject.io/api-docs/auth/cert

```
# 環境に合わせた設定で、ログインしてください
$ VAULT_ADDR=[Vault Server] vault login ...
$ CRL_NAME=`date "+%Y%m%d%H%M%S"`; vault write /auth/cert/crls/$CRL_NAME crl=@crl.pem
```

登録したCRLを確認し、`serial.txt`のSerial Numberと一致しているか確認します。

```
$ vault read --field=serials auth/cert/crls/$CRL_NAME
map[XXXXXXXXXX....:map[]]
```

### 認証エラーになるか動作確認

最後に、TLSクライアント認証でVaultにログインができなくなったことを確認します。

```
$ vault login \
    -method=cert \
    [この他,ca-certなど必要あれば指定してください...] \
    -client-cert=client.pem \
    -client-key=client-key.pem \
    -path=auth/cert/ \
  name=[role名]
Error authenticating: Error making API request.

URL: PUT https://[Vault Server]/v1/auth/cert/login
Code: 400. Errors:

* no chain matching all constraints could be found for this login certificate

```

ああ、よかった。
これでHappy Holidayが過ごせそうです:christmas_tree: