ALB の相互認証機能を試してみる 「トラストストアで検証」編

記事タイトルとURLをコピーする

こんにちは😺 カスタマーサクセス部の山本です。

ALB の相互認証機能を試してみました。

公式資料

公式ドキュメント:
Mutual authentication with TLS in Application Load Balancer - Elastic Load Balancing

公式ブログ:
Application Load Balancer の相互認証により、証明書ベースのクライアント ID が確実に検証されます | Amazon Web Services ブログ

S2N:
Introducing s2n-tls, a New Open Source TLS Implementation | AWS Security Blog

OpenSSL Req:
/docs/man3.0/man1/openssl-req.html

OpenSSL x509v3_config:
/docs/manmaster/man5/x509v3_config.html

ALB の相互認証機能の概要

クライアント証明書をインストールした PC のみから、ALB 配下に配置しているアプリケーションへの接続を許可できます。
これが、今までのサーバー証明書のみでの認証との違いです。
サーバー証明書はサーバーの身元情報をクライアントに提示して証明します。一方、クライアント証明書はクライアントの身元情報をサーバーに提示して証明します。
サーバー証明書に加えて、クライアント証明書を使った認証をすることにより、サーバー・クライアントの双方で信頼性を確認できます。

相互認証を行うには、認証局(CA)から発行されたルート証明書(または中間証明書)がサーバ側に必要です
ルート証明書は、クライアントから提示されたクライアント証明書の署名を検証するために使用します。
サーバーはルート証明書の公開鍵を使って、クライアント証明書のデジタル署名を確認し、証明書が信頼しているCAによって発行されたものであること、改ざんされていないことを保証します。

ALB で相互認証を行う際には、以下の 2 方式があります。

  1. 「トラストストアで検証」:ルート証明書を ALB のトラストストアに配置して、ALB がクライアント証明書の署名を検証する方式(=ALBで署名を検証)
  2. 「パススルー」:ALB がクライアント証明書の署名を HTTP ヘッダに付与して、EC2 など ALB 配下の リソースに送信し検証する方式(=ALB配下で署名を検証)

クライアント証明書の署名の検証を ALB で行うのか? ALB 配下のリソースで行うのか?を選べるということです。
ALB で行う場合は、アプリケーション側での実装が不要で、簡易的に実施できます。
EC2 など ALB 配下の リソースで行う場合は、アプリケーション側で HTTP ヘッダを解釈して、署名を検証する実装が必要です。

また、証明書の内容に基づいてユーザーに権限を付与するといった実装ができます。
クライアント証明書は、ユーザーの識別情報(例:ユーザー名、メールアドレスなど)、組織情報、役職情報などを含むことができます。これを使って、認証後に特定のアクセス権限をユーザーに付与することができます。 X-Amzn-Mtls- で始まるHTTPヘッダに、クライアント証明書の情報が含まれます。これをバックエンドのEC2などで利用して、クライアント毎に権限を付与することが可能です。

参考:Mutual authentication with TLS in Application Load Balancer - Elastic Load Balancing

本記事では、1 「トラストストアで検証」方式を試してみました。
認証局となる EC2 を作成し、openssl でルート証明書とクライアント証明書を作成しました。サーバー証明書は ACM で発行したものをもともと使用していて、そのまま使用しています。サーバー証明書は特に変更等は不要です。

制約

大きな制約としては以下がありそうです。
他の制約については以下のドキュメントも参照ください。

Mutual authentication with TLS in Application Load Balancer - Elastic Load Balancing

  • サポートされている証明書: X.509v3
  • サポートされている公開キー: RSA 2K – 8K または ECDSA secp256r1、secp384r1、secp521r1
  • サポートされる署名アルゴリズム: RSA/SHA を使用した SHA256、384、512 EC/SHA を使用した SHA256、384、512 MGF1 を使用した RSASSA-PSS を使用した 256,384,512 ハッシュ

試してみた

認証局となるEC2上で、ルート証明書を作成

Amazon Linux 2023 の 2024 年 1/31 版を使用しました。

OpenSSL のバージョンは 3.0.8 でした。

ルート証明書を作成するための 設定ファイルを作成します。

  • root_openssl.cnf
[ req ]
default_bits = 2048
default_keyfile = rootCA.key
distinguished_name = req_distinguished_name
x509_extensions = v3_ca 

