問題の概要
TLS経由でRedisインスタンスに接続しようとして、次のようなストレスの溜まるトレースバック(エラー)に遭遇したことはありませんか?
ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1123)
このエラーは、PythonアプリケーションがRedisサーバーの身元を検証しようとして失敗したときに発生します。本質的には、クライアントはSSL証明書を確認できていますが、それを署名した認証局(CA)を認識できていない状態です。ローカルストアにあるルート証明書への信頼できるリンクがないため、セキュリティ上の理由から接続が切断されます。
検証が失敗する理由
SSLハンドシェイクが失敗する理由はいくつかあります。一般的な原因の1つは、CAバンドルの欠落です。クライアントがディスク上のどこにルート証明機関(CA)ファイルがあるかを把握していない可能性があります。
内部テストで自己署名証明書を使用している場合、クライアントはデフォルトでそれらを拒否します。AWS ElastiCacheやAzure Cache for Redisのようなマネージドサービスも特定のCAを使用しており、これらが環境のデフォルトのトラストストアに存在しないことがあります。最後に、macOS上のPythonはこの問題でよく知られています。システムのキーチェーンではなく、独自の内部証明書バンドルを使用することが多いためです。
ステップバイステップの修正方法
1. CA証明書を手動で指定する
本番環境において最も信頼できる修正方法は、信頼できるCAファイルをクライアントに明示的に指定することです。これは、amazon-trust-services.pemファイルが必要になることが多いAWS ElastiCacheを使用する場合によく行われます。
import redis
# 例: ダウンロードしたCA証明書へのパス
ca_cert_path = "/etc/ssl/certs/redis-ca.pem"
r = redis.Redis(
host='あなたのRedisクラスターのホスト名',
port=6379,
ssl=True,
ssl_ca_certs=ca_cert_path,
password='あなたのセキュアなパスワード'
)
# 成功した場合、Trueが返されます
print(r.ping())
2. Certifiパッケージを活用する
ほとんどのパブリックCAは、すでにcertifiライブラリによって管理されています。これは、macOSやDockerコンテナ内でのSSL問題を解決するための標準的な方法です。まず、パッケージをインストールします。
pip install certifi
次に、redis-pyにcertifiの証明書ストアを使用するように指示します:
import redis
import certifi
r = redis.Redis(
host='あなたのRedisホスト',
port=6379,
ssl=True,
ssl_ca_certs=certifi.where()
)
print(r.ping())
3. OSレベルの証明書を更新する
根本的な原因がオペレーティングシステムの古さにある場合もあります。ローカルストアが古いと、Let's Encryptなどのプロバイダーからの新しい証明書を認識できません。
Ubuntu/Debianの場合:
sudo apt-get update
sudo apt-get install --reinstall ca-certificates
sudo update-ca-certificates
macOSの場合:
公式インストーラー経由でPython 3.11または3.12をインストールした場合は、付属のコマンドスクリプトを実行して証明書をインストールする必要があります。3.xを実際のバージョンに置き換えてください:
/Applications/Python\ 3.x/Install\ Certificates.command
4. 検証を無効にする(開発環境のみ)
ローカルの開発マシンで作業していますか?その場合は、チェックを完全にバイパスできます。本番環境では絶対に行わないでください。中間者攻撃に対して接続が脆弱になります。
import redis
import ssl
r = redis.Redis(
host='localhost',
port=6379,
ssl=True,
ssl_cert_reqs=None # 検証をスキップ
)
print(r.ping())
接続のテスト
単純なPINGを実行してください。クライアントがPONGを返せば、TLSハンドシェイクは正常に機能しています。それでも問題が解決しない場合は、redis-cliでテストして、問題がPython固有のものかどうかを確認してください:
# redis-cliでテスト
redis-cli -h あなたのホスト -p 6379 --tls --cacert /path/to/ca.pem ping
将来の失敗を防ぐ方法
- 更新の自動化: 毎月
pip install --upgrade certifiを実行して、ルート証明書の情報を最新に保ちます。 - 有効期限の監視: 証明書は多くの場合90日ごとに期限切れになります。アプリが停止する前に更新できるよう、アラートを設定してください。
- 設定の共通化: スクリプト内に文字列をハードコードするのではなく、
REDIS_CA_PATHのような環境変数を使用してください。

