AWS Client VPNクライアント証明書の管理手順を整理してみた

OpenVPNのeasy-rsaをつかってAWS Client VPNのクライアント証明書を管理する方法を紹介します。クライアント認証では認証局(CA)のルート証明書が肝です。
2020.05.19

AWS Client VPNは2種類のクライアント認証を提供します。

  • Active Directory 認証
  • 証明書を使用した相互認証

本ブログでは、OpenVPN easy-rsaを使う、相互認証におけるクライアント証明書の基本的な管理方法について紹介します。

ポイント

  • VPNエンドポイント作成時の証明書指定
  • 相互認証のVPNエンドポイントを作成する場合、ACMにインポートしたサーバー証明書とクライアント証明書を指定
  • サーバーとクライアントの証明書が同じCAから発行される場合、VPN作成時のクライアント証明書のARNには、サーバー証明書のARNを指定できる。クライアント証明書のACMインポートは不要
  • クライアント証明書の発行
  • 利用者毎に個別の証明書を発行できる
  • 発行した証明書とVPNエンドポイントやACMとの関連付けは不要
  • クライアント証明書の失効
  • CA で失効で続きを行い、クライアント証明書失効リスト(CRL)を更新
  • CRLをクライアントVPNエンドポイントにインポート

検証環境

  • OS : Mac
  • AWS CLI : 1系
  • OpenVPN easy-rsa : 3.0.7

クライアントVPNの初期構築

まずは、「クライアント認証と認可 - AWS Client VPN」にある証明書の発行手順をなぞりながら、クライアントVPNの最低限の設定を済ませます。

1. OpenVPN easy-rsaをインストール

認証機関 (CA)を構築するサーバーにOpenVPN easy-rsaをインストールします。

Git でソースコードを取得することも可能ですが、次のURLからリリースされたアーカイブをダウンロードすることもできます。

Releases · OpenVPN/easy-rsa · GitHub

$ wget https://github.com/OpenVPN/easy-rsa/releases/download/v3.0.7/EasyRSA-3.0.7.tgz
$ tar zxfv EasyRSA-3.0.7.tgz
$ cd EasyRSA-3.0.7

2. PKI環境を初期化

新しい PKI 環境を初期化します。

$ ./easyrsa init-pki

3. 認証機関 (CA) を構築

新しい認証機関 (CA) を構築します。

$ ./easyrsa build-ca nopass

pki/ca.crtがこのCAのルート証明書です。

認証局(Issuer)が自分自身(Subject)に対して自己署名しています。

$ openssl x509 -text -noout -in pki/ca.crt 
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            ba:f6:6e:cf:78:74:62:2a
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=Easy-RSA CA
        Validity
            Not Before: May 18 06:48:58 2020 GMT
            Not After : May 16 06:48:58 2030 GMT
        Subject: CN=Easy-RSA CA
...

デフォルト設定では、有効期間がほぼ10年(3650日)あります。

4. サーバーとクライアント向けに証明書とキーを生成

サーバー向け

$ ./easyrsa build-server-full server nopass

クライアント向け

$ ./easyrsa build-client-full client1.domain.tld nopass

5. 主要ファイルをカスタムフォルダにコピー

サーバー証明書とキー、およびクライアント証明書とキーをカスタムフォルダにコピーしてから、カスタムフォルダに移動します。

$ mkdir ~/custom_folder/
$ cp pki/ca.crt ~/custom_folder/
$ cp pki/issued/server.crt ~/custom_folder/
$ cp pki/private/server.key ~/custom_folder/
$ cp pki/issued/client1.domain.tld.crt ~/custom_folder
$ cp pki/private/client1.domain.tld.key ~/custom_folder/
$ cd ~/custom_folder/

ここまでは公式ドキュメントと同じです。

6. ACMに証明書をインポート

クライアント VPNエンドポイントを作成する前に、ACMに証明書を登録します。

ドキュメントに記載されているように、「クライアント証明書の認証機関 (発行者) がサーバー証明書の認証機関 (発行者) と異なる場合にのみ、クライアント証明書を ACM にアップロードする必要があります。」

今回の証明書の作成手順では、クライアント証明書とサーバー証明書で同じ認証機関を利用しているため、サーバー証明書だけをACMに登録します。

$ aws acm import-certificate \
  --certificate file://server.crt \
  --private-key file://server.key \
  --certificate-chain file://ca.crt \
  --region region
{
    "CertificateArn": "arn:aws:acm:..."
}

なお、証明書のCA情報は認証局鍵識別子(Authority Key Identifier)から確認することも可能です。

  • server.crt
  • client1.domain.tld.crt

「X509v3 Authority Key Identifier」が同じであることを確認してください。

...
            X509v3 Authority Key Identifier:
                keyid:D9:FD:3C:41:44:1B:11:37:B2:9D:39:2E:64:82:A1:6A:57:CC:07:33
                DirName:/CN=Easy-RSA CA
                serial:BA:F6:6E:CF:78:74:62:2A