[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = JP
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Yamanashi
localityName = Locality Name (eg, city)
localityName_default = Tsuru
organizationName = Organization Name (eg, company)
organizationName_default = shika-darake
organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = tech
commonName = Common Name (eg, YOUR name)
commonName_default = sample

[ v3_ca ]
basicConstraints = critical, CA:TRUE
subjectKeyIdentifier = hash
keyUsage = digitalSignature, keyCertSign, cRLSign

秘密鍵とルート証明書を作成します。

openssl genrsa -out rootCA.key 2048 # 秘密鍵作成
openssl req -x509 -new -nodes \
-key rootCA.key \
-sha256 \
-days 1024 \
-out rootCA.pem \
-config root_openssl.cnf

ルート証明書 ( rootCA.pem ) と秘密鍵 ( rootCA.key ) ができます。

参考:ルート証明書 ( rootCA.pem )の内容
openssl x509 -text -noout -in rootCA.pem で確認

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            2c:8e:00:78:7d:35:08:04:25:87:eb:c6:dd:1b:ee:7e:62:d9:2f:3e
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = JP, ST = Yamanashi, L = Tsuru, O = shika-darake, OU = tech, CN = sample
        Validity
            Not Before: Feb  8 11:32:20 2024 GMT
            Not After : Nov 28 11:32:20 2026 GMT
        Subject: C = JP, ST = Yamanashi, L = Tsuru, O = shika-darake, OU = tech, CN = sample
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:b0:70:65:e1:71:2f:f7:a1:23:29:3c:89:c7:e0:
                    56:15:cb:ab:f2:03:23:7a:9b:c0:35:b1:63:7b:fa:
                    6f:82:ef:8f:23:12:85:d1:1a:b5:f2:e7:4a:f1:c3:
                    96:12:3f:ad:e7:31:76:1c:1d:ea:ce:6e:00:b8:92:
                    0f:4c:5d:fd:26:c9:b4:09:93:f7:f4:d4:35:65:be:
                    e0:cf:d8:bb:63:e1:dd:88:2e:12:11:b5:6e:fd:f7:
                    91:94:ed:63:ca:61:2c:3c:61:4e:98:97:f9:1c:f7:
                    99:61:e1:e9:35:23:4f:a9:56:31:70:12:e6:47:26:
                    4c:9e:8c:f3:bf:dd:93:23:ff:0a:2e:d2:80:fd:8c:
                    b5:6a:42:41:97:92:b0:5a:9d:ff:8b:42:e2:f0:67:
                    f3:e6:73:59:6c:a6:c8:13:47:1f:dc:6a:50:3a:13:
                    f8:ce:b5:11:79:72:df:de:aa:33:6f:39:68:3d:9d:
                    c2:bd:bf:30:9d:2b:6d:82:78:dd:45:9c:cf:08:fa:
                    28:c3:dc:63:a9:13:12:3e:4c:cd:f6:1b:30:6f:ae:
                    79:2f:81:da:17:f6:b9:85:64:ae:5e:c4:cb:6d:61:
                    ff:66:25:07:18:c4:59:61:17:ea:7f:d2:a5:49:06:
                    f0:ef:73:a2:0d:92:de:c6:e0:f8:45:a7:2f:9f:11:
                    93:bf
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Subject Key Identifier: 
                90:CF:C9:77:26:86:47:71:4E:D6:3A:56:31:C0:C3:9C:36:FC:DF:0E
            X509v3 Key Usage: 
                Digital Signature, Certificate Sign, CRL Sign
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        25:7c:e6:33:8b:77:36:7a:02:2a:e2:4c:f6:c9:eb:bb:82:78:
        65:9a:a3:26:da:7d:39:ef:66:31:4b:d1:dd:f6:57:d6:a6:93:
        87:81:88:be:91:44:a2:d3:ef:07:8b:12:e8:b2:d8:8d:39:4a:
        33:35:1f:69:fa:2c:97:96:68:d9:7c:04:4a:cb:1e:33:59:1a:
        9d:60:79:44:e1:4b:5f:cf:05:ac:bf:77:ba:3f:20:ee:1b:8f:
        21:f3:cc:c5:aa:5f:26:ac:9c:95:f1:34:12:33:f3:54:9c:f2:
        45:70:63:c7:06:04:f5:ed:1f:82:3b:c6:75:ce:f6:85:d2:d9:
        36:a5:26:20:37:87:bd:cf:95:9e:1d:b8:84:f4:4b:8a:2e:23:
        8a:a9:96:02:3d:fe:8f:e3:e1:12:96:0d:c8:aa:4b:34:aa:66:
        4b:7b:02:2a:ef:61:3e:05:d6:49:1d:76:e9:aa:21:94:cb:41:
        bb:c3:3a:bb:da:96:23:52:4b:2b:6a:66:da:5c:39:93:3c:57:
        0b:7c:a9:90:b1:15:32:e4:34:bf:6e:92:d6:1f:e5:08:89:db:
        43:51:f4:91:c3:2b:57:a2:b9:f5:1b:fc:e5:42:c6:f8:f0:91:
        15:41:03:a4:88:19:0d:c2:50:d2:74:96:e7:72:70:3f:ba:d7:
        e6:b1:35:1a

クライアント上で、証明書署名要求(CSR)を作成

クライアントは Apple M2 の MacBook Pro ( Sonoma 14.2.1 ) とします。
クライアント上で、証明書署名要求(CSR)を作成します。
サブジェクトの CN を sample-2 にしています。実際はクライアント証明書を識別する情報(ユーザー名、メールアドレス、所属組織など)にすることが多いです。

openssl req -new \
-newkey rsa:2048 \
-nodes -keyout clientkey.pem \
-out client.csr \
-subj "/C=JP/ST=Yamanashi/L=Tsuru/O=shika-darake/OU=tech/CN=sample-2"

証明書署名要求(CSR)ファイル ( client.csr ) と秘密鍵 ( clientkey.pem ) ができています。

作成した証明書署名要求(CSR)ファイル ( client.csr ) を認証局となる EC2 にコピーします。

認証局となる EC2 上で、クライアント証明書の発行

クライアントで作成した 証明書署名要求(CSR)ファイル ( client.csr ) を同一ディレクトリに配置ください。
クライアント証明書用に x509v3 の設定ファイルを作成します。

  • client_ext.cnf
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
authorityKeyIdentifier = keyid

クライアント証明書を作成します。

openssl x509 -req -in client.csr \
-CA rootCA.pem \
-CAkey rootCA.key \
-CAcreateserial -out clientcert.pem \
-days 365 \
-extfile client_ext.cnf -extensions v3_req
  • クライアント証明書 ( clientcert.pem ) ができました。

参考:クライアント証明書 ( clientcert.pem )の内容
openssl x509 -text -noout -in clientcert.pem で確認

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            1a:82:63:9c:47:b8:f7:6e:71:55:44:6d:bb:5a:c3:24:a5:82:f5:d5
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = JP, ST = Yamanashi, L = Tsuru, O = shika-darake, OU = tech, CN = sample
        Validity
            Not Before: Feb  8 11:34:54 2024 GMT
            Not After : Feb  7 11:34:54 2025 GMT
        Subject: C = JP, ST = Yamanashi, L = Tsuru, O = shika-darake, OU = tech, CN = sample-2
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:c4:a9:32:c9:b3:4a:97:a6:9b:e2:55:a6:a9:b3:
                    96:3f:3c:cf:00:4d:12:a6:c4:25:7f:1d:dc:ac:bd:
                    02:6e:fd:fa:87:67:61:41:72:f5:30:45:5e:e7:a9:
                    f7:40:4c:ac:90:94:c7:3a:aa:53:a3:8c:29:83:79:
                    3e:1d:69:12:21:90:bb:c8:1a:64:52:19:33:5a:e3:
                    5c:83:77:d2:f0:5c:2a:66:61:2a:b9:7c:30:ab:ba:
                    74:59:92:07:29:42:7d:db:ef:f6:a5:e0:eb:64:56:
                    de:b8:2b:5c:c8:8c:10:ea:47:5d:89:6a:08:b1:7c:
                    02:f3:cb:23:48:9f:fc:a6:07:a2:da:21:bd:5c:df:
                    48:52:46:14:0a:69:c6:8f:57:6a:a9:1f:c9:7b:8f:
                    50:cc:80:76:c9:b2:98:08:3e:5e:74:6d:e1:95:d9:
                    6c:9c:15:df:96:1f:bd:24:f0:ee:27:1c:92:38:b0:
                    7b:a4:2e:48:2a:19:3a:eb:99:07:61:f6:71:db:f1:
                    2e:c1:33:50:02:db:18:cd:30:e7:e0:09:68:a7:d0:
                    2c:22:dc:27:f8:3d:2f:bd:e4:12:e2:d3:a3:2e:a9:
                    e0:2c:4e:02:9f:d6:0c:6f:13:ad:e0:2a:22:e0:13:
                    26:03:a6:26:b4:70:c0:ab:b2:a9:31:16:60:44:ab:
                    ec:6d
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:FALSE
            X509v3 Key Usage: 
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Authority Key Identifier: 
                90:CF:C9:77:26:86:47:71:4E:D6:3A:56:31:C0:C3:9C:36:FC:DF:0E
            X509v3 Subject Key Identifier: 
                DB:AF:F7:8A:4B:CD:B7:A5:97:56:6F:5A:5E:4F:CE:18:B1:DE:91:74
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        52:2c:e0:23:77:d8:2a:04:0a:a2:c7:bf:f1:9a:b9:ca:be:52:
        8b:0b:e3:fb:3c:ed:40:94:6e:cc:b6:bd:41:cb:77:1b:72:1f:
        9d:ba:e1:3b:91:71:e3:74:c0:87:29:94:87:00:64:bf:04:0e:
        9e:34:d2:89:d7:f5:c6:1b:94:f6:47:c6:80:92:b1:93:09:a3:
        ce:85:5b:48:a1:b4:fd:6e:79:ce:ab:62:36:19:22:36:98:1c:
        29:ad:aa:66:df:0d:06:7b:5b:80:1b:72:a0:b5:f1:df:ec:ed:
        1d:da:7f:bc:34:26:46:82:51:b6:b2:99:6b:cc:1a:47:73:b1:
        45:ad:2a:cd:ba:ee:ae:02:1e:38:e3:83:bd:1a:f4:48:ac:03:
        e4:4c:96:5f:26:15:d1:05:e9:3c:6d:a7:78:ce:00:a2:ea:ec:
        4a:0f:74:09:d7:00:6f:0b:b7:09:51:77:37:ac:df:19:be:10:
        6e:ef:cd:bb:f4:cf:38:03:63:6d:9b:09:61:72:97:ca:b6:48:
        85:d8:05:c3:f2:fa:13:5a:e8:7d:85:93:8b:96:6b:af:71:03:
        37:dd:ea:41:8a:18:fc:97:78:fa:da:40:1a:bb:43:b1:40:a0:
        1b:ec:3e:68:3c:d5:2d:43:b4:29:7f:29:df:d1:21:9e:7c:11:
        97:ee:fe:d4

クライアントPC にクライアント証明書を登録するため、ファイルの中身(テキスト)をコピーして、クライアントに保存しておきます。

S3 にルート証明書 ( rootCA.pem ) をアップロード

S3 に認証局となるEC2上で作成したルート証明書 ( rootCA.pem ) をアップロードします。

ALB にルート証明書 ( rootCA.pem ) を設定する

ALB のリスナールールにチェックを入れ、「アクション」のプルダウンメニューから「編集」を押します。

セキュアリスナーの設定」内にある「クライアント証明書の処理」を編集していきます。
相互認証 (mTLS)」にチェックを入れます。 「トラストストアで検証」を選びます。

期限切れのクライアント証明書を許可しない - 推奨」のままにします。

新しいトラストストア」を選択し、S3 にアップロードしたルート証明書 ( rootCA.pem ) を選択します。

変更の保存」を押します。

リスナーが正常に変更されました。」と出ることを確認します。

動作確認

Mac OS 上の Google Chrome で確認してみる

クライアント上にあるクライアント証明書 ( clientcert.pem ) を、キーチェーンアクセスの「証明書」タブの中にドラッグアンドドロップし、登録します。

インポートした証明書をダブルクリックし、「この証明書を使用するとき」を「常に信頼」にします。「x」で閉じます。

インポートした証明書を右クリックし、「新規証明書プリファレンス」をクリックします。

対象の URL を入れて「追加」をクリックします。

以下のコマンドで、秘密鍵を PKCS#12形式に変換します。

openssl pkcs12 -export -out certificate.pfx -inkey clientkey.pem -in clientcert.pem -legacy

PKCS#12形式に変換した秘密鍵をキーチェーンアクセスにインポートします。
ファイルをダブルクリックすると、キーチェーンアクセスに登録できます。

ブラウザで WEB サイトに接続します。ポップアップが出るので、インポートした証明書を選択します。

OSにログインする際のユーザー名とパスワードを入れます。

接続できました。

まとめ

ALB の相互認証機能を試してみました。本機能を利用することで、アプリケーションコードの実装をしなくても、クライアント証明書を使った認証を実現できるようになります。
本記事ではクライアント証明書を実際のクライアントである MacBook Pro 上で作成しました。
Windows 端末でも openssl は使用可能なので、同様のことが可能と思います。
また、クライアント端末で作らずに、サーバー上で作成したクライアント証明書を配布するケースも多いと思います。
Windows 環境では Active Directory のグループポリシー機能を利用して配布する、といった方法があります。
クライアント証明書の作成や、セキュアな配布方法などの運用課題は検討しつつ、本機能を使って手軽に相互認証を導入してみてはいかがでしょうか。

失効リスト機能などを試せていないので、時間があれば後続記事を書こうと思います!

続きのブログです。CRL リストを試しました。
blog.serverworks.co.jp

余談

関東でも雪が降ったので、伊豆の山を歩いてきました。

山本 哲也 (記事一覧)

カスタマーサクセス部のエンジニア(一応)

好きなサービス:ECS、ALB

趣味:トレラン、登山(たまに)