Azure VMからのAzure MySQL TLS/SSL証明書エラー原因と対処・予防策まとめ

「ローカル PC からは Azure Database for MySQL Flexible Server に接続できるのに、Azure VM やコンテナからだけ TLS/SSL 証明書エラーで落ちる……」という事象は、クラウド運用では珍しくありません。特に 2025 年 9 月のようにルート証明書のローテーションが入ると、証明書チェーンの差分がそのまま障害につながります。この記事では、実際に Azure VM で発生した MySQL TLS/SSL 証明書エラーを題材に、「原因の切り分け」「復旧手順」「今後の予防策」を、Azure と Linux 双方の視点から詳しく解説します。

目次

Azure VM からだけ MySQL TLS/SSL エラーが発生した事象の概要

まずは今回の Azure MySQL TLS/SSL 証明書エラーの状況を整理します。ポイントは次の 3 つです。

  • Azure Database for MySQL Flexible Server 自体は正常稼働している
  • ローカルの Windows / macOS からは TLS/SSL 接続が問題なく確立できる
  • 同じ .pem 証明書ファイルを指定している Azure VM やコンテナからのみ、2025-09-16 05:00 UTC 以降に TLS/SSL ハンドシェイク エラーが発生する

この時点で疑うべきなのは「ネットワーク」ではなく、「クライアント側の証明書ストア」や「MySQL クライアント設定」です。実際、本ケースの根本原因は Azure MySQL Flexible Server 側のルート証明書ローテーションでした。

質問と回答をまとめたサマリ

項目内容
発生タイミング2025-09-16 05:00 UTC 以降、Azure VM / コンテナからの TLS/SSL ハンドシェイクが失敗し始めた。
症状MySQL クライアントやアプリケーションから接続すると、SSL connection error や certificate verify failed といったエラーでコネクションが確立できない。
根本原因2025-09-01 に Azure MySQL Flexible Server のルート証明書がローテーションされ、新しい信頼チェーンに対して、VM / コンテナ側の単一ルート証明書ファイルでは検証ができなくなった。
必要なルート CADigiCert Global Root CA DigiCert Global Root G2 Microsoft RSA Root CA 2017 これら 3 つを 1 つの CA バンドルとして使用する必要がある。
対処手順(概要)上記 3 つのルート証明書を入手し、ca-bundle.pem に連結する。 MySQL クライアント / ドライバの ssl_ca(または ssl-ca)設定を ca-bundle.pem に更新する。 VM / コンテナを再起動、またはアプリケーションを再デプロイし、openssl s_client で検証する。
ローカル PC が動いた理由Windows / macOS のシステム証明書ストアには新しい DigiCert / Microsoft ルート証明書がすでに含まれており、OS のアップデートで自動取得されていた。一方、ミニマルな Linux VM / コンテナは、古い単一の .pem を直接指定しており更新されていなかった。

次のセクションでは、この TLS/SSL 証明書エラーの根本原因となった「ルート証明書ローテーション」と「信頼チェーンの検証」について、もう少し踏み込んで解説します。

根本原因: Azure MySQL Flexible Server のルート証明書ローテーション

TLS/SSL で行われる「証明書チェーン検証」とは

MySQL に限らず、TLS/SSL でサーバーに接続する場合、クライアント側では次のような流れで証明書を検証します。

  1. サーバー(今回で言えば Azure MySQL Flexible Server)が自分のサーバー証明書と、中間 CA 証明書チェーンをクライアントに提示する。
  2. クライアントは、提示された証明書チェーンの「一番上」が自分の持っているルート証明書ストア(信頼された CA の一覧)のどれかと一致するかを確認する。
  3. 一致しなければ「信頼できるルート CA まで証明書チェーンをたどれない」と判断し、certificate verify failed などの TLS/SSL エラーを返す。

つまり、Azure MySQL Flexible Server 側が新しいルート CA を使うようになると、クライアント側にも同じルート CA(またはそれを含むバンドル)が必要になります。

2025-09-01 のルート証明書ローテーションで何が変わったのか

