データベース接続における 'SSL error: decryption failed or bad record mac' の修正方法

intermediate🔒 SSL/TLS2026-06-15| Linux (Ubuntu/Debian), PostgreSQL, Docker, AWS/GCP/Azure ネットワーク

Error Message

SSL error: decryption failed or bad record mac
#postgresql#データベース#ssl#ネットワーク#docker

クイック修正:通常はMTUが原因です

このエラーの約90%は、ネットワークのMTU(最大転送単位)の不一致に起因します。パケットがネットワークセグメントのサイズ制限(VPN、Dockerブリッジ、クラウドVPCなどで一般的)を超えると、断片化(フラグメンテーション)が発生します。SSL/TLSプロトコルはこの断片化をうまく処理できず、チェックサムエラーを引き起こします。

即時の対応: ネットワークインターフェースまたはDockerネットワークのMTUを1400または1450に下げてください。すぐにテストを行い、接続が安定するか確認してください。

# テスト用に一時的にMTUを1400に下げる
sudo ip link set dev eth0 mtu 1400

なぜこれが発生するのか

このエラーはデータ整合性の崩壊を意味します。つまり、クライアントに届いたデータが、サーバーで生成されたメッセージ認証コード(MAC)と一致しないということです。転送中にデータが破損したか、切り捨てられたか、あるいは不適切に再構成された可能性があります。

SSL/TLSプロトコルは整合性に関して非常に厳格です。パケット内のたった1バイトの欠落や1ビットの反転でも、復号プロセスは失敗します。標準的なタイムアウトとは異なり、このエラーは接続自体は成功したものの、大量のトラフィックが流れ始めた時点でデータストリームが崩壊したことを示しています。

問題が発生しやすい箇所

  • Dockerコンテナ: DockerのMTUのデフォルトは1500です。しかし、ホストがそれより低いMTU(Google Cloud Platformの標準である1460など)を使用している場合、ブリッジでパケットが切り捨てられます。
  • VPNと暗号化トンネル: IPsecやWireGuardなどのプロトコルは、データに追加のヘッダーを付与します。その結果、実際のペイロードに使用できるサイズが標準の1500バイト未満になります。
  • レガシーなネットワーク機器: まれに、故障しかけているスイッチや安価なNIC(ネットワークカード)が、高負荷時にビット反転を起こすことがあります。
  • ライブラリの不一致: データベースドライバー(libpqなど)とシステムのOpenSSLバージョンの互換性の欠如により、不安定な復号動作が発生することがあります。

修正方法

1. MTUの調整

クラウド環境やVPNでは、標準の1500バイトよりも低いMTUが必要になることがよくあります。ip addrで現在の設定を確認してください。経路がサポートする正確な制限を見つけるには、"do not fragment"(断片化禁止)フラグを付けてpingを使用します。

# 1472バイトでテスト(1500バイト - ICMP/IPヘッダーの28バイト)
ping -M do -s 1472 your-database-host.com

"Frag needed" エラーが表示される場合は、pingが成功するまで -s の値を下げてください(1430、次に1400など)。成功した値に28を加えたものが、最大MTUになります。

Ubuntu(Netplanを使用)でこの変更を永続化するには:

# /etc/netplan/01-netcfg.yaml
network:
  version: 2
  ethernets:
    eth0:
      mtu: 1400

2. DockerネットワークのMTU設定

アプリケーションがDocker内で動作し、外部データベースに接続している場合は、docker-compose.yml でブリッジのMTUを合わせる必要があります。

networks:
  default:
    driver: bridge
    driver_opts:
      com.docker.network.driver.mtu: 1400

3. クライアントライブラリのアップグレード

ソフトウェアのバグが原因であることもあります。PostgreSQLを使用している場合、libpq が重いSSL処理を担当します。ベースイメージをアップグレード(例えば python:3.9-slim から python:3.12-slim へ移行)することで、根深いOpenSSLのバグが解決することがよくあります。

sudo apt-get update && sudo apt-get install --only-upgrade libpq5 openssl

4. NICハードウェアオフロードの無効化

ベアメタルサーバーでは、NIC(ネットワークカード)がGeneric Receive Offload (GRO) を介してTCP処理の最適化を試みることがあります。これらの最適化が暗号化されたパケットを損なうことがあります。ハードウェアの干渉を排除するために、これらをオフにしてください。

sudo ethtool -K eth0 gro off
sudo ethtool -K eth0 lro off

検証の実施

MTUの問題があっても、パケットサイズ制限に達しない小規模なクエリは成功することが多いです。接続を真にテストするには、数千行を返すクエリを実行してください。これにより、ネットワークに巨大な断片化されたペイロードの処理を強制させます。

# psqlを使用して大規模なデータセットを取得する
psql "sslmode=require host=your-db.com user=myuser dbname=mydb" -c "SELECT * FROM large_table LIMIT 5000;" > /dev/null

これが bad record mac エラーなしで完了すれば、トンネルはようやく安定したと言えます。

予防とネットワークのヒント

信頼性の高いデータベースは、安定したネットワーク基盤の上に成り立っています。サブネットやVPCを設計する際は、常にIPsecなどのセキュリティレイヤーのオーバーヘッドを考慮してください。

私はネットワーク境界を設計する際、ToolCraftのSubnet Calculatorのようなツールを頼りにしています。これはCIDR範囲を視覚化し、サブネットのロジックが特定のMTU調整を必要とする VPN ゲートウェイと競合しないようにするのに役立ちます。早期にこれらの境界を計画しておくことで、後々のイライラするSSLハンドシェイクや復号エラーを防ぐことができます。

最後に、ファイアウォールのログで「Deep Packet Inspection」(DPI)設定を確認してください。一部の企業向けファイアウォールはSSLトラフィックを検査しようとして、誤ってレコードを破損させ、この特定のエラーメッセージを引き起こすことがあります。

Related Error Notes