目次
はじめに
TLS とは、2つのサーバー間でセキュアなデータ通信を行うためのプロトコルです。転送中のデータを暗号化し、第三者による内容の盗聴や改竄を防ぐための技術です。
TLS といえば Web ブラウザなどフロントエンドとバックエンドの間の通信という文脈が多いですが、データベースにおいても、アプリケーションとデータベースの間の通信や、データベースクラスターの各ノード間の通信を暗号化するために TLS を適用することがあります。MongoDB も例外ではなく、転送中のデータを暗号化するため TLS の機能を標準で提供しています。この記事では、TLS の基本的な概念と、TLS 通信を有効にして MongoDB を構築する方法について説明します。
この記事には、以下の内容が含まれます。
- TLS (SSL) 通信とは何か
- MongoDB での TLS 通信の概要
- MongoDB での TLS の具体的な設定
- MongoDB での TLS の運用 (TLSの有効化、証明書の更新、性能への影響)
What is TLS?
基本説明
TLS (Transport Layer Security) とは、セキュアなデータ通信を行うためのプロトコルの名前です。SSL (Secure Sockets Layer) というプロトコルの後継として標準化されたもので、「SSL」という名称がすでに普及していたため、現在使われているのは TLS ですが、両者を区別せずに「SSL/TLS」などと呼ぶこともあります。この記事では「TLS」で統一します。
TLS の主要な機能は次の 3 つにまとめることができます。
-
暗号化 (Encryption)
- 公開鍵暗号と共通鍵暗号を組み合わせて使用して、通信経路を通って転送されるデータを暗号化する
-
認証 (Authentication)
- 証明書を使用して、データを転送するサーバーの正当性を保証する
-
整合性 (Integrity)
- メッセージ認証コード (MAC) を使用して、転送されたデータが改竄されていないことを保証する
いずれにしても、ユーザーのプライバシーを保護し、安全なデータ送受信を行うための技術と言えるでしょう。標準化形式として多くのデバイスやシステムでサポートされており、相互運用性が高いことも特徴です。
一方で、暗号化と復号に計算リソースを消費すること、また設定や管理が複雑化してコストがかかることなどが主なリスクとして挙げられます。
TLS は 2025 年現在バージョン 1.3 までリリースされており、パフォーマンスやセキュリティ性が改善され続けています。古いバージョンは脆弱性の観点から非推奨であり、MongoDBでは TLS 1.0 はサポートしていません。
以降の説明でしばしば、TLS の一般的な用語として、「クライアント」と「サーバー」という言葉を使いますが、DB の文脈においては「クライアント」=アプリケーションサーバー、「サーバー」=DB サーバーと置き換えれば分かりやすいかと思います。
TLS 構成要素
TLS 通信を行うための構成要素として重要なものをピックアップしてみます。(※)のついている用語はこのあと別途説明します。
Component | Storage Location | Definition |
---|---|---|
CA (Certificate Authority; 認証局) | Outside the Service | サーバー証明書を発行し、その信頼性を保証する主体 |
ルート証明書 (Root Certificate) | Client, Server |
証明書チェーン の最上位にある信頼の基点 クライアントがサーバー証明書を検証する際に使用される 通常、ブラウザやOSにプリインストールされている |
サーバー証明書 (Server Certificate) | Server | CAによって署名された証明書 サーバーがクライアントに提示する クライアントはこの証明書を検証してサーバーの信頼性を確認する |
クライアント証明書 (Client Certificate) | Client | クライアントがサーバーに提示し、クライアントの信頼性を証明する Optional クライアント証明書は用いない(クライアント側の検証はしない)場合も多い |
セッションキー (Session Key) | In Memory |
TLSハンドシェイク 中に生成される 実際にデータを暗号化・復号するために使用される(対称鍵) |
TLS ハンドシェイク
TLS ハンドシェイクは、TLS を使った通信セッションを始めるためのプロセスです。
TLS ハンドシェイクの中で、通信する二者 (サーバーとクライアント) は互いを認識・検証し、使用する暗号化アルゴリズムを決定し、データ暗号化に用いるセッションキーを共有します。
証明書チェーン (Certificate Chain)
他のCAに署名された証明書が、さらに他の証明書を署名することも可能です。このような証明書を 中間証明書 (Intermediate Certificate) と呼びます。証明書でありつつ、署名の主体 (=認証局) としても機能するという意味で、中間CAと呼ぶこともあります。
外部CAのルート証明書(ROOT)が証明書INT1を署名し、証明書INT1がさらに別の証明書INT2を署名し、証明書INT2がサーバー証明書LEAFを署名している、という具合に、署名関係のチェーンができることになります。
ROOT → INT1 → INT2 → LEAF
このような、ルート証明書から、実際にサーバーやクライアントに置かれる証明書 (エンドエンティティ証明書) へ至る関係を 証明書チェーン (Certificate Chain) と呼びます。エンドエンティティ証明書は、木構造の末端に来るためリーフ証明書とも呼ばれます。
ではなぜ、直接CAのルート証明書から署名を受けるのではなく、わざわざ署名関係が複雑になる中間証明書を使うのでしょうか。いくつかの理由があります。
- セキュリティの強化
- ルート証明書の秘密鍵は最も機密性が高い情報で、あまり頻繁には使いたくない
- 管理の柔軟性
- 発行・更新・失効などをルート証明書を変更せずにできる
- 体系的な管理
- 証明書チェーンは木構造の信頼の階層 (trust hierarchy) をなし、各証明書の責任を段階的に管理しやすくなる
MongoDB TLS
概要
MongoDBでは、次の2種類の通信路に対するTLSが提供されています。
-
1) Client (Application Server) ↔ MongoDB Server 間
- クライアント (アプリケーション) とDBサーバーの間の通信
-
2) DB ノード (mongod/mongos) 間
- Replica SetやSharded Clusterにおける、各メンバー間の通信
Client ↔ MongoDB間の通信と、mongod/mongos ノード間通信におけるTLSの利用は、net.tls.mode
オプションを使用してある程度個別に選択することができます。
このオプションを指定しない場合のデフォルト値は disabled
になります。allowTLS
, preferTLS
, requireTLS
の順に非TLS通信を厳格に制限するオプションになります。
- Incoming Connection = 自身への接続 (Clientから / 他のDBノードから)
- DISABLED = non-TLS 接続のみ受け入れます。
- ENABLED = TLS/non-TLS 両方の接続を受け入れます。
- REQUIRED = TLS 接続のみ受け入れます。
- Outgoing Connection = 自身からの接続 (他のDBノードへ)
- NO USE = TLS を使わずに接続します。
- USE = TLS を使って接続します。
net.tls.mode |
Incoming Connection | Outgoing Connection | Client <-> MongoDB | DB Node <-> DB Node |
---|---|---|---|---|
disabled |
DISABLED | NO USE | TLSは使用しない | TLSは使用しない |
allowTLS |
ENABLED | NO USE | TLS/non-TLS ともに接続可能 | TLSは使用しない |
preferTLS |
ENABLED | USE | TLS/non-TLS ともに接続可能 | TLSを使用する |
requireTLS |
REQUIRED | USE | TLS でのみ接続可能 | TLSを使用する |
要件
MongoDB
MongoDB 3.0 以降で、Community Edition を含むすべての MongoDB で TLS を利用可能です。実質的には標準機能と言ってよいでしょう。
ただし、後述する証明書の Online Rotate は MongoDB 5.0 以降でのみ利用可能です。TLS のバージョンは 1.1 以降をサポートしています (1.0 は不可)。
暗号化方式など、より詳細な要件は公式ドキュメントも参照してください。
証明書
TLSを使うためには、何らかの方法で有効なTLS証明書を発行し、MongoDB サーバーに配布する必要があります。
- 自己署名証明書 (開発環境でのみ使用)
- 他のCAの署名を受けずに、自分自身によって署名された証明書です。
- ルート証明書は、証明書チェーンの最上位にあって他の者からの署名を受けることはできないので、ルート証明書は自己署名証明書になります。
- 外部のCAを使わずに手元で証明書を用意してテストすることが可能ですが、サーバーの信頼性を証明するのには十分でないので、通常本番環境では用いません。
- 他のCAの署名を受けずに、自分自身によって署名された証明書です。
- 認証局(CA)から署名を受けた証明書
- 本番環境では、他のCAによって署名された証明書をサーバーに設置し、MongoDB を構築します。
- 証明書の具体的な発行方法は、使用するCAや環境によるため本稿ではスコープ外とします。
MongoDBに配布する際には以下の要件に注意してください。
- 証明書の有効期限が切れていないこと
- すべての証明書が同一のCAによって署名を受けていること
- 証明書の Subject Alternative Name (SAN) または Common Name (CN) にDBサーバーのホスト名またはIPアドレスが含まれていること
- ホスト名(またはIPアドレス)が一致しないとクライアントからの接続時に失敗します
-
net.tls.allowInvalidHostnames
オプションをtrue
にすることで検証をスキップ可能
(参考) Certificate-Based Authentication
TLS の証明書は X.509 という標準化形式の証明書で、暗号化のほかに認証の機能も持っています。MongoDB でもこれを利用して、通信の暗号化だけでなく、認証にも同じ証明書を使う機能が提供されています。
MongoDB が提供する X.509 証明書による認証には次の 2 種類があります。
- Client-MongoDB 通信において、User / Password の代わりに X.509 証明書で認証を行う(クライアント認証)
- mongod/mongos ノード間通信において、 keyFile の代わりに X.509 証明書で認証を行う (メンバーシップ認証)
TLS を有効にしたからといって、認証も X.509 証明書で行わないといけないわけではありません。反対に、X.509 証明書のによる認証を利用するには、TLS 通信を有効にする必要があります。
本稿の主題ではないため詳述は避け、TLS と関連した設定として紹介するに留めておきます。
セットアップ例
この章では、TLS が有効な設定で新しい MongoDB を構築する具体的な手順を見てみましょう。
ここでは、次のような仕様でセットアップします。
- クライアント証明書は使わない
- Client ↔ MongoDB間、DB Node間の通信ともにTLSを適用する
テストは MongoDB 6.0 + Rocky Linux 8 で行いました。
証明書を用意する
証明書の発行
一般的なTLS証明書の発行の手順は、次のとおりです。
-
サーバー秘密鍵を用意する
$ openssl genrsa -out private.key 2048
-
秘密鍵から証明書署名要求 (CSR; Certificate Signing Request) を作成する
-
ssl.cnf
ファイルを作成 - CAによって要件がありますが、共通して重要なのは
alt_names
に証明書を置くサーバーの DQFN または IP アドレスを指定することです。
ssl.cnf (example)[ req ] default_bits = 2048 default_md = sha256 prompt = no distinguished_name = req_distinguished_name req_extensions = v3_req [ req_distinguished_name ] C = YOUR COUNTRY ST = YOUR SITE L = YOUR LOCATION O = YOUR ORGANIZATION [ v3_req ] subjectAltName = @alt_names [alt_names] DNS.1 = YOUR.HOSTNAME.com
- CSRの作成例
$ sudo openssl req -new -key private.key -out cert.csr -config ssl.cnf
-
-
証明書署名要求 (CSR) を認証局 (CA) に送信する
-
認証局 (CA) から署名された証明書が送られてくる
- 加えて、CAのルート証明書と中間証明書も取得する必要があります。
ちなみに、テスト用に自己署名証明書を使ってMongoDBをデプロイする Hands-on が公式ドキュメントに用意されているので、外部CAなしで簡単にテストしたい場合はこちらも参照するとよいと思います。
MongoDB で用いる形式
MongoDBで用いる証明書ファイルは、拡張子 .pem
の plaintext で、秘密鍵と証明書を両方含む必要があります。
例えば、サーバー秘密鍵が server.key
, サーバー証明書が server.cer
というファイルだったとすると、MongoDBで用いる Server Certificate File ( server.pem
とします) は次のように用意すればよいです。
$ (cat server.cer; echo; cat server.key) > server.pem
ルート証明書から該当のリーフ証明書 (サーバー証明書) までのすべての証明書チェーンを含む .pem
ファイルも必要になります。
例えば、ルート証明書 (root.cer
) → 中間証明書 (inter.cer
) → サーバー証明書 (server.cer
) という階層で署名されているとすると、MongoDBで用いる CAFile ( ca.pem
とします) は次のように用意すればよいです。
ここで、ルート証明書が最後になるように順番通りに並べる必要があることに注意してください。
$ (cat inter.cer; echo; cat root.cer) > ca.pem
DB サーバー側の設定
実際にDBサーバーに配布すべきファイルと、config fileの設定項目を紹介します。Replica SetやSharded Clusterの場合は、すべてのノードで同様に設定が必要です。
- MongoDB サーバーに配布するファイル
Item | Description |
---|---|
Server Certificate File | サーバーの秘密鍵と証明書を連結した .pem ファイル |
CAFile | 証明書を検証するための、Rootからの全ての証明書チェーンを含む .pem ファイル |
- Config fileの設定
Option | Example Value | Detail |
---|---|---|
net.tls.mode |
requireTLS |
Client↔DB間のTLS、DB Node間のTLS を両方とも使用します。 |
net.tls.certificateKeyFile |
server.pem |
Server Cert File のパス |
net.tls.CAFile |
ca.pem |
CAFile のパス |
net.tls.allowConnectionsWithoutCertificates |
true |
クライアント証明書を使わないため true (default は false ) |
net.tls.allowInvalidCertificates |
false |
テスト環境で自己署名証明書を使用する場合は true
|
net.tls.allowInvalidHostnames |
false |
SANにホスト名が含まれるかの検証をスキップする場合は true
|
- confファイル
net:
tls:
mode: requireTLS
certificateKeyFile: server.pem
CAFile: ca.pem
allowConnectionsWithoutCertificates: true
他は通常の MongoDB 構築と同じように、data directory など必要なものを用意して mongod (mongos) を実行すればよいです。
アプリケーションサーバー (クライアント) 側の設定
アプリケーション側でも証明書の配布と、接続時の設定が必要になります。CAFileはDBサーバーにあるものと同じもので大丈夫です。
Item | Description |
---|---|
CAFile | 証明書を検証するための、Rootからの全ての証明書チェーンを含む .pem ファイル |
例えば Mongosh (MongoDB Shell) からの接続する場合は、次のオプションを追加すればよいです。今回 DB を requireTLS
で立てているため、TLS を使わない接続は拒否されます。
mongosh "mongodb+srv://{YOUR_USER}:{YOUR_PASSWORD}@{YOUR_HOSTNAME}/?authSource=admin" --tls --tlsCAFile ca.pem
Option | Example Value | Detail |
---|---|---|
--tls |
なし | TLS接続を有効化する。なお、+srv 接続文字列形式を使う場合は、デフォルトで TLS が有効となる。 |
--tlsCAFile |
ca.pem |
CA Fileのパス |
運用作業
この章では、MongoDB の TLS に関連する運営上の事項について解説します。次の内容を含みます。
- TLS が 無効な MongoDB を、サービスを止めずに (onlineで) TLS が有効な設定に変更する方法
- TLS 証明書を online で交換する方法
- TLS 使用にともなう性能への影響
TLS が有効な設定にアップグレードする
既にサービス中のMongoDBクラスターでTLS設定を変更するときは、net.tls.mode
オプションを順に変更していく必要があります (Rolling Update) 。
たとえば mode
が disabled
の状態からいきなり requireTLS
に変更すると、クライアントからの接続が一時的に全て切れることになってしまいますから、段階を踏んで安全に更新することが重要です。setParameter
コマンドを使って値を更新する場合、ステップを飛ばした更新はできないようになっています。
> db.adminCommand( { getParameter: 1, tlsMode: 1 })
{
tlsMode: 'allowTLS',
ok: 1,
'$clusterTime': {
clusterTime: Timestamp({ t: 1740129237, i: 1 }),
signature: {
hash: Binary.createFromBase64('gSG+Ncfk/MQW9xpPFLhPKeJDFVI=', 0),
keyId: Long('7457746784316555270')
}
},
operationTime: Timestamp({ t: 1740129237, i: 1 })
}
> db.adminCommand( { setParameter: 1, tlsMode: "requireTLS" })
MongoServerError[BadValue]: Illegal state transition for tlsMode, attempt to change from allowTLS to requireTLS
net.tls.mode
オプションの仕様を再掲します。
-
Incoming Connection = 自身への接続 (Clientから / 他のDBノードから)
-
DISABLED
= non-TLS 接続のみ受け入れます。 -
ENABLED
= TLS/non-TLS 両方の接続を受け入れます。 -
REQUIRED
= TLS 接続のみ受け入れます。
-
-
Outgoing Connection = 自身からの接続 (他のDBノードへ)
-
NO USE
= TLS を使わずに接続します。 -
USE
= TLS を使って接続します。
-
net.tls.mode |
Incoming Connection | Outgoing Connection | Client <-> MongoDB | DB Node <-> DB Node |
---|---|---|---|---|
disabled |
DISABLED | NO USE | TLSは使用しない | TLSは使用しない |
allowTLS |
ENABLED | NO USE | TLS/non-TLS ともに接続可能 | TLSは使用しない |
preferTLS |
ENABLED | USE | TLS/non-TLS ともに接続可能 | TLSを使用する |
requireTLS |
REQUIRED | USE | TLS でのみ接続可能 | TLSを使用する |
作業手順の例
TLSを全く使っていなかった (disabled
) クラスターで、non-TLS 接続を拒否する (requireTLS
) 設定に変更する場合の作業例です。
-
Config file に以下の設定を追加する
net: tls: mode: allowTLS certificateKeyFile: <Certificate Key PATH> CAFile: <CAFile PATH>
-
全てのノードを再起動する
ReadPreference 等の使用状況に注意して、適切に hidden 設定を使いながら順番に再起動してください。- この時点で、
net.tls.mode
はallowTLS
に切り替わっている - Clientからの通信は TLS / non-TLS 両方受け入れる状態
- この時点で、
-
全てのクライアントをTLSを使用するように切り替える
-
全てのノードを
preferTLS
に切り替える (コマンドでonlineに変更可能)> db.adminCommand( { setParameter: 1, tlsMode: "preferTLS" } )
- この時点で、全ての通信はTLSを使用している状態
-
全てのノードを
requireTLS
に切り替える> db.adminCommand( { setParameter: 1, tlsMode: "requireTLS" } )
-
次にノードを再起動したときに正しく設定されるように、config fileを修正する
mongod.confnet: tls: mode: requireTLS
証明書の交換
MongoDB 5.0 以降では、rotateCertificates
コマンドを利用して、TLS証明書の交換は、サーバーを再起動することなく online に実行できるようになりました。
rotateCertificates
コマンドの適用対象は以下の3つです。
- TLS証明書
- CA証明書
- CRLファイル (証明書失効リスト)
作業手順の例
MongoDBサーバーのTLS証明書を交換する作業の具体例です。
-
新しい証明書のファイル名は、既存の証明書と同じにする必要がある
// AS-IS $ ls server.pem server_new.pem // Modify $ mv server.pem server_old.pem $ mv server_new.pem server.pem // TO-BE $ ls server.pem server_old.pem
-
次のコマンドを実行する
messageフィールドはoptional。指定するとログファイルに出力されるようになる> db.adminCommand( { rotateCertificates: 1, message: "Rotating certificates" } )
ログファイルの出力
{"t":{"$date":"2024-12-06T22:58:42.541+09:00"},"s":"I", "c":"COMMAND", "id":4988500, "ctx":"conn47","msg":"Certificate rotation completed successfully","attr":{"message":"Rotating certificates"}}
- 既に接続済みのクライアントは、次に接続されるまでは、古い証明書を使って接続し続ける
パフォーマンステスト
TLS は MongoDB の性能にどのような影響を与えるのでしょうか。一般に、TLSを使用しない通信と比べると、送信側での暗号化・受信側での復号にそれぞれリソースを使うため、CPUに負荷がかかりスループットに影響が出る可能性があります。
そこで、POCDriver という MongoDB 用のベンチマークテストツールを利用して、TLS の使用が MongoDB のパフォーマンスにどのような影響を与えるかテストしてみました。指標として、RPS、Latency、CPU 使用率、Read/Write Ticketの消費状況をチェックしましたが、結論としては、大きな差は確認できませんでした。
実行環境
- Spec:
- CPU : 4core
- Memory : 16GB
- Disk : SSD 100GB
- Rocky Linux 8.10
- MongoDB 6.0.14 Community
- Replica Set (Primary x 1, Secondary x 2)
- 同一のネットワーク構成を使用 (社内プライベートネットワーク)
詳細
POCDriver では、Insert / Key Query / Range Query / Update の 4 種類の DML に対して個別に QPS を設定してベンチマークテストを実行することができます。例えば、次のように実行すると、
java -jar $poc_driver_jar -t 100 -s 1000 -q 100000 -i 100000 -d 3600 -c $mongodb_connection
-
t
(スレッド数) は 100, -
s
(slow queryの閾値) は 1000 ms, -
q
(全体のqps) は 100,000 qps, -
i
(insert) は 100,000 qps, -
d
(実行期間) は 3,600 s
という設定で MongoDB に DML が送られます。
TLS を使用しないクラスターと、Client ↔ MongoDB Server 間および DB Node 間の全ての通信に TLS を使用するクラスターで、同じテストを実行しました。Insert 操作 (write) のみ、Query 操作 (read) のみの場合と、両方を組み合わせた場合の 3 つのシナリオで実行しました。
結果は次の表のとおりです。
数字はいずれも、ベンチマークテストを実行した時間に、前後の5分ずつを加えた期間に計測した値の平均です。
Insert
- 100,000 QPS (insert)
- 1 hour
Item | non-TLS | TLS |
---|---|---|
Query per Second | 62,754 | 62,643 |
Write Latency (ms) | 0.896 | 0.883 |
CPU Usage | 57% | 57% |
Write Ticket (max=128) | 127 | 127 |
Query
- 15,000 QPS (key query)
- 1 hour
Item | non-TLS | TLS |
---|---|---|
Query per Second | 11,445 | 11,370 |
Read Latency (ms) | 4.676 | 4.956 |
CPU Usage | 83% | 83% |
Read Ticket (max=128) | 125 | 125 |
Mixed
- 10,500 QPS (key query)
- 150 QPS (update)
- 20,000 QPS (insert)
- 2 hour
Item | non-TLS | TLS |
---|---|---|
Query per Second (R) | 6,956 | 6,945 |
Query per Second (W) | 13,126 | 13,103 |
Latency (ms) (R) | 15.308 | 14.990 |
Latency (ms) (W) | 1.248 | 1.226 |
CPU Usage | 89% | 90% |
Ticket (max=128) (R) | 112 | 115 |
Ticket (max=128) (W) | 127 | 127 |
簡易的なテストではありますが、大きな差はないことが確認できると思います。
さいごに
ここまで、TLS (SSL) 通信の基本概念から始め、MongoDB における TLS 機能の概要、具体的な構築方法といくつかの運用上の作業について説明しました。
業務で MongoDB の TLS について技術検証を行う機会があり、自分なりに調べた内容を整理してみましたが、MongoDB の TLS は、rolling update が問題なく実行できるように設計されたオプションなど、シンプルなようで奥が深いと思います。
さらには MongoDB を通じて TLS 自体への理解も深めることができ、良い機会となりました。
この記事が MongoDB の構築や運用、また MongoDB を用いるアプリケーションの設計において役に立たちましたら幸いです。
また、内容や文章に関してアドバイスやレビューをくださったチームの皆さんに感謝いたします。
最後までお読みいただきありがとうございました。