2025-09-01 に Azure Database for MySQL Flexible Server の証明書チェーンが更新され、サーバー証明書のルートが新しい DigiCert / Microsoft ルート CA を前提とするチェーンに切り替わりました。具体的には、次の 3 つのルート CA が信頼チェーンの前提として必要になっています。

  • DigiCert Global Root CA
  • DigiCert Global Root G2
  • Microsoft RSA Root CA 2017

以前は、これらのうち一部だけを含んだ単一の .pem ファイルでも接続できていたため、Azure VM やコンテナのイメージに「古いルート CA ファイル」を直接コピーして使っていたケースが多くあります。しかしローテーション後は、3 つすべてを含む CA バンドルが必要になり、古い単一ファイルのままでは TLS/SSL ハンドシェイクに失敗するようになりました。

なぜローカル PC はエラーにならなかったのか

ローカルの Windows / macOS で接続できていた理由はシンプルで、OS が自動的に証明書ストアを最新状態に保っているためです。

  • Windows Update や macOS のソフトウェアアップデートにより、新しい DigiCert / Microsoft のルート証明書が OS の「信頼されたルート証明書ストア」に取り込まれていた。
  • MySQL Workbench や各種 GUI クライアント、ODBC ドライバ等は「OS の証明書ストア」を参照しており、個別に .pem を指定する必要がなかった。

一方で、多くの Azure VM やコンテナでは次のような構成になっていました。

  • 最小限の Linux イメージ(例: Debian slim / Alpine 等)を使用し、ca-certificates パッケージを長期間更新していない。
  • アプリケーション設定で ssl_ca=/path/to/azure-mysql-ca.pem のように、かなり前にダウンロードした単一 CA ファイルを直接指定している。
  • イメージのビルド時に .pem を取り込んでおり、コンテナごとに更新作業をしていなかった。

この結果、ローカル PC だけ正常で、Azure VM / コンテナからは MySQL TLS/SSL 証明書エラーが発生するという「一見ネットワークっぽい」現象が生じたわけです。

対処手順: 3 つのルート CA を連結した ca-bundle.pem を作成する

ここからは、実際に Azure VM やコンテナ側で行うべき復旧手順を、できるだけ具体的に説明していきます。大まかなステップは次のとおりです。

  1. 現在使用している CA ファイル / MySQL 設定の確認
  2. 必要な 3 つのルート CA を入手
  3. 3 つを連結して ca-bundle.pem を作成
  4. MySQL クライアント / アプリケーションの TLS 設定を更新
  5. openssl s_client と MySQL クライアントで疎通確認

1. 現在の MySQL TLS 設定と CA ファイルを確認する

まずは、今どこにどの .pem ファイルを置いているかを把握しましょう。代表的な確認ポイントは以下です。

  • MySQL CLI
    接続オプションに --ssl-ca や --ssl-mode が指定されていないか確認します。
  • 設定ファイル(my.cnf / my.ini)
    /etc/mysql/my.cnf や アプリケーション専用の設定ファイルに次のような記述がないか確認します。
[client]
ssl-mode=VERIFY_IDENTITY
ssl-ca=/etc/mysql/certs/azure-mysql-ca.pem
  • 環境変数やアプリ設定
    Java / PHP / Node.js / Python などのドライバでは、環境変数や接続文字列で CA ファイルを指定している場合があります。
言語 / ドライバ主な CA 指定方法
MySQL CLI--ssl-ca=/path/to/ca.pem
JDBC (MySQL)trustCertificateKeyStoreUrl や useSSL=true + JSSE 設定
PHP (mysqli / PDO)MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT を避け、MYSQLI_OPT_SSL_CA 等で指定
Python (mysql-connector / PyMySQL)ssl_ca 引数で CA パスを指定
Node.js (mysql2 等)接続オプションの ssl: { ca: fs.readFileSync('ca.pem') }

これらのいずれかで古い単一 CA ファイルを参照している場合、今回の TLS/SSL 証明書エラーの直接的な原因になっている可能性が高いです。

