はじめに
社内ネットワークからAWS CLIを使っていたところ、ある日突然 aws cloudfront コマンドだけがSSLエラーで失敗するようになりました。不思議なことに、同じプロファイル・同じ端末で実行する aws s3 コマンドは問題なく成功します。
「同じAWS CLIなのに、なぜコマンドによってSSLエラーが出たり出なかったりするのか?」
この記事では、その原因を特定するまでの調査方法と解決方法を紹介します。同じような状況(社内プロキシ環境でAWS CLIの一部コマンドだけSSLエラーになる)でハマっている方の参考になれば幸いです。
問題
発生した事象は以下の通りです。
# CloudFrontコマンド → SSLエラー
PS> aws cloudfront list-distributions --profile my-profile
SSL validation failed for https://cloudfront.amazonaws.com/2020-05-31/distribution
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed:
unable to get local issuer certificate (_ssl.c:1028)
# S3コマンド → 正常
PS> aws s3 ls --profile my-profile
2026-04-29 17:05:17 my-bucket-1
2026-04-29 10:34:44 my-bucket-2
- 同じPowerShellセッション(=同じ環境変数)で実行している
- 同じプロファイル・同じ認証情報を使っている
- 先月までは両方とも問題なく実行できていた
なお、--ca-bundle オプションで社内CA証明書を指定すると成功することは確認できていました。
# これは成功する
PS> aws cloudfront list-distributions --profile my-profile --ca-bundle C:\corp-ca.crt
つまり「経路上で証明書が社内CAのものに差し替えられている(SSLインスペクション)」ことは推測できたのですが、それならなぜS3コマンドはエラーにならないのか? が謎でした。
エラーの意味
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed:
unable to get local issuer certificate
このエラーは、AWS CLIが受け取ったサーバー証明書の発行元(Issuer)を信頼済みリストの中から見つけられなかったという意味です。
ポイントは、AWS CLI(Python製)はWindowsのOS証明書ストアを参照せず、Pythonの certifi パッケージが持つ独自の証明書バンドルだけを見るという点です。Windowsには社内CA証明書がグループポリシー等で登録済みでも、AWS CLIはそれを知らないため、社内CAが発行した(=プロキシが差し替えた)証明書を検証できずエラーになります。
解決方法
調査で分かったこと(結論)
先に結論を書くと、原因はプロキシがドメインごとにSSLインスペクションの対象/除外を振り分けていたことでした。
| 通信先 | プロキシでの扱い | 証明書の発行元 | AWS CLIの結果 |
|---|---|---|---|
s3.ap-northeast-1.amazonaws.com |
検査せず素通し(バイパス) | Amazon純正 | 正常 |
cloudfront.amazonaws.com |
検査(証明書差し替え) | 社内プロキシ製品のCA | SSLエラー |
S3のような大量データが流れる通信は、パフォーマンス上の理由でインスペクション除外にする運用は企業ネットワークではよくあるパターンのようです。「先月まで問題なかった」のは、プロキシ側の検査対象設定が変更され、CloudFront向け通信が新たに検査対象に追加されたためと考えられます。
調査方法
原因特定の決め手になったのは、**「AWS CLIと同じプロキシ経由で、実際に届く証明書の発行元(Issuer)を確認する」**ことでした。順を追って説明します。
Step 1: ブラウザでの確認は当てにならないことを知る
最初はブラウザで https://cloudfront.amazonaws.com を開き、鍵アイコンから証明書の発行元を確認しました。しかしこの方法には落とし穴があります。
ブラウザはWindowsのシステムプロキシ設定(PACファイル等)に従い、AWS CLIは環境変数 HTTPS_PROXY に従います。 この2つが別のプロキシ(または別のポリシー)を指している場合、ブラウザで見えた証明書とAWS CLIが受け取る証明書は別物になります。
実際、私の環境ではブラウザ経由だとS3もCloudFrontも両方証明書が差し替えられて見えたため、「S3だけ成功する」事実と矛盾してしまい、調査が混乱しました。
Step 2: AWS CLIと同じ経路で証明書を確認する
そこで、AWS CLIと同じ環境変数のプロキシを明示指定して、curl.exe(Windows 10以降に標準搭載)で証明書チェーンを表示させます。
# S3エンドポイントの証明書発行元を確認
curl.exe -s --proxy $env:HTTPS_PROXY https://s3.ap-northeast-1.amazonaws.com -o NUL -w "%{certs}" | Select-String "Issuer"
結果:
Issuer:C=US, O=Amazon, CN=Amazon RSA 2048 M04
Issuer:C=US, O=Amazon, CN=Amazon Root CA 1
Issuer:C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., ...
S3向けはAmazon純正の証明書チェーンがそのまま届いています。 つまりプロキシはS3向け通信を検査していません。これが aws s3 コマンドが成功する理由です。
Step 3: CloudFrontも同様に確認する(失効チェックの罠に注意)
同じコマンドをCloudFrontに対して実行すると、今度は別のエラーが出ました。
curl.exe -v --proxy $env:HTTPS_PROXY https://cloudfront.amazonaws.com -o NUL -w "%{certs}"
curl: (35) schannel: next InitializeSecurityContext failed:
CRYPT_E_NO_REVOCATION_CHECK (0x80092012) - 失効の関数は証明書の失効を確認できませんでした。
これは、Windows版curlが使うschannel(Windows標準のTLS機能)が既定で行う**証明書の失効確認(CRL/OCSP)**に失敗したエラーです。社内CAが発行する証明書は失効情報の公開先が用意されていないことが多く、この確認が失敗します。S3では起きずCloudFrontでだけ起きたこと自体が、「CloudFront向けは証明書が差し替えられている」傍証になります。
失効確認をスキップする --ssl-no-revoke を付けて再実行します。
curl.exe -s --ssl-no-revoke --proxy $env:HTTPS_PROXY https://cloudfront.amazonaws.com -o NUL -w "%{certs}" | Select-String "Issuer"
結果:
Issuer:C=JP, ST=Tokyo, O=Example Filter Inc., OU=Example Filter Inc., CN=Example Filter Inc. CA
発行元が社内で使われているプロキシ製品ベンダーのCAになっていました。これで「プロキシはS3を素通しし、CloudFrontは証明書を差し替えている」ことが確定です。
調査方法まとめ
- ブラウザやPowerShellの
HttpWebRequestはシステムプロキシ設定に従うため、AWS CLIの通信経路の確認には使えない(別経路を見てしまう可能性がある) -
curl.exe --proxy $env:HTTPS_PROXYでAWS CLIと同じ経路を再現し、-w "%{certs}"で証明書のIssuerを確認するのが確実 - 社内CA発行の証明書は失効確認に失敗しがちなので、curlには
--ssl-no-revokeを付ける - Issuerが
Amazon系なら素通し、社内CA名ならインスペクション対象と判定できる
恒久対応
原因が分かれば対応はシンプルです。社内CA証明書を含む証明書バンドルをプロファイルに設定します。
aws configure set ca_bundle C:\corp-ca.crt --profile my-profile
これで --ca-bundle を毎回付けなくても、このプロファイルでのすべてのAWSコマンドが正常に動作します。
注意点として、指定する証明書ファイルには社内CA証明書とAmazon系ルート証明書の両方が含まれている必要があります。CloudFront(差し替えあり)とS3(Amazon純正)の両方の検証に使われるためです。設定後は両方のコマンドが成功することを確認しましょう。
また、環境変数で全プロファイル一括で設定する方法もあります。
# 恒久設定する場合はシステム環境変数に登録
$env:AWS_CA_BUNDLE = "C:\corp-ca.crt"
おわりに
「同じCLIなのにコマンドによってSSLエラーの有無が違う」という一見不可解な事象でしたが、正体はプロキシのドメイン別インスペクションポリシーというネットワーク側の設定でした。
今回の学びをまとめると:
- AWS CLIはOSの証明書ストアもシステムプロキシ設定も見ない(certifiと環境変数を見る)
- 証明書まわりの調査は「どの経路を通った通信か」を揃えないと誤った結論に至る
-
curl.exe -w "%{certs}"はプロキシ環境の証明書調査に非常に便利
同種の事象は「先月まで動いていたのに急に壊れた」という形で突然やってくるので、プロキシのインスペクション対象が変わった可能性を疑う、という引き出しを持っておくと調査が早くなると思います。
参考
- AWS CLIの証明書バンドル設定(ca_bundle)- AWS公式ドキュメント
- curl - Write out variables(%{certs})
- CRYPT_E_NO_REVOCATION_CHECK について(Microsoft Learn)
JISOUのメンバー募集中!
プログラミングコーチングJISOUでは、新たなメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか?
興味のある方は、ぜひホームページをのぞいてみてください!
▼▼▼
https://projisou.jp