...

openssl コマンドで確認したルート証明書(pki/ca.crt)のSerial Numberとも一致していますね。

7. クライアントVPNエンドポイントの作成

クライアント VPN エンドポイントを作成します。

「認証オプション」で「相互認証の使用」をチェックし、「認証情報」の

  • サーバー証明書 ARN
  • クライアント証明書 ARN

両方に、先程アップロードしたサーバー証明書のARNを指定します。

8. VPN接続確認

VPNをプロビジョン後は、client1.domain.tld のキー・証明書でVPN接続できることを確認してください。

証明書の発行

全員が同じクライアント証明書を使い回すことも可能ですが、利用者ごとにクライアント証明書を発行して細かくアクセスコントロールするのが無難でしょう。

新規にクライアント証明書を発行するには、ユニークなエンティティ名を指定して easyrsa build-client-fullを実行します。

$ cd /path/to/easy-rsa/easyrsa3
$ ./easyrsa build-client-full client2.domain.tld nopass
...[snip]
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'client2.domain.tld'
Certificate is to be certified until Aug 20 13:48:09 2022 GMT (825 days)

Write out database with 1 new entries
Data Base Updated

あとは、出力された

  • (証明書)pki/issued/client2.domain.tld.crt
  • (キー)pki/private/client2.domain.tld.key

を利用者に展開します。

新規証明書とAWS環境との関連付けは不要です。 証明書の発行はCA内の操作に閉じています。

証明書の更新

証明書には期限が存在します。期限を迎える前に、証明書を再発行しましょう。

証明書を更新するには $ ./easyrsa renew エンティティ nopassを実行します。

エンティティ「client1.domain.tld」の証明書を更新する手順は以下の通りです。

# 証明書を更新
$ ./easyrsa renew client1.domain.tld nopass

Note: using Easy-RSA configuration from: ./vars
Using SSL: openssl OpenSSL 1.0.2t  10 Sep 2019


Please confirm you wish to renew the certificate with the following subject:

subject=
    commonName                = client1.domain.tld


Type the word 'yes' to continue, or any other input to abort.
  Continue with renew: yes
Generating a RSA private key
...
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'client1.domain.tld'
Certificate is to be certified until Jun 22 12:20:25 2021 GMT (400 days)

Write out database with 1 new entries
Data Base Updated
$

証明書の期限が400日後に延長されています。

出力された

  • pki/issued/client1.domain.tld.crt
  • pki/private/client1.domain.tld.key

をユーザーに展開します。

証明書の更新発行とAWS環境との関連付けは不要です。 証明書の発行はCA内の操作に閉じています。

証明書の期限管理

  • 証明書の有効期間(EASYRSA_CERT_EXPIRE。デフォルトは 825日)
  • 期限の何日前から証明書を更新できるか(EASYRSA_CERT_RENEW。デフォルトは30日)

は設定ファイルで変更できます。

vars.examplevarsにリネームし、該当行をコメントアウト、及び、値を変更して下さい。

vars

...
# In how many days should certificates expire?
set_var EASYRSA_CERT_EXPIRE   400
...
# How many days before its expiration date a certificate is allowed to be
# renewed?
set_var EASYRSA_CERT_RENEW 30
...

更新可能日よりも前に証明書を更新しようとすると、エラーが発生します。

次はEASYRSA_CERT_RENEWが30日にも関わらず、証明書が30日以上有効な状態で更新しようとした場合のエラーです。

$ ./easyrsa renew client1.domain.tld nopass

Note: using Easy-RSA configuration from: ./vars
Using SSL: openssl OpenSSL 1.0.2t  10 Sep 2019


Please confirm you wish to renew the certificate with the following subject:

subject=
    commonName                = client1.domain.tld


Type the word 'yes' to continue, or any other input to abort.
  Continue with renew: yes

Easy-RSA error:

Certificate expires in more than 30 days.
Renewal not allowed.

証明書の更新切れチェック

easy-rsa に更新間近の証明書を簡単にチェックする仕組みはないようなので、証明書一覧を取得し、各証明書の期限をチェックするような処理を定期実行したほうが良いでしょう。

証明書を失効

キーが漏洩したり、クライアントVPNを利用しなくなったユーザーに対しては、証明書を失効させます。

CAの証明書失効リスト(Certificate Revocation List;CRL)を更新し、VPNエンドポイントとCRLを関連付けます。

証明書を失効

CAに対して証明書を失効させ、CRLを更新します。

エンティティ「client2.domain.tld」の証明書を失効させる手順は以下の通りです。

# 証明書を失効させる
$ ./easyrsa revoke client2.domain.tld

Note: using Easy-RSA configuration from: ./vars
Using SSL: openssl OpenSSL 1.0.2t  10 Sep 2019


Please confirm you wish to revoke the certificate with the following subject:

subject=
    commonName                = client2.domain.tld


Type the word 'yes' to continue, or any other input to abort.
  Continue with revocation: yes
