まとめ
Azure SDK for Pythonを使っていてサービスへの接続時に証明書検証エラーSSL: CERTIFICATE_VERIFY_FAILED
が出た時、
雑に回避したかったら各クライアントのコンストラクタにconnection_verify=False
を渡せば良い。
(本番運用するような場合はちゃんとルート証明書を端末のRootCAに追加しよう!怒られても知らないぞ!)
# こんな感じ
blob_service_client = BlobServiceClient.from_connection_string(connect_str, connection_verify=False)
経緯
会社のうんこPC ちょっと特殊な環境で開発しているときにAzure SDK for PythonからAzureのサービスを呼び出したところ証明書エラーが出てしまった。
azure.core.exceptions.ServiceRequestError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1028)
このエラーは実行環境が信頼しているCA証明書でサーバー(Azure)側の証明書検証がしてしまうというもので、
正攻法としては信頼するCA証明書のリストにAzureのルート証明書を追加すれば解決するのだが、PCがうんこすぎてそこらへん全然自由にいじれない
割と急いでたり動作検証で一回だけ通ればよかったりするときにわざわざ証明書をいじるのはけっこうめんどくさい。
全然汚い方法でいいからサクッと解決したくなった(=証明書検証自体をスキップしたい)。
環境
- Python 3.13.2
- azure-storage-blob 12.25.1
- Azure Blob Storageは作成済みとする
手順
キーワード引数を指定
とりあえず使うコードはこんな感じ。
対象とするAzureサービスはなんでもいいが今回はBlob Storageのクライアントを使う。
これを特定の環境で実行したとき、次のようなエラーが出るものとする。
(接続文字列はportalから取得できる)
# 依存関係入れる
$ pip install -r requirements.txt
# ...
# 接続文字列を環境変数に設定
$ export AZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=...;AccountKey=...;EndpointSuffix=core.windows.net
# スクリプト実行、証明書検証エラー発生
$ python main.py
Traceback (most recent call last):
File "/test/main.py", line 14, in <module>
for container in container_list:
^^^^^^^^^^^^^^
File "/usr/local/lib/python3.13/site-packages/azure/core/paging.py", line 123, in __next__
return next(self._page_iterator)
File "/usr/local/lib/python3.13/site-packages/azure/core/paging.py", line 75, in __next__
self._response = self._get_next(self.continuation_token)
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.13/site-packages/azure/storage/blob/_models.py", line 538, in _get_next_cb
return self._command(
~~~~~~~~~~~~~^
marker=continuation_token or None,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
maxresults=self.results_per_page,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cls=return_context_and_deserialized,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
use_location=self.location_mode)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.13/site-packages/azure/core/tracing/decorator.py", line 105, in wrapper_use_tracer
return func(*args, **kwargs)
File "/usr/local/lib/python3.13/site-packages/azure/storage/blob/_generated/operations/_service_operations.py", line 685, in list_containers_segment
pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access
~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_request, stream=_stream, **kwargs
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/usr/local/lib/python3.13/site-packages/azure/core/pipeline/_base.py", line 240, in run
return first_node.send(pipeline_request)
~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.13/site-packages/azure/core/pipeline/_base.py", line 96, in send
response = self.next.send(request)
File "/usr/local/lib/python3.13/site-packages/azure/core/pipeline/_base.py", line 96, in send
response = self.next.send(request)
File "/usr/local/lib/python3.13/site-packages/azure/core/pipeline/_base.py", line 96, in send
response = self.next.send(request)
[Previous line repeated 2 more times]
File "/usr/local/lib/python3.13/site-packages/azure/core/pipeline/policies/_redirect.py", line 204, in send
response = self.next.send(request)
File "/usr/local/lib/python3.13/site-packages/azure/core/pipeline/_base.py", line 96, in send
response = self.next.send(request)
File "/usr/local/lib/python3.13/site-packages/azure/storage/blob/_shared/policies.py", line 555, in send
raise err
File "/usr/local/lib/python3.13/site-packages/azure/storage/blob/_shared/policies.py", line 527, in send
response = self.next.send(request)
File "/usr/local/lib/python3.13/site-packages/azure/core/pipeline/_base.py", line 96, in send
response = self.next.send(request)
File "/usr/local/lib/python3.13/site-packages/azure/core/pipeline/_base.py", line 96, in send
response = self.next.send(request)
File "/usr/local/lib/python3.13/site-packages/azure/core/pipeline/_base.py", line 96, in send
response = self.next.send(request)
[Previous line repeated 1 more time]
File "/usr/local/lib/python3.13/site-packages/azure/storage/blob/_shared/policies.py", line 301, in send
response = self.next.send(request)
File "/usr/local/lib/python3.13/site-packages/azure/core/pipeline/_base.py", line 96, in send
response = self.next.send(request)
File "/usr/local/lib/python3.13/site-packages/azure/core/pipeline/_base.py", line 96, in send
response = self.next.send(request)
File "/usr/local/lib/python3.13/site-packages/azure/core/pipeline/_base.py", line 128, in send
self._sender.send(request.http_request, **request.context.options),
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.13/site-packages/azure/core/pipeline/transport/_requests_basic.py", line 409, in send
raise error
azure.core.exceptions.ServiceRequestError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1028)
ここで証明書検証をスキップするようにしたいが、
使ってるクライアントにそれっぽいオプションがなさそう…詰んだ…
と思ったが、ちょっとエラーを調べるとこのクライアントが内部で呼び出してるクラスとそのパラメータみたいなのが出てきた。
- https://github.com/Azure/azure-sdk-for-python/blob/21ba181f927de0a6580ad75a068dd9c5045420e9/sdk/storage/azure-storage-blob/azure/storage/blob/_blob_service_client.py#L54
- https://github.com/Azure/azure-sdk-for-python/blob/21ba181f927de0a6580ad75a068dd9c5045420e9/sdk/core/azure-core/azure/core/pipeline/transport/_requests_basic.py#L259
- https://learn.microsoft.com/ja-jp/python/api/azure-core/azure.core.configuration.connectionconfiguration?view=azure-python
(BlobServiceClientがHTTPリクエストを送るメソッドの内部でConnectionConfigurationを呼び出していて、このclassにはconnection_verifyといういかにも証明書検証の有無を司ってそうなパラメータがある)
試しにキーワード引数(kwargs)をクライアントのコンストラクタに渡してみよう…
コードを書き換えて再度実行したところ、
「ちゃんと証明書検証したほうがいいぞ」的な警告は出るものの今度はエラーにならずちゃんと呼び出しが成功した(コンテナ名の一覧が表示できた)。
$ python main.py
/usr/local/lib/python3.13/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host 'stuzimihsrexample.blob.core.windows.net'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
warnings.warn(
example-container-1
example-container-2
azure.core.configuration.ConnectionConfigurationは他のAzureサービスのクライアントでも通信時に使われているようなので、
同様に各クライアントのコンストラクタ引数にconnection_verify=False
を渡せば証明書検証を回避できるはず。
やったぜ。
(証明書エラーの再現手順)
普通の環境で実行しても証明書エラーに遭うことはなかなかないので、
今回は問題を再現するため以下の手順で信頼するCA証明書が欠けているような状態を作った。
雑にDockerでPythonコンテナを立ち上げ、RootCAのリストを取得する。
$ # 作業ディレクトリ
$ pwd
/path/to/tempdir
$ ls -1 test
main.py
requirements.txt
$ # testディレクトリをマウントしてPythonコンテナを立て、そのままbashで入る
$ docker container run --rm -it -v /path/to/tempdir/test:/test python:3 /bin/bash
root@f8a544bd9c26:/#
root@f8a544bd9c26:/# # 依存関係入れる
root@f8a544bd9c26:/# cd test
root@f8a544bd9c26:/test# pip install -r requirements.txt
# ...
root@f8a544bd9c26:/test# # RootCAの場所を確認
root@f8a544bd9c26:/test# python
Python 3.13.2 (main, Mar 18 2025, 22:43:33) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import certifi
>>> certifi.where()
'/usr/local/lib/python3.13/site-packages/certifi/cacert.pem'
>>> exit
root@f8a544bd9c26:/test# # RootCAをコピーしてホストOS側で編集する
root@f8a544bd9c26:/test# cp /usr/local/lib/python3.13/site-packages/certifi/cacert.pem ./cacert.pem
拾ってきたRootCAをホストOS側から任意のエディタで開き、# Issuer: CN=DigiCert Global Root G2
の部分のpem(画像では1740~1768行目)をごっそり削除する。
(Blob StorageのRootCAがDigiCert Global Root G2
であることはブラウザで確認した。)
これで信頼されたRootCAのリストからBlob StorageのRootCAが消えたので、
これをPythonが参照するように上書きする。
root@f8a544bd9c26:/test# # 編集後のcacert.pemで上書き
root@f8a544bd9c26:/test# cp ./cacert.pem /usr/local/lib/python3.13/site-packages/certifi/cacert.pem
あとはAzure portalから接続文字列をコピーしてきて環境変数に張り付けて、
root@f8a544bd9c26:/test# # 接続文字列を環境変数に設定
root@f8a544bd9c26:/test# export AZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=...;AccountKey=...;EndpointSuffix=core.windows.net
コードを実行したらちゃんとエラーになった。
root@f8a544bd9c26:/test# python main.py
Traceback (most recent call last):
File "/test/main.py", line 14, in <module>
for container in container_list:
^^^^^^^^^^^^^^
#...
azure.core.exceptions.ServiceRequestError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1028)
おわり
うんこPCで開発すんのマジしんどい、営業マンと同じ構成のポンコツ制限PCをエンジニアに使わせるんじゃないよ
超久しぶりにPython触ったら色々わかんなすぎて泣いてしまった。
やっぱりGoがいいなあ…