2. DigiCert / Microsoft の 3 つのルート CA を入手する

次に、以下の 3 つのルート証明書を入手します。

  • DigiCert Global Root CA
  • DigiCert Global Root G2
  • Microsoft RSA Root CA 2017

入手方法としては、主に次の 2 パターンがあります。

  • 公式サイトから .crt / .pem としてダウンロードする方法
    セキュリティ上、必ず CA ベンダーや Microsoft の公式情報源から取得してください。
  • すでに最新化されている別の OS からエキスポートする方法
    例えば、ローカルの Windows / macOS には新しいルート CA がインポート済みである可能性が高いため、そこからエクスポートして .pem に変換し、VM / コンテナに持ち込む方法もあります。

ファイル名は任意ですが、分かりやすさのために次のような名称で保存しておくと良いでしょう。

  • DigiCert_Global_Root_CA.pem
  • DigiCert_Global_Root_G2.pem
  • Microsoft_RSA_Root_CA_2017.pem

これら 3 ファイルを VM / コンテナ内のディレクトリ(例: /etc/mysql/certs)に配置します。

3. 3 つのルート証明書を連結して ca-bundle.pem を作成する

次に、配置した 3 つの .pem を連結し、MySQL クライアントや各種ドライバから参照する CA バンドルを作ります。Linux であれば cat コマンド一発です。

sudo mkdir -p /etc/mysql/certs

sudo cat DigiCert_Global_Root_CA.pem \
  DigiCert_Global_Root_G2.pem \
  Microsoft_RSA_Root_CA_2017.pem \
  > /etc/mysql/certs/ca-bundle.pem

sudo chmod 644 /etc/mysql/certs/ca-bundle.pem

この ca-bundle.pem を、以後は MySQL クライアント / ドライバの ssl_ca / ssl-ca として指定します。

4. MySQL クライアント / アプリケーションの TLS 設定を更新する

続いて、MySQL クライアントやアプリケーションの接続設定を ca-bundle.pem を参照するように変更します。

MySQL CLI の例

mysql -h <your-mysql-host> \
  -u <user> -p \
  --ssl-mode=VERIFY_IDENTITY \
  --ssl-ca=/etc/mysql/certs/ca-bundle.pem

--ssl-mode は VERIFY_IDENTITY を推奨します(サーバー証明書の CN / SAN とホスト名を突き合わせ、なりすましを防ぐため)。

my.cnf に設定する場合

[client]
ssl-mode=VERIFY_IDENTITY
ssl-ca=/etc/mysql/certs/ca-bundle.pem

この設定を行うことで、MySQL CLI だけでなく、同じ my.cnf を参照するアプリケーションも共通で TLS/SSL 設定を利用できます。

アプリケーション・ドライバ設定の例

技術スタック設定例(概要)
Python (mysql-connector)mysql.connector.connect(ssl_ca='/etc/mysql/certs/ca-bundle.pem', ssl_verify_cert=True)
Node.js (mysql2)createConnection({ ssl: { ca: fs.readFileSync('/etc/mysql/certs/ca-bundle.pem') } })
PHP (mysqli)mysqli_ssl_set($link, NULL, NULL, '/etc/mysql/certs/ca-bundle.pem', NULL, NULL);
.NET (MySqlConnector)接続文字列に SSL Mode=VerifyCA; CertificateFile=/etc/mysql/certs/ca-bundle.pem 等を設定

細かなオプション名やパラメータはドライバごとに異なるものの、「サーバー証明書を検証するための CA バンドル」として ca-bundle.pem を指させばよい、という考え方は共通です。

5. openssl s_client と MySQL クライアントで疎通確認する

設定変更後は、必ず TLS レベルと MySQL レベルの両方で接続確認を行いましょう。

openssl s_client で TLS ハンドシェイクだけを確認

openssl s_client -connect <your-mysql-host>:3306 \
  -servername <your-mysql-host> \
  -CAfile /etc/mysql/certs/ca-bundle.pem