Using configuration from /Users/jsmith/EasyRSA-3.0.7/pki/easy-rsa-90112.wfP0sa/tmp.jrFGwa
Revoking Certificate 0DACEEA5C11A6948B3C6E9AC3502CFA5.
Data Base Updated

IMPORTANT!!!

Revocation was successful. You must run gen-crl and upload a CRL to your
infrastructure in order to prevent the revoked cert from being accepted.


# 証明書失効リスト(CRL)を更新
$ ./easyrsa gen-crl

Note: using Easy-RSA configuration from: ./vars
Using SSL: openssl OpenSSL 1.0.2t  10 Sep 2019
Using configuration from /Users/jsmith/EasyRSA-3.0.7/pki/easy-rsa-90205.2q2Aak/tmp.poeCn8

An updated CRL has been created.
CRL file: /Users/jsmith/EasyRSA-3.0.7/pki/crl.pem

失効した証明書は、CAに問い合わせても見つかりません。

# 失効した証明書はCAに問い合わせてもみつからない
$ ./easyrsa show-cert client2.domain.tld
Using SSL: openssl OpenSSL 1.0.2t  10 Sep 2019

Easy-RSA error:

No such cert file with a basename of 'client2.domain.tld' is present.
Expected to find this file at:
/Users/jsmith/EasyRSA-3.0.7/pki/issued/client2.domain.tld.crt


# 失効した証明書はインデックスファイルで「V(alid)」ではなく「R(evoked)」扱い
$ cat pki/index.txt
V       220821064937Z           7172155BBEEEC5CEAE9A8A6CA5B0501B        unknown /CN=server
V       220821070413Z           C069FFDE8575A1C98F109594C442E06D        unknown /CN=client1.domain.tld
R       220821070604Z   200518111534Z   0DACEEA5C11A6948B3C6E9AC3502CFA5        unknown /CN=client2.domain.tld

CRLをクライアントVPNエンドポイントに関連付け

更新したCRLをクライアントVPNエンドポイントに関連付けます。

$ aws ec2 import-client-vpn-client-certificate-revocation-list \
  --certificate-revocation-list file:///Users/jsmith/EasyRSA-3.0.7/pki/crl.pem  \
  --client-vpn-endpoint-id cvpn-endpoint-XXX
{
    "Return": true
}

失効した証明書では接続できなくなっていることを確認してください。

クライアンとサーバーの認証機関が同じ場合、クライアント証明書をACMにアップロードしなくて良い理由

ドキュメント「クライアント認証と認可」には次の記載があります。

クライアント証明書の認証機関 (発行者) がサーバー証明書の認証機関 (発行者) と異なる場合にのみ、クライアント証明書を ACM にアップロードする必要があります。

この理由を考えます。

証明書は認証局が秘密鍵でデジタル署名します。 証明書を受け取ると、証明書を発行したCAのルート証明書(に含まれる公開鍵)を利用して署名を検証します。

相互認証なクライアントVPNエンドポイントの作成時には、ACMにインポートした証明書のARNを指定します。 今回ようなプライベートCAの場合、ACMへのインポート時に指定する証明書チェーンがルート証明書にあたります。

公式ドキュメントのGetting Startedチュートリアルでは、easy-rsaで新規に認証局を作成し、次のコマンドで証明書をインポートしています。

$ aws acm import-certificate \
  --certificate file://client1.domain.tld.crt \ # 証明書
  --private-key file://client1.domain.tld.key \ # プライベートキー
  --certificate-chain file://ca.crt \           # 証明書チェーン:ルート証明書
  --region region

クライアント証明書の検証で必要になるのは、証明書チェーンで指定したルート証明書だけであり、クライアントの

  • 証明書
  • プライベートキー

は利用しません。

そのため、クライアントVPNエンドポイント作成時の[クライアント証明書 ARN] には、ルート証明書が同じサーバー証明書のARNを指定しても動作します。クライアント証明書をACMにアップロードする必要はありません。

冒頭の「クライアント証明書の認証機関 (発行者) がサーバー証明書の認証機関 (発行者) と異なる場合にのみ、クライアント証明書を ACM にアップロードする必要があります。」につながります。

まとめ

OpenVPNのeasy-rsaを利用したAWS Client VPN向けのクライアント証明書の管理方法を紹介しました。

ポイントは、CAのルート証明書がVPNエンドポイントに登録されている限り、CAに閉じた操作でクライアント証明書を発行できることです。

OpenVPN easy-rsaを利用すると、OpenSSLを直接利用するよりもはるかに簡単に証明書を管理できるものの、証明書の発行・失効・期限切れチェック・更新などの操作は、それなりに手間です。 証明書の数が増えると、運用負荷へのインパクトも大きいです。

AWS Client VPNは

  • Active Directory認証
  • 証明書を使用した相互認証(本ブログで紹介)

の2種類のクライアント認証に対応しています。

運用をイメージしながら、適切な認証方式をご採用ください。

参考