APIがあるサイトの認証局が変わって証明書が変更になったんですが、OS側の証明書は更新されたのにAPIをたたいているPythonモジュールのほうがエラーを吐くようになってしまったので、調査対応した備忘録。
結論
PythonのrequestsモジュールはOS標準の証明書を参照しておらず、requestsモジュール内部の証明書を見て外部通信を行う。requestsモジュールはcrtifiモジュールがないとrequestsモジュール内部の証明書をOSの証明書で更新してくれない。requestsモジュールのインストール時、requestsのバージョンが2.16以降はrequestsモジュールインストール時にcertifiモジュールも一緒にインストールしてくれるが、2.16以前では自分でcertifiモジュールをインストールする必要がある。 今回はcertifiを入れられる環境ではなかった(動かしている環境に変更を加えるのが非常に大変即時で対応できない&外部通信のための穴あけでハンコリレーが発生する)ので別の手段として、requestsモジュールが参照する先を指定する方法を実施。環境変数を設定することで明示的にrequestsモジュールの証明書参照先をOSで持っている証明書のファイルパスを指定し対応した。
実行環境
- requests 2.5.3
CentOSとPythonのバージョンについては割愛。。。
対処内容
1. requestsモジュールのバージョンを上げる
requestsモジュールがバージョン 2.16より古ければ、requestsモジュールのアップデートで対応可能。
$ pip install -U requests
今回はこの方式だとハンコリレーが発生しそうだったので諦めました。
2. certifiモジュールを追加する
requests2.4以降の場合はこちらの方法でも対応可能。certifiモジュールがインストールされていければ、インストールを行う。
$ pip install certifi
この方式も1と同様でハンコリレーが発生して対応が遅くなりそうだったので諦めました。
3. 環境変数を設定し、明示的に参照先を指定する。
環境変数REQUESTS_CA_BUNDLE
を指定することでアプリケーションの修正を行わずに証明書の参照先を変えることができます。
一時的には
$ export REQUESTS_CA_BUNDLE=<証明書の配置先>
で対応可能です。が、PythonプログラムをCronで定期起動しているため、Cron呼び出し時に上記環境変数が設定されないといけません。
そんなとき、一番簡単な方法はCrontabの一番上に環境変数をそのまま書くことです。そうするとCrontabが呼び出されたときに最初に環境変数をセットして動いてくれます。
$ crontab -e ----- REQUESTS_CA_BUNDLE=<証明書の配置先> 00 * * * * <実行コマンド>
こんな感じでした。
まとめ
今回動かしているプログラムは別途新しくする予定だったので、新しいプログラムのほうでは最新バージョンのrequestsを使用するため発生しないため、今安定動作しているプログラムに手を入れたくなかったんですよね。環境変数の設定だけで済んだので対応が最小で済んでよかったです。