最後のほうに表示される Verify return code: 0 (ok) が確認できれば、証明書チェーンの検証は成功しています。逆に、ここでエラーが出る場合は、ca-bundle.pem に必要なルート CA が含まれていない、もしくはファイルパスが誤っている可能性があります。

MySQL クライアントでも確認する

次に、実際の MySQL クライアントで接続し、暗号化状態を確認します。

mysql -h <your-mysql-host> \
  -u <user> -p \
  --ssl-mode=VERIFY_IDENTITY \
  --ssl-ca=/etc/mysql/certs/ca-bundle.pem

mysql> SHOW STATUS LIKE 'Ssl_cipher';
mysql> SHOW VARIABLES LIKE 'have_ssl';

Ssl_cipher に値が入り、have_ssl が YES になっていれば、TLS/SSL 接続は正常です。ここまで確認できれば、ひとまず Azure VM / コンテナからの MySQL TLS/SSL 証明書エラーは解消されたと考えてよいでしょう。

コンテナ環境での注意点: イメージとシークレットの管理

Azure Container Instances や Kubernetes(AKS)などのコンテナ基盤から MySQL に接続している場合、証明書の扱いが VM と少し変わります。代表的なパターンと注意点を整理しておきます。

Dockerfile に証明書を組み込む場合

最もシンプルなのは、ビルド時に ca-bundle.pem をイメージ内にコピーしてしまう方法です。

FROM debian:12-slim

RUN apt-get update && apt-get install -y ca-certificates && \
    mkdir -p /etc/mysql/certs

COPY ca-bundle.pem /etc/mysql/certs/ca-bundle.pem

ENV MYSQL_SSL_CA=/etc/mysql/certs/ca-bundle.pem

この場合、証明書を更新したいときはイメージの再ビルドが必須になるため、後述する「定期的なビルドパイプライン」や「自動テスト」と組み合わせることが重要です。

Kubernetes シークレットやボリュームとしてマウントする場合

より柔軟に運用したい場合は、Kubernetes の Secret や ConfigMap として ca-bundle.pem を登録し、Pod にマウントする方法が有効です。これにより、証明書だけを差し替えて Rolling Update を掛けることができ、アプリケーションイメージ自体を頻繁に作り直す必要がなくなります。

いずれの方式でも共通するベストプラクティスは「証明書ファイルの場所を環境変数化しておく」ことです。MYSQL_SSL_CA などの環境変数経由でアプリに渡しておけば、証明書の保管場所を変えても、アプリ側のコード変更を最小限に抑えられます。

今後の予防策: 証明書ローテーションで落ちない Azure MySQL 運用

一度このような TLS/SSL 証明書エラーを経験すると、「次の証明書ローテーションでは絶対に落としたくない」と感じるはずです。ここでは、今後同様のトラブルを未然に防ぐための具体的な対策を整理します。

Azure Service Health アラートで「プラットフォーム証明書の更新」を監視する

Azure では、各種マネージドサービスの証明書更新や計画メンテナンスを「サービス正常性(Service Health)」として通知しています。特に Azure Database for MySQL Flexible Server のような PaaS では、証明書ローテーションが事前アナウンスされるケースが多いため、必ずアラートを設定しておきましょう。

  • Azure ポータルの Service Health から「正常性アラート」を作成
  • 対象サービスに「Azure Database for MySQL」を含める
  • サブスクリプションレベルで「プラットフォーム証明書の更新」「計画メンテナンス」カテゴリにチェック
  • 通知先としてメール / Teams / Webhook などを登録

これだけで、「証明書がいつローテーションされるのか」「どのリージョンが対象なのか」といった情報を自動的に受け取れます。特に大規模システムでは、アプリ担当だけでなく SRE / インフラチームにも通知が届くよう、配信グループを整備しておくと安心です。

Azure の更新カレンダーや変更履歴を週次でチェックする

証明書ローテーションのような基盤変更は、Azure の更新カレンダーや変更履歴に掲載されることが多くあります。これらを RSS やチーム内の定例会議で共有する運用にしておくと、「いつの間にか根本原因が変わっていた」という状況を避けやすくなります。

  • 週次・隔週で Azure の更新情報をレビューする担当者を決める
  • MySQL / PostgreSQL / SQL などデータベース系のアップデートは必ずチェック対象にする
  • 証明書関連の告知があれば、すぐにステージング環境で検証するタスクを起票する

