AnsibleのuriおよびGet_urlモジュールで発生する「SSL: CERTIFICATE_VERIFY_FAILED」を修正する

intermediate🔧 Ansible2026-07-01| Ubuntu 20.04/22.04、RHEL/CentOS 8/9、Ansible 2.9〜2.17、Python 3.8以上

Error Message

Status code was -1 and not [200]: Request failed: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:1007)>
#ansible#ssl#uri#get_url

状況

プレイブックで get_urluri を使ってファイルをダウンロードしたり、内部APIをポーリングしたりしています。一見問題なさそうに見えますが、ターミナルに次のエラーが表示されます:

FAILED! => {
    "msg": "Status code was -1 and not [200]: Request failed: "
}

ステータスコード -1 は接続が確立できなかったことを意味します。よくある原因:

  • 自己署名証明書を使用している内部サーバー
  • 企業のSSLインスペクションプロキシ(Zscaler、Forcepoint、SSL bumpを使ったSquid)
  • PythonがCAとして認識していない内部CAで署名された証明書を使うステージング/開発環境
  • 誰も気づかなかった期限切れのサーバー証明書 — Ansibleが検出するまで

Python/Ansibleが証明書を拒否する理由

Ansibleの uri および get_url モジュールは内部でPythonの urllib を使用しています。システムのCAバンドル、またはインストールされていれば certifi パッケージのバンドルに対してSSL証明書を検証します。サーバーの証明書がそのバンドル内のCAによって署名されていない場合、Pythonは接続を切断してステータス -1 を返します。プロンプトも警告も出さず、ただ強制終了されます。

ブラウザはこれを別の方法で処理します — 独自のトラストストアを持ち、クリックスルーの警告を表示します。Pythonにはそのような選択肢がありません。

応急処置 — 検証を無効化する(注意して使用)

管理された内部ネットワーク上で今すぐ動かす必要がありますか? validate_certs: false を追加してください:

- name: Download file from internal server
  get_url:
    url: "https://internal.example.com/files/package.tar.gz"
    dest: /tmp/package.tar.gz
    validate_certs: false
- name: Hit internal API
  uri:
    url: "https://internal-api.example.com/health"
    method: GET
    validate_certs: false
  register: api_response

**これはSSL検証を完全に無効化します。**ファイアウォール内の内部ツールには許容できます。インターネットや機密データに関わるものには絶対に使用しないでください — MITM攻撃に対して無防備になります。

恒久的な解決策 — CA証明書を追加する

正しいアプローチは、内部CAをPythonに信頼させることです。セットアップに応じて3つの選択肢があります。

オプション1:AnsibleにCAバンドルを指定する

ca_path パラメーターを使用すると、タスクごとに信頼する証明書を正確に指定できます:

- name: Download with custom CA
  get_url:
    url: "https://internal.example.com/files/package.tar.gz"
    dest: /tmp/package.tar.gz
    ca_path: /etc/ssl/certs/internal-ca.crt

- name: URI with custom CA
  uri:
    url: "https://internal-api.example.com/status"
    ca_path: /etc/ssl/certs/internal-ca.crt

CA証明書はインフラまたはセキュリティチームから入手してください。自己署名サーバー証明書の場合は、コントロールノードから直接取得します:

# Grab the cert from the server
openssl s_client -connect internal.example.com:443 -showcerts </dev/null 2>/dev/null | \
  openssl x509 -outform PEM > /etc/ssl/certs/internal-ca.crt

オプション2:CAをシステムトラストストアに追加する

一度追加すれば、Ansible、curl、Pythonスクリプトなど — すべてが自動的に認識します。繰り返し使用する場合に最適な選択肢です。

Ubuntu/Debianの場合:

sudo cp internal-ca.crt /usr/local/share/ca-certificates/internal-ca.crt
sudo update-ca-certificates

RHEL/CentOS/Rockyの場合:

sudo cp internal-ca.crt /etc/pki/ca-trust/source/anchors/internal-ca.crt
sudo update-ca-trust

完了したら、タスクから validate_certs: falseca_path を削除してください — システムトラストストアが処理を引き継ぎます。

オプション3:環境変数でCAバンドルを設定する

root権限がない場合やコンテナで実行している場合は、プレイブックの environment ブロックに REQUESTS_CA_BUNDLE を設定します:

- name: Download with CA via env var
  get_url:
    url: "https://internal.example.com/files/package.tar.gz"
    dest: /tmp/package.tar.gz
  environment:
    REQUESTS_CA_BUNDLE: /path/to/internal-ca.crt

恒久的な設定として、Ansibleを起動するシェルプロファイル(例:~/.bashrc/etc/profile.d/ansible-ca.sh)にエクスポートします:

export REQUESTS_CA_BUNDLE=/etc/ssl/certs/internal-ca.crt
export SSL_CERT_FILE=/etc/ssl/certs/internal-ca.crt

企業プロキシ(SSLインスペクション)

ZscalerなどのプロキシはHTTPSトラフィックを傍受し、すべての接続をプロキシ自身のCAで再署名します。Pythonはそのプロキシ証明書を見て、信頼できないと判断して接続を切断します。

解決策:ITチームからプロキシのルートCAを入手し、上記のオプション2を使って追加してください。そのCAがトラストストアに入れば、プロキシ経由のすべてのHTTPS通信が機能します — 検証バイパスは不要です。

まず、プロキシが実際に原因かどうか確認します:

# Check what cert you're actually getting
openssl s_client -connect internal.example.com:443 </dev/null 2>/dev/null | openssl x509 -text -noout | grep -E "Issuer|Subject"

Issuer にサーバーの実際のCAではなくプロキシベンダー(Zscaler Inc のような名称)が表示されていれば、それが原因です。

修正の確認

フルプレイブックを実行する前に、コントロールノードでテストします:

# Quick Python check
python3 -c "
import urllib.request
response = urllib.request.urlopen('https://internal.example.com')
print(response.status)
"
# Or inspect the cert chain with curl
curl -v https://internal.example.com 2>&1 | grep -E "SSL|certificate|verify"

次に、最小限のプレイブックを実行してAnsibleが正常に動作することを確認します:

- name: Verify SSL fix
  hosts: localhost
  gather_facts: false
  tasks:
    - name: Check endpoint
      uri:
        url: "https://internal.example.com/health"
        method: GET
      register: result
    - debug:
        msg: "Status: {{ result.status }}"

validate_certs: false なしで Status: 200 が返れば — 完了です。

チェックリスト

  • 実際のCA証明書を取得する — 検証を無効化するだけでは済ませない
  • Ubuntuの場合:update-ca-certificates;RHELの場合:update-ca-trust
  • タスクレベルの制御には ca_path を、グローバルな修正にはシステムトラストストアを使用する
  • 企業プロキシの背後にいる場合は、サーバー証明書ではなくプロキシのルートCAをITチームから入手する
  • まず openssl s_client を実行して、どの証明書チェーンが使われているか正確に確認する

Related Error Notes