こうした「地味な情報収集」の有無が、本番リリース時の安定性に大きく影響することを、今回のようなインシデントが教えてくれます。

Azure Status での異常検知と、社内向け連絡フローを整える

計画済みの証明書ローテーションとは別に、緊急対応として証明書の更新が行われる場合もあります。その場合は Azure Status に速報的に掲載されることが多いため、SRE や当番エンジニアがこれを把握できるようにしておくとよいでしょう。

  • 障害当番が Azure Status を確認するタイミング(アラート発報時など)を運用ルールとして明文化する
  • Azure 側のステータスと、自社監視(アプリのエラーレート、TLS エラー数)を突き合わせて判断する
  • 「証明書ローテーション中の一時的な切断の可能性」など、ユーザーへの説明テンプレートを用意しておく

Azure Status の情報だけで直接解決できるわけではありませんが、「原因が自社ネットワーク側なのか、Azure プラットフォーム側なのか」を切り分ける貴重な手がかりになります。

CI/CD や監視に TLS 検証ジョブを組み込む

証明書ローテーションを事前に感知する強力な方法が、「定期的な TLS 検証ジョブ」を仕込んでおくことです。具体的には、次のような監視が有効です。

監視方法コマンド例検出できる問題
openssl ベースの TLS チェックopenssl s_client -connect host:3306 -servername host -CAfile ca-bundle.pem証明書チェーンの検証失敗、サーバー証明書の有効期限切れなど
MySQL CLI ベースの接続チェックmysql --ssl-mode=VERIFY_IDENTITY --ssl-ca=ca-bundle.pem -e "SELECT 1"TLS/SSL ハンドシェイク失敗、認証エラー、DB 停止など広範な問題
アプリ相当のヘルスチェックアプリケーションの接続ヘルスエンドポイントを呼び出し、HTTP 200 を確認アプリの設定ミス、ドライババージョン不整合、接続プール枯渇など

これらを CI/CD パイプラインや定期ジョブ(cron、Azure Functions、Logic Apps 等)として実行し、エラー検出時にアラートを上げることで、証明書ローテーションの「本番影響」を最小限に抑えられます。

VM / コンテナの ca-certificates パッケージをこまめに更新する

今回のような問題は、そもそも OS 側のルート証明書ストアが最新であれば、かなりの割合で防げます。Linux VM / コンテナでは、少なくとも以下のような更新を定期的に行うことをおすすめします。

Debian / Ubuntu 系

sudo apt-get update
sudo apt-get install --only-upgrade ca-certificates

RHEL / CentOS / Rocky Linux 系

sudo yum update ca-certificates

コンテナの場合も、ベースイメージが古いままだとルート CA が更新されません。ビルドパイプラインで「定期的にベースイメージを更新する」「それでも不足する分はカスタム CA バンドルで補う」といった方針を明文化しておくとよいでしょう。

カスタム CA バンドル運用のベストプラクティス

Azure VM / コンテナでは、「必要なルート CA だけを抜き出したカスタム CA バンドル」を配布するケースが多くあります。この運用自体は有効ですが、次のようなポイントを必ず押さえておきましょう。

  • 単一のルート CA に依存しない
    今回のように、複数の DigiCert / Microsoft ルート CA をまたがるチェーンに変わることがあります。常に複数ルートを想定したバンドル構成にしておきましょう。
  • ソースと更新方法を明確にする
    「どこから CA を取得するのか」「誰がいつ更新するのか」をドキュメント化し、担当者が替わっても運用が途切れないようにします。
  • テスト環境で先行検証する
    新しい CA を追加したら、必ずステージング環境やプレプロダクション環境で openssl s_client とアプリケーション接続の両方を検証してから本番に反映します。
  • Infrastructure as Code と合わせて管理する
    Bicep / ARM / Terraform などの IaC テンプレートと合わせて、「どの VM / コンテナにどの CA バンドルを配るのか」をコード化しておくと、構成ドリフトを防ぎやすくなります。

よくある誤解とアンチパターン

最後に、Azure MySQL Flexible Server を TLS/SSL で運用する際に陥りがちなアンチパターンをいくつか挙げておきます。今回のような証明書エラーを再発させないためにも、避けるべきポイントとして押さえておきましょう。

「とりあえず VERIFY_IDENTITY を外す」

証明書エラーが出たときに、ssl-mode=DISABLED や VERIFY_CA から PREFERRED へ下げてしまう対応は非常に危険です。短期的には接続が通るかもしれませんが、

  • 中間者攻撃(MITM)への耐性が下がる
  • 「本番は暗号化しているつもり」が実は平文だった、というセキュリティ事故につながる

といったリスクがあります。本記事で紹介したように、正しい CA バンドルを整備しつつ VERIFY_IDENTITY を維持するのが理想的です。

「ローカル PC で動いているから安全だと思い込む」

ローカル PC で接続できる場合でも、VM / コンテナ側の CA バンドルが古いままのケースは多々あります。OS ごとに証明書ストアの更新タイミングが違うため、「どこか一つの環境で動いているから、他でも大丈夫だろう」と考えるのは危険です。

特に、

  • 本番だけが古いゴールデンイメージを使っている
  • 本番のコンテナだけカスタム CA バンドルをマウントしている

といった構成では、「開発環境は正常だが、本番だけ TLS/SSL 証明書エラーになる」という状況が起こりがちです。

「イメージ内にハードコードした .pem を放置する」

コンテナイメージや VM テンプレートに .pem をハードコードしたまま、数年単位で更新されていないケースもよく見かけます。今回のようなルート証明書ローテーションが入ると、一斉に障害が発生するリスクがあります。

これを防ぐには、

  • CA バンドルのバージョンをタグ付けし、更新時に必ずイメージを再ビルドする
  • CA 更新をトリガーに CI/CD パイプラインを走らせる運用を構築する

といった「証明書もアプリと同じリリースプロセスで管理する」考え方が重要です。

まとめ: Azure VM の MySQL TLS/SSL エラーは「古い CA バンドル」が原因だった

本記事では、Azure Database for MySQL Flexible Server で発生した TLS/SSL 証明書エラーを題材に、原因と対処、そして今後の予防策を詳しく解説しました。ポイントを改めて整理すると、次のようになります。

  • 2025-09-01 の Azure MySQL Flexible Server のルート証明書ローテーションにより、従来の単一 CA ファイルでは新しい証明書チェーンを検証できなくなった。
  • 新しいチェーンでは、DigiCert Global Root CA / DigiCert Global Root G2 / Microsoft RSA Root CA 2017 の 3 つのルート CA を含む CA バンドルが必要となった。
  • ローカル PC は OS の証明書ストアが自動更新されていたため問題なく、古い CA ファイルに依存していた Azure VM / コンテナのみ TLS/SSL エラーが顕在化した。
  • VM / コンテナ側では、3 つのルート CA を連結した ca-bundle.pem を作成し、MySQL クライアント / ドライバの ssl_ca に設定し直すことで復旧できる。
  • 今後の予防策として、Azure Service Health アラート、更新カレンダーの定期チェック、TLS 検証ジョブの自動化、ca-certificates パッケージの更新などを組み合わせることが重要である。

Azure 上で MySQL を本番運用する際、「ネットワーク」「アプリケーションコード」だけでなく、「証明書チェーン」と「CA バンドル」の観点を常に意識しておくことは、もはや必須と言えます。今回のような TLS/SSL 証明書エラーに一度きちんと向き合っておけば、次の証明書ローテーションではむしろ「問題なく乗り切れた」ことを確認する絶好の機会にもなるはずです。

この記事の内容を、自身の Azure MySQL Flexible Server 運用に照らし合わせながら、証明書運用の仕組みをぜひ一度見直してみてください。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次