VMware Tanzu RabbitMQ 4.3のRPMパッケージをRocky Linux 9にインストールするメモ
Note
- 2026-05-13 Tanzu RabbitMQ 4.3版に更新しました
- 2026-05-14 TLSの有効化セクションを追加しました
以下の手順では、VMware Tanzu RabbitMQのRPMパッケージをRocky Linux 9にインストールし、3台のクラスタを構成します。
ホスト名はrmq-1、rmq-2、rmq-3とします。また、以下の手順ではファイアウォールが無効になっている前提としますが、必要であれば以下のTCPポートを開放してください。
| ポート | 用途 |
|---|---|
| 4369 | epmd (Erlang Port Mapper Daemon) |
| 5672 | AMQP (平文) |
| 5671 | AMQPS (TLS) |
| 15672 | HTTP (Management UI) |
| 15671 | HTTPS (Management UI) |
| 25672 | Erlang distribution |
| 35672-35682 | CLI tools |
目次
VMware Tanzu RabbitMQのダウンロード
Broadcom Supportにログインして、VMware Tanzu RabbitMQのダウンロードページにアクセスします。
最新バージョンを選択します。この手順書では4.3.0を使用します。

"I agree to the Terms and Conditions"にチェック(要リンククリック)を入れて、

EL9用のインストーラーを~/Downloadsなどにダウンロードします。

(必要であれば)rpmに含まれるファイルを確認します。
rpm -qlp tanzu-rabbitmq-server-4.3.0-1.el9.x86_64.rpm
Tanzu RabbitMQのインストール
このセクションでの作業は rmq-1 上で行います。
インストール作業に使用する、あるいは作業に便利なパッケージをインストールします。
sudo dnf install -y wget lsof less vim
RabbitMQに必要なErlangのRPMパッケージをダウンロードします。
wget https://github.com/rabbitmq/erlang-rpm/releases/download/v27.3.4.11/erlang-27.3.4.11-1.el9.x86_64.rpm
Tanzu RabbitMQと依存パッケージをインストールします。
sudo dnf install -y logrotate erlang-27.3.4.11-1.el9.x86_64.rpm tanzu-rabbitmq-server-4.3.0-1.el9.x86_64.rpm
インストールされたパッケージのバージョンを確認します。
$ rpm -qa | grep -E 'erlang|rabbitmq'
erlang-27.3.4.11-1.el9.x86_64
tanzu-rabbitmq-server-4.3.0-1.el9.x86_64
$ sudo rabbitmqctl --version
4.3.0
Tanzu RabbitMQのサービスを有効化し、サービスを起動します。
sudo systemctl enable tanzu-rabbitmq-server
sudo systemctl start tanzu-rabbitmq-server
Tanzu RabbitMQサービスの状態を確認します。Started Tanzu RabbitMQ server.が出力されていることを確認してください。
$ systemctl status tanzu-rabbitmq-server | cat
● tanzu-rabbitmq-server.service - Tanzu RabbitMQ server
Loaded: loaded (/usr/lib/systemd/system/tanzu-rabbitmq-server.service; enabled; preset: disabled)
Drop-In: /run/systemd/system/service.d
└─zzz-lxc-service.conf
Active: active (running) since Wed 2026-05-13 11:24:34 JST; 5s ago
Main PID: 1024 (beam.smp)
Tasks: 52 (limit: 617453)
Memory: 158.3M (peak: 173.3M)
CPU: 2.656s
CGroup: /system.slice/tanzu-rabbitmq-server.service
├─1024 /usr/lib64/erlang/erts-15.2.7.8/bin/beam.smp -W w -MBas ageffcbf -MHas ageffcbf -MBlmbcs 512 -MHlmbcs 512 -MMmcs 30 -pc unicode -P 1048576 -t 5000000 -stbt db -zdbbl 128000 -sbwt none -sbwtdcpu none -sbwtdio none -- -root /usr/lib64/erlang -bindir /usr/lib64/erlang/erts-15.2.7.8/bin -progname erl -- -home /var/lib/rabbitmq -- -pa "" -noshell -noinput -s rabbit boot -boot start_sasl -syslog logger "[]" -syslog syslog_error_logger false -kernel prevent_overlapping_partitions false --
├─1037 erl_child_setup 32768
├─1084 /usr/lib64/erlang/erts-15.2.7.8/bin/inet_gethost 4
├─1085 /usr/lib64/erlang/erts-15.2.7.8/bin/inet_gethost 4
└─1088 /bin/sh -s rabbit_disk_monitor
May 13 11:24:33 rmq-1 rabbitmq-server[1024]: Doc guides: https://www.rabbitmq.com/docs
May 13 11:24:33 rmq-1 rabbitmq-server[1024]: Support: https://www.rabbitmq.com/docs/contact
May 13 11:24:33 rmq-1 rabbitmq-server[1024]: Tutorials: https://www.rabbitmq.com/tutorials
May 13 11:24:33 rmq-1 rabbitmq-server[1024]: Monitoring: https://www.rabbitmq.com/docs/monitoring
May 13 11:24:33 rmq-1 rabbitmq-server[1024]: Upgrading: https://www.rabbitmq.com/docs/upgrade
May 13 11:24:33 rmq-1 rabbitmq-server[1024]: Logs: /var/log/rabbitmq/rabbit@rmq-1.log
May 13 11:24:33 rmq-1 rabbitmq-server[1024]: <stdout>
May 13 11:24:33 rmq-1 rabbitmq-server[1024]: Config file(s): (none)
May 13 11:24:34 rmq-1 rabbitmq-server[1024]: Starting broker... completed with 0 plugins.
May 13 11:24:34 rmq-1 systemd[1]: Started Tanzu RabbitMQ server.
リッスンしているポートを確認します。
$ sudo lsof -n -i -P | grep -i listen | grep rabbitmq
epmd 881 rabbitmq 3u IPv4 629997 0t0 TCP *:4369 (LISTEN)
epmd 881 rabbitmq 4u IPv6 629998 0t0 TCP *:4369 (LISTEN)
beam.smp 1024 rabbitmq 24u IPv4 627526 0t0 TCP *:25672 (LISTEN)
beam.smp 1024 rabbitmq 39u IPv6 627537 0t0 TCP *:5672 (LISTEN)
RabbitMQに対してpingが通ることを確認します。
$ sudo rabbitmq-diagnostics ping
Will ping rabbit@rmq-1. This only checks if the OS process is running and registered with epmd. Timeout: 60000 ms.
Ping succeeded
rmq-1をクローンして、rmq-2とrmq-3を作成
ここまで作業した内容を rmq-2 と rmq-3 にも適用します。 rmq-1 VMをクローンして rmq-2 と rmq-3 を作ると良いでしょう。もし、 rmq-2 と rmq-3 も最初から構築する場合は rmq-1 の /var/lib/rabbitmq/.erlang.cookie の内容をコピーして rmq-2 と rmq-3 の起動前に配置してください。
$ sudo cat /var/lib/rabbitmq/.erlang.cookie
QQFAJVSPYEVMCQLYCPTH
rmq-1 、 rmq-2 、 rmq-3 のIPアドレスとホスト名を全ノードの /etc/hosts に記入してください。以下は例です。事前に決めた固定IPアドレスを使う場合は rmq-1 の複製時点で作成しておくと良いでしょう。
cat <<EOF | sudo tee -a /etc/hosts
192.168.139.108 rmq-1
192.168.139.201 rmq-2
192.168.139.227 rmq-3
EOF
RabbitMQクラスタの作成
rmq-2 と rmq-3 上で次のコマンドを実行し、 rmq-1 のクラスタに参加します。
sudo rabbitmqctl join_cluster rabbit@rmq-1
Warning
旧バージョンのRabbitMQの場合は、クラスタのセットアップに追加の手順が必要です。
以下のコマンドは任意のノード上で実行してください。
次のコマンドでクラスタの状態を確認します。3台のノードが表示されていることを確認してください。
$ sudo rabbitmqctl cluster_status
Cluster status of node rabbit@rmq-1 ...
Basics
Cluster name: rabbit@rmq-1
Total CPU cores available cluster-wide: 48
Cluster Tags
(none)
Disk Nodes
rabbit@rmq-1
rabbit@rmq-2
rabbit@rmq-3
Running Nodes
rabbit@rmq-1
rabbit@rmq-2
rabbit@rmq-3
Versions
rabbit@rmq-1: Tanzu RabbitMQ 4.3.0 on Erlang 27.3.4.11
rabbit@rmq-2: Tanzu RabbitMQ 4.3.0 on Erlang 27.3.4.11
rabbit@rmq-3: Tanzu RabbitMQ 4.3.0 on Erlang 27.3.4.11
CPU Cores
Node: rabbit@rmq-1, available CPU cores: 16
Node: rabbit@rmq-2, available CPU cores: 16
Node: rabbit@rmq-3, available CPU cores: 16
Maintenance status
Node: rabbit@rmq-1, status: not under maintenance
Node: rabbit@rmq-2, status: not under maintenance
Node: rabbit@rmq-3, status: not under maintenance
Alarms
(none)
Network Partitions
(none)
Listeners
Node: rabbit@rmq-1, interface: [::], port: 25672, protocol: clustering, purpose: inter-node and CLI tool communication
Node: rabbit@rmq-1, interface: [::], port: 5672, protocol: amqp, purpose: AMQP 0-9-1 and AMQP 1.0
Node: rabbit@rmq-1, interface: [::], port: 15672, protocol: http, purpose: HTTP API
Node: rabbit@rmq-2, interface: [::], port: 25672, protocol: clustering, purpose: inter-node and CLI tool communication
Node: rabbit@rmq-2, interface: [::], port: 5672, protocol: amqp, purpose: AMQP 0-9-1 and AMQP 1.0
Node: rabbit@rmq-2, interface: [::], port: 15672, protocol: http, purpose: HTTP API
Node: rabbit@rmq-3, interface: [::], port: 15672, protocol: http, purpose: HTTP API
Node: rabbit@rmq-3, interface: [::], port: 25672, protocol: clustering, purpose: inter-node and CLI tool communication
Node: rabbit@rmq-3, interface: [::], port: 5672, protocol: amqp, purpose: AMQP 0-9-1 and AMQP 1.0
Feature flags
Flag: classic_mirrored_queue_version, state: enabled
Flag: classic_queue_type_delivery_support, state: enabled
Flag: detailed_queues_endpoint, state: enabled
Flag: direct_exchange_routing_v2, state: enabled
Flag: drop_unroutable_metric, state: enabled
Flag: empty_basic_get_metric, state: enabled
Flag: feature_flags_v2, state: enabled
Flag: implicit_default_bindings, state: enabled
Flag: khepri_db, state: enabled
Flag: listener_records_in_ets, state: enabled
Flag: maintenance_mode_status, state: enabled
Flag: message_containers, state: enabled
Flag: message_containers_deaths_v2, state: enabled
Flag: quorum_queue, state: enabled
Flag: quorum_queue_non_voters, state: enabled
Flag: rabbit_exchange_type_local_random, state: enabled
Flag: rabbitmq_4.0.0, state: enabled
Flag: rabbitmq_4.1.0, state: enabled
Flag: rabbitmq_4.2.0, state: enabled
Flag: rabbitmq_4.3.0, state: enabled
Flag: restart_streams, state: enabled
Flag: stream_filtering, state: enabled
Flag: stream_queue, state: enabled
Flag: stream_sac_coordinator_unblock_group, state: enabled
Flag: stream_single_active_consumer, state: enabled
Flag: stream_update_config_command, state: enabled
Flag: tie_binding_to_dest_with_keep_while_cond, state: enabled
Flag: topic_binding_projection_v4, state: enabled
Flag: track_qq_members_uids, state: enabled
Flag: tracking_records_in_ets, state: enabled
Flag: user_limits, state: enabled
Flag: virtual_host_metadata, state: enabled
(任意で) クラスタ名を rabbit@rmq-1 から rmq-cluster に変更します。
sudo rabbitmqctl set_cluster_name rmq-cluster
変更されたことを確認します。
$ sudo rabbitmqctl cluster_status | grep "Cluster name:"
Cluster name: rmq-cluster
admin ユーザーを作成します。初期状態で設定されている guest ユーザーは localhost からのみ利用可能です。
sudo rabbitmqctl add_user admin 'VMware1!'
sudo rabbitmqctl set_user_tags admin administrator
sudo rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"
ユーザー一覧を確認します。
$ sudo rabbitmqctl list_users
Listing users ...
user tags
admin [administrator]
guest [administrator]
(任意で) guest ユーザーを削除します。
sudo rabbitmqctl delete_user guest
Management Pluginの有効化
次のコマンドを各ノード上で実行し、RabbitMQのManagement Pluginを有効化します。
sudo rabbitmq-plugins enable rabbitmq_management
リッスンしているポートを確認して15672が追加されたことを確認します。
$ sudo lsof -n -i -P | grep -i listen | grep rabbitmq
epmd 881 rabbitmq 3u IPv4 629997 0t0 TCP *:4369 (LISTEN)
epmd 881 rabbitmq 4u IPv6 629998 0t0 TCP *:4369 (LISTEN)
beam.smp 1024 rabbitmq 24u IPv4 627526 0t0 TCP *:25672 (LISTEN)
beam.smp 1024 rabbitmq 39u IPv6 627537 0t0 TCP *:5672 (LISTEN)
beam.smp 1024 rabbitmq 42u IPv4 661184 0t0 TCP *:15672 (LISTEN)
いずれかのノードのIPの15672ポートにブラウザでアクセスし、 admin ユーザーでログインします。次のような画面が表示されます。

動作確認
簡単なPythonプログラムで動作確認します。
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install pika
次の rabbitmq_cluster_check.py を作成してください。
#!/usr/bin/env python3
"""
RabbitMQ クラスタ動作確認スクリプト
- クラスタ内の任意のノードに接続 (フェイルオーバー)
- Quorum Queue を作成
- 指定数のメッセージを Publish
- 同じキューから Consume し、件数・内容を検証
- 成功時 exit 0 / 失敗時 exit 1
必要なパッケージ:
pip install pika
実行例 (TLS あり):
python3 rabbitmq_cluster_check.py \
--hosts rabbit01,rabbit02,rabbit03 \
--port 5671 \
--user admin --password 'YOUR_PASSWORD' \
--ca /etc/rabbitmq/certs/ca.crt \
--count 5 --cleanup
実行例 (mTLS / クライアント証明書あり):
python3 rabbitmq_cluster_check.py \
--hosts rabbit01,rabbit02,rabbit03 \
--port 5671 \
--user admin --password 'YOUR_PASSWORD' \
--ca /etc/rabbitmq/certs/ca.crt \
--cert /etc/rabbitmq/certs/client.crt \
--key /etc/rabbitmq/certs/client.key \
--count 5 --cleanup
実行例 (TLS なし):
python3 rabbitmq_cluster_check.py \
--hosts rabbit01,rabbit02,rabbit03 \
--port 5672 --no-tls \
--user admin --password 'YOUR_PASSWORD' \
--count 5 --cleanup
"""
import argparse
import ssl
import sys
import time
import uuid
import pika
def build_params(hosts, port, vhost, user, password,
use_tls, ca_file, skip_verify,
client_cert=None, client_key=None):
credentials = pika.PlainCredentials(user, password)
ssl_context = None
if use_tls:
ssl_context = ssl.create_default_context(cafile=ca_file)
if client_cert and client_key:
ssl_context.load_cert_chain(certfile=client_cert, keyfile=client_key)
if skip_verify:
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
params_list = []
for host in hosts:
kwargs = dict(
host=host,
port=port,
virtual_host=vhost,
credentials=credentials,
connection_attempts=2,
retry_delay=1,
socket_timeout=10,
heartbeat=30,
)
if use_tls:
kwargs["ssl_options"] = pika.SSLOptions(ssl_context, server_hostname=host)
params_list.append(pika.ConnectionParameters(**kwargs))
return params_list
def connect_with_failover(params_list):
last_err = None
for params in params_list:
try:
conn = pika.BlockingConnection(params)
print(f"[OK] Connected to {params.host}:{params.port}")
return conn
except Exception as e:
print(f"[WARN] Failed to connect to {params.host}: {e}")
last_err = e
raise RuntimeError(f"All nodes unreachable. Last error: {last_err}")
def main():
p = argparse.ArgumentParser(description="RabbitMQ cluster sanity check")
p.add_argument("--hosts", default="rabbit01,rabbit02,rabbit03",
help="comma-separated hostnames (default: rabbit01,rabbit02,rabbit03)")
p.add_argument("--port", type=int, default=5671,
help="AMQP port (default: 5671 for TLS, use 5672 for plain)")
p.add_argument("--vhost", default="/", help="vhost (default: /)")
p.add_argument("--user", default="admin", help="username (default: admin)")
p.add_argument("--password", required=True, help="password")
p.add_argument("--queue", default="test.quorum.check",
help="queue name (default: test.quorum.check)")
p.add_argument("--count", type=int, default=5, help="number of messages (default: 5)")
p.add_argument("--no-tls", action="store_true", help="disable TLS")
p.add_argument("--ca", default="/etc/rabbitmq/certs/ca.crt",
help="CA certificate path for TLS")
p.add_argument("--cert", default=None,
help="client certificate path for mutual TLS (mTLS)")
p.add_argument("--key", default=None,
help="client private key path for mutual TLS (mTLS)")
p.add_argument("--skip-verify", action="store_true",
help="skip TLS hostname/cert verification (NOT for production)")
p.add_argument("--cleanup", action="store_true",
help="delete queue after test")
args = p.parse_args()
hosts = [h.strip() for h in args.hosts.split(",") if h.strip()]
use_tls = not args.no_tls
print("=" * 60)
print(f" Target: {hosts} port={args.port} vhost={args.vhost}")
print(f" User: {args.user}")
print(f" Queue: {args.queue} (quorum)")
print(f" Messages: {args.count}")
print(f" TLS: {'enabled (CA=' + args.ca + ')' if use_tls else 'disabled'}")
print("=" * 60)
params_list = build_params(
hosts=hosts, port=args.port, vhost=args.vhost,
user=args.user, password=args.password,
use_tls=use_tls, ca_file=args.ca, skip_verify=args.skip_verify,
client_cert=args.cert, client_key=args.key,
)
# ---- Connect (with failover) ----
connection = connect_with_failover(params_list)
channel = connection.channel()
channel.confirm_delivery() # Publisher Confirms 有効化
# ---- Declare Quorum Queue ----
print(f"[INFO] Declaring quorum queue: {args.queue}")
channel.queue_declare(
queue=args.queue,
durable=True,
arguments={"x-queue-type": "quorum"},
)
# ---- Publish ----
run_id = uuid.uuid4().hex[:8]
print(f"[INFO] Publishing {args.count} messages (run_id={run_id})")
sent = []
for i in range(1, args.count + 1):
body = f"[{run_id}] msg-{i:03d} ts={int(time.time())}"
channel.basic_publish(
exchange="",
routing_key=args.queue,
body=body.encode("utf-8"),
properties=pika.BasicProperties(
delivery_mode=2, # persistent
content_type="text/plain",
),
mandatory=True,
)
sent.append(body)
print(f" -> SENT {body}")
# ---- Consume ----
print(f"[INFO] Consuming up to {args.count} messages")
received = []
# 今回送ったメッセージだけを対象にする (run_id でフィルタ)
for _ in range(args.count * 2): # 余裕をもってループ
method, _, body = channel.basic_get(queue=args.queue, auto_ack=False)
if method is None:
break
msg = body.decode("utf-8")
if run_id in msg:
received.append(msg)
channel.basic_ack(delivery_tag=method.delivery_tag)
print(f" <- RECV {msg}")
else:
# 別 run_id のメッセージは requeue
channel.basic_nack(delivery_tag=method.delivery_tag, requeue=True)
if len(received) == args.count:
break
# ---- Verify ----
missing = set(sent) - set(received)
success = (len(sent) == len(received)) and not missing
print("-" * 60)
print(f" Sent: {len(sent)}")
print(f" Received: {len(received)}")
if missing:
print(f" Missing: {len(missing)}")
for m in sorted(missing):
print(f" - {m}")
print("-" * 60)
# ---- Cleanup ----
if args.cleanup:
print(f"[INFO] Deleting queue: {args.queue}")
channel.queue_delete(queue=args.queue)
connection.close()
if success:
print("[PASS] RabbitMQ cluster check succeeded.")
sys.exit(0)
else:
print("[FAIL] RabbitMQ cluster check failed.")
sys.exit(1)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("[ABORT] Interrupted by user.")
sys.exit(130)
except Exception as e:
print(f"[ERROR] {e}")
sys.exit(1)
スクリプトを実行します。
python rabbitmq_cluster_check.py --hosts rmq-1,rmq-2,rmq-3 --port 5672 --user admin --password 'VMware1!' --count 5 --no-tls --cleanup
次のような出力になればOKです。
============================================================
Target: ['rmq-1', 'rmq-2', 'rmq-3'] port=5672 vhost=/
User: admin
Queue: test.quorum.check (quorum)
Messages: 5
TLS: disabled
============================================================
[OK] Connected to rmq-1:5672
[INFO] Declaring quorum queue: test.quorum.check
[INFO] Publishing 5 messages (run_id=0fb619ec)
-> SENT [0fb619ec] msg-001 ts=1776328534
-> SENT [0fb619ec] msg-002 ts=1776328534
-> SENT [0fb619ec] msg-003 ts=1776328534
-> SENT [0fb619ec] msg-004 ts=1776328534
-> SENT [0fb619ec] msg-005 ts=1776328534
[INFO] Consuming up to 5 messages
<- RECV [0fb619ec] msg-001 ts=1776328534
<- RECV [0fb619ec] msg-002 ts=1776328534
<- RECV [0fb619ec] msg-003 ts=1776328534
<- RECV [0fb619ec] msg-004 ts=1776328534
<- RECV [0fb619ec] msg-005 ts=1776328534
------------------------------------------------------------
Sent: 5
Received: 5
------------------------------------------------------------
[PASS] RabbitMQ cluster check succeeded.
TLSの有効化
ここまでは平文(AMQP 5672、Management UI 15672)で構成しました。このセクションでは、クライアント向け通信に続けてノード間通信も段階的にTLS化します。
- opensslで証明書(CA・サーバ・クライアント)を生成
- 全ノードに証明書を配置
- AMQPS (5671) を有効化
- Management UIをHTTPS (15671) 化
- ノード間通信(Erlang distribution)のTLS化
- mTLS(クライアント証明書の検証)を有効化
Note
rabbitmqctl や rabbitmq-diagnostics はAMQPではなくErlang distribution経由で通信します。そのため、AMQP (5671) のTLS化はCLIツールに影響しませんが、ノード間通信(25672ポート)をTLS化した後はCLIツール側にもTLS設定が必要になります(後述)。
TLS証明書の生成
rmq-1 上で openssl コマンドを使い、1つのCAと、3ノードで共用する1枚のサーバ証明書(SANに rmq-1, rmq-2, rmq-3 を持つ)、ノード間通信の client ロールおよび mTLS テスト用のクライアント証明書をまとめて生成します。鍵はRSA 4096、有効期限は3650日です。記事中で必要なすべてのケース(AMQPS / Management HTTPS / ノード間 distribution の server・client ロール / AMQP mTLS)をこの1回の発行作業でカバーします。
作業ディレクトリを作成します。
mkdir -p ~/certs && cd ~/certs
CAの秘密鍵と自己署名証明書を作成します。
# CA private key
openssl genrsa -out ca_key.pem 4096
chmod 600 ca_key.pem
# Self-signed CA certificate (10 years)
openssl req -x509 -new -key ca_key.pem -sha256 -days 3650 \
-subj "/O=RabbitMQ Lab/CN=RabbitMQ Lab CA" \
-out ca_certificate.pem
サーバ証明書用の設定ファイル server.cnf を作成します。subjectAltName (SAN) に3ノードのホスト名を列挙することで、1枚の証明書を全ノードで共用できるようにします。
cat <<'EOF' > server.cnf
[req]
distinguished_name = dn
prompt = no
[dn]
O = server
CN = rmq-cluster
[v3_server]
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = rmq-1
DNS.2 = rmq-2
DNS.3 = rmq-3
EOF
サーバ証明書(SAN付き、全ノード共用)を生成します。
# Server private key
openssl genrsa -out server_key.pem 4096
chmod 600 server_key.pem
# CSR
openssl req -new -key server_key.pem -config server.cnf -out server.csr
# Sign with CA (SAN/EKU are taken from the v3_server section of server.cnf)
openssl x509 -req -in server.csr -sha256 -days 3650 \
-CA ca_certificate.pem -CAkey ca_key.pem -CAcreateserial \
-extfile server.cnf -extensions v3_server \
-out server_certificate.pem
クライアント証明書用の設定ファイル client.cnf を作成します。ノード間通信の client ロールでは extendedKeyUsage = clientAuth が必要なため、サーバ証明書とは別の証明書として発行します。
cat <<'EOF' > client.cnf
[req]
distinguished_name = dn
prompt = no
[dn]
O = client
CN = rmq-client
[v3_client]
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
EOF
クライアント証明書を生成します。
# Client private key
openssl genrsa -out client_key.pem 4096
chmod 600 client_key.pem
# CSR
openssl req -new -key client_key.pem -config client.cnf -out client.csr
# Sign with CA
openssl x509 -req -in client.csr -sha256 -days 3650 \
-CA ca_certificate.pem -CAkey ca_key.pem -CAcreateserial \
-extfile client.cnf -extensions v3_client \
-out client_certificate.pem
生成された証明書のSANとEKUを確認します。
$ openssl x509 -in server_certificate.pem -noout -ext subjectAltName,extendedKeyUsage
X509v3 Extended Key Usage:
TLS Web Server Authentication
X509v3 Subject Alternative Name:
DNS:rmq-1, DNS:rmq-2, DNS:rmq-3
$ openssl x509 -in client_certificate.pem -noout -ext extendedKeyUsage
X509v3 Extended Key Usage:
TLS Web Client Authentication
CAとの連鎖検証を行います。両方とも OK であれば成功です。
$ openssl verify -CAfile ca_certificate.pem server_certificate.pem client_certificate.pem
server_certificate.pem: OK
client_certificate.pem: OK
~/certs ディレクトリに以下のファイルが揃います。以降のセクションではこれらを各ノードに配布します。
$ ls -lh ~/certs
-rw-r--r-- 1 toshiaki toshiaki 1.9K May 19 13:33 ca_certificate.pem
-rw-r--r-- 1 toshiaki toshiaki 41 May 19 13:33 ca_certificate.srl
-rw------- 1 toshiaki toshiaki 3.2K May 19 13:33 ca_key.pem
-rw-r--r-- 1 toshiaki toshiaki 222 May 19 13:33 client.cnf
-rw-r--r-- 1 toshiaki toshiaki 1.6K May 19 13:33 client.csr
-rw-r--r-- 1 toshiaki toshiaki 1.9K May 19 13:33 client_certificate.pem
-rw------- 1 toshiaki toshiaki 3.2K May 19 13:33 client_key.pem
-rw-r--r-- 1 toshiaki toshiaki 308 May 19 13:33 server.cnf
-rw-r--r-- 1 toshiaki toshiaki 1.6K May 19 13:33 server.csr
-rw-r--r-- 1 toshiaki toshiaki 2.0K May 19 13:33 server_certificate.pem
-rw------- 1 toshiaki toshiaki 3.2K May 19 13:33 server_key.pem
Note
ここでは秘密鍵をパスフレーズなしで生成しています。パスフレーズ付きの鍵を使う場合は、RabbitMQ側に ssl_options.password の設定が必要です。
証明書の配置
各ノードに /etc/rabbitmq/certs ディレクトリを作成し、以下のファイルを共通の名前で配置します。証明書は rabbitmq ユーザーから読める必要があります。
ca_certificate.pem— CA証明書(全ノード共通)server_certificate.pem/server_key.pem— サーバ証明書(SANにrmq-1,rmq-2,rmq-3を含む1枚を全ノード共通で使う)client_certificate.pem/client_key.pem— ノード間通信の client ロールおよび mTLS テスト用のクライアント証明書(全ノード共通)
3ノードとも完全に同じ証明書セットを配置するため、すべてのノードで同じ rabbitmq.conf / inter_node_tls.config を使えます。
Warning
sudo cp で /etc/rabbitmq/certs/ 配下に作成したファイルは root 所有・mode 600 になるため、rabbitmq ユーザーから読めません。必ず以下の chown と chmod まで通して実行してください。証明書ファイルが rabbitmq ユーザーで読めないと、RabbitMQ サーバ自体は起動するものの、CLI ツール (rabbitmqctl) や他ノードからの inter-node TLS dist が証明書をロードできずに失敗し、サーバログに TLS エラーすら残らない状態でハングします(後段の Khepri が timeout_waiting_for_leader を出すなど、原因が分かりにくい症状になります)。後から証明書を追加コピーした場合も chown / chmod を再実行してください。
rmq-1 での作業:
sudo mkdir -p /etc/rabbitmq/certs
sudo cp ~/certs/ca_certificate.pem /etc/rabbitmq/certs/ca_certificate.pem
sudo cp ~/certs/server_certificate.pem /etc/rabbitmq/certs/server_certificate.pem
sudo cp ~/certs/server_key.pem /etc/rabbitmq/certs/server_key.pem
sudo cp ~/certs/client_certificate.pem /etc/rabbitmq/certs/client_certificate.pem
sudo cp ~/certs/client_key.pem /etc/rabbitmq/certs/client_key.pem
sudo chown -R rabbitmq:rabbitmq /etc/rabbitmq/certs
sudo chmod 755 /etc/rabbitmq/certs
sudo chmod 640 /etc/rabbitmq/certs/ca_certificate.pem
sudo chmod 640 /etc/rabbitmq/certs/server_certificate.pem
sudo chmod 640 /etc/rabbitmq/certs/server_key.pem
sudo chmod 640 /etc/rabbitmq/certs/client_certificate.pem
sudo chmod 640 /etc/rabbitmq/certs/client_key.pem
rmq-2 と rmq-3 にも同じファイルを転送して配置します。サーバ証明書もクライアント証明書も全ノード共通なので、3ノードとも完全に同じファイルセットになります。以下は rmq-1 から rmq-2 へ転送する例です(rmq-3 も同様)。
# rmq-1上
scp ~/certs/ca_certificate.pem \
~/certs/server_certificate.pem \
~/certs/server_key.pem \
~/certs/client_certificate.pem \
~/certs/client_key.pem \
rmq-2:/tmp/
# rmq-2上
sudo mkdir -p /etc/rabbitmq/certs
sudo cp /tmp/ca_certificate.pem /etc/rabbitmq/certs/ca_certificate.pem
sudo cp /tmp/server_certificate.pem /etc/rabbitmq/certs/server_certificate.pem
sudo cp /tmp/server_key.pem /etc/rabbitmq/certs/server_key.pem
sudo cp /tmp/client_certificate.pem /etc/rabbitmq/certs/client_certificate.pem
sudo cp /tmp/client_key.pem /etc/rabbitmq/certs/client_key.pem
sudo chown -R rabbitmq:rabbitmq /etc/rabbitmq/certs
sudo chmod 755 /etc/rabbitmq/certs
sudo chmod 640 /etc/rabbitmq/certs/ca_certificate.pem
sudo chmod 640 /etc/rabbitmq/certs/server_certificate.pem
sudo chmod 640 /etc/rabbitmq/certs/server_key.pem
sudo chmod 640 /etc/rabbitmq/certs/client_certificate.pem
sudo chmod 640 /etc/rabbitmq/certs/client_key.pem
rm /tmp/ca_certificate.pem /tmp/server_certificate.pem /tmp/server_key.pem \
/tmp/client_certificate.pem /tmp/client_key.pem
鍵のパーミッションは 640(owner rabbitmq / group rabbitmq のみ読み取り可)にしているため、後の動作確認で作業ユーザーから /etc/rabbitmq/certs/*.pem を直接読みたい場合は、作業ユーザーを rabbitmq グループに追加しておきます。後述の mTLS の動作確認で作業ユーザーから秘密鍵を参照するため、テストを実行するノード(記事中では rmq-1)で実行しておきます。
sudo usermod -aG rabbitmq $(whoami)
グループの変更を現在のシェルに反映するため、いったんログアウト・ログインし直すか、newgrp rabbitmq を実行します。
$ id
uid=1000(toshiaki) gid=1000(toshiaki) groups=1000(toshiaki),10(wheel),989(rabbitmq)
Note
公開証明書 (ca_certificate.pem / server_certificate.pem / client_certificate.pem) は他者に渡しても問題ない情報のため 644 にしても構いません。秘密鍵 (server_key.pem / client_key.pem) は同一ホスト上の任意ユーザーにTLS資格情報を盗まれないよう、本記事では 640 にしています。
AMQPS (5671) の有効化
記事冒頭の systemctl status の出力で Config file(s): (none) だったとおり、初期状態では設定ファイルがありません。全ノードで /etc/rabbitmq/rabbitmq.conf を作成します。
cat <<'EOF' | sudo tee /etc/rabbitmq/rabbitmq.conf
listeners.ssl.default = 5671
ssl_options.cacertfile = /etc/rabbitmq/certs/ca_certificate.pem
ssl_options.certfile = /etc/rabbitmq/certs/server_certificate.pem
ssl_options.keyfile = /etc/rabbitmq/certs/server_key.pem
ssl_options.verify = verify_none
ssl_options.fail_if_no_peer_cert = false
EOF
Note
平文のAMQP (5672) も引き続きリッスンされます。平文を無効化したい場合は listeners.tcp = none を追加してください。本記事では後の動作確認で平文も使うため残しています。
全ノードでサービスを再起動します。
sudo systemctl restart tanzu-rabbitmq-server
リッスンしているポートを確認し、5671が追加されたことを確認します。
$ sudo lsof -n -i -P | grep -i listen | grep rabbitmq
epmd 881 rabbitmq 3u IPv4 629997 0t0 TCP *:4369 (LISTEN)
epmd 881 rabbitmq 4u IPv6 629998 0t0 TCP *:4369 (LISTEN)
beam.smp 3518 rabbitmq 24u IPv4 801487 0t0 TCP *:25672 (LISTEN)
beam.smp 3518 rabbitmq 42u IPv4 807112 0t0 TCP *:15672 (LISTEN)
beam.smp 3518 rabbitmq 43u IPv6 801517 0t0 TCP *:5672 (LISTEN)
beam.smp 3518 rabbitmq 44u IPv6 801523 0t0 TCP *:5671 (LISTEN)
openssl s_client でTLSハンドシェイクを確認します。Verify return code: 0 (ok) であればCA検証に成功しています。
$ openssl s_client -connect rmq-1:5671 -CAfile /etc/rabbitmq/certs/ca_certificate.pem </dev/null 2>/dev/null | grep -E 'subject=|Verify return code'
subject=O=server, CN=rmq-cluster
Verify return code: 0 (ok)
$ openssl s_client -connect rmq-2:5671 -CAfile /etc/rabbitmq/certs/ca_certificate.pem </dev/null 2>/dev/null | grep -E 'subject=|Verify return code'
subject=O=server, CN=rmq-cluster
Verify return code: 0 (ok)
$ openssl s_client -connect rmq-3:5671 -CAfile /etc/rabbitmq/certs/ca_certificate.pem </dev/null 2>/dev/null | grep -E 'subject=|Verify return code'
subject=O=server, CN=rmq-cluster
Verify return code: 0 (ok)
「動作確認」セクションで作成した rabbitmq_cluster_check.py を、TLS経由で実行します。--ca を指定するとTLSが有効になります(ポートは5671を指定)。
python rabbitmq_cluster_check.py \
--hosts rmq-1,rmq-2,rmq-3 --port 5671 \
--user admin --password 'VMware1!' \
--ca /etc/rabbitmq/certs/ca_certificate.pem \
--count 5 --cleanup
次のように TLS: enabled と表示され、[PASS] で終わればOKです。
============================================================
Target: ['rmq-1', 'rmq-2', 'rmq-3'] port=5671 vhost=/
User: admin
Queue: test.quorum.check (quorum)
Messages: 5
TLS: enabled (CA=/etc/rabbitmq/certs/ca_certificate.pem)
============================================================
[OK] Connected to rmq-1:5671
[INFO] Declaring quorum queue: test.quorum.check
[INFO] Publishing 5 messages (run_id=c834a3ea)
-> SENT [c834a3ea] msg-001 ts=1779166061
-> SENT [c834a3ea] msg-002 ts=1779166061
-> SENT [c834a3ea] msg-003 ts=1779166061
-> SENT [c834a3ea] msg-004 ts=1779166061
-> SENT [c834a3ea] msg-005 ts=1779166061
[INFO] Consuming up to 5 messages
<- RECV [c834a3ea] msg-001 ts=1779166061
<- RECV [c834a3ea] msg-002 ts=1779166061
<- RECV [c834a3ea] msg-003 ts=1779166061
<- RECV [c834a3ea] msg-004 ts=1779166061
<- RECV [c834a3ea] msg-005 ts=1779166061
------------------------------------------------------------
Sent: 5
Received: 5
------------------------------------------------------------
[INFO] Deleting queue: test.quorum.check
[PASS] RabbitMQ cluster check succeeded.
Management UIのHTTPS (15671) 化
全ノードの /etc/rabbitmq/rabbitmq.conf に以下を追記します。
cat <<'EOF' | sudo tee -a /etc/rabbitmq/rabbitmq.conf
management.ssl.port = 15671
management.ssl.cacertfile = /etc/rabbitmq/certs/ca_certificate.pem
management.ssl.certfile = /etc/rabbitmq/certs/server_certificate.pem
management.ssl.keyfile = /etc/rabbitmq/certs/server_key.pem
EOF
全ノードでサービスを再起動し、15671が追加されたことを確認します。
$ sudo systemctl restart tanzu-rabbitmq-server
$ sudo lsof -n -i -P | grep -i listen | grep rabbitmq | grep 15671
beam.smp 3757 rabbitmq 42u IPv4 822635 0t0 TCP *:15671 (LISTEN)
Note
平文のHTTP (15672) はそのまま残ります。閉じたい場合は management.tcp.ip = 127.0.0.1 でローカルからのみアクセス可能にするなどの対応を検討してください。
ブラウザで https://rmq-1:15671/ にアクセスし、admin ユーザーでログインします。(端末側の/etc/hostsも更新されている前提です。)
プライベートCAで署名した証明書なので、ブラウザは証明書の警告を表示します。

"rmq-1 にアクセスする(安全ではありません)"をクリックすれば、HTTPS対応されたManagement UIにアクセスできます。

なお、ca_certificate.pem をOSやブラウザの信頼済みCAに登録すると警告は出なくなります。
ノード間通信(Erlang distribution)のTLS (mTLS) 化
ここまではクライアント向け通信(AMQPS / Management UI)をTLS化しました。次に、ノード間通信(Erlang distribution、25672ポート)と、その上で通信するCLIツール(rabbitmqctl など)の通信もTLS化します。証明書は「証明書の配置」セクションで配置済みのもの (server_certificate.pem / server_key.pem および client_certificate.pem / client_key.pem) を再利用します。
Note
distribution では自ノードが他ノードに接続するときの クライアントロール にも証明書が必要(mTLS)で、そこには id-kp-clientAuth を持つ client 証明書を指定する必要があります。「TLS証明書の生成」セクションで発行したサーバ証明書は Extended Key Usage が id-kp-serverAuth のみで id-kp-clientAuth を持たないため、サーバ証明書をクライアントロールに流用すると相手ノードに invalid_ext_keyusage で拒否されます。そのため client_certificate.pem / client_key.pem を別途用意しています。
Warning
ノード間通信のTLS化は、クラスタ全体を一括で切り替える必要があります。TLS設定済みのノードと未設定(平文)のノードは通信できないため、ローリング再起動(1台ずつの再起動)はできません。全ノードの設定を変更してから、クラスタ全体を停止・起動します。
inter_node_tls.config の作成
全ノードで /etc/rabbitmq/inter_node_tls.config を作成します。これはErlangのtermファイルで、server(自ノードがサーバとして振る舞うとき)と client(自ノードがクライアントとして他ノードに接続するとき)の双方のTLSオプションを記述します。
cat <<'EOF' | sudo tee /etc/rabbitmq/inter_node_tls.config
%% coding: utf-8
[
{server, [
{cacertfile, "/etc/rabbitmq/certs/ca_certificate.pem"},
{certfile, "/etc/rabbitmq/certs/server_certificate.pem"},
{keyfile, "/etc/rabbitmq/certs/server_key.pem"},
{secure_renegotiate, true},
{verify, verify_peer},
{fail_if_no_peer_cert, true}
]},
{client, [
{cacertfile, "/etc/rabbitmq/certs/ca_certificate.pem"},
{certfile, "/etc/rabbitmq/certs/client_certificate.pem"},
{keyfile, "/etc/rabbitmq/certs/client_key.pem"},
{secure_renegotiate, true},
{verify, verify_peer}
]}
].
EOF
sudo chown rabbitmq:rabbitmq /etc/rabbitmq/inter_node_tls.config
sudo chmod 644 /etc/rabbitmq/inter_node_tls.config
Note
「TLS証明書の生成」セクションでは秘密鍵をパスフレーズなしで生成しているため password の指定は不要です。パスフレーズ付きの鍵を使う場合は server / client の両方に {password, "..."} を追加してください。
rabbitmq-env.conf の設定
全ノードで /etc/rabbitmq/rabbitmq-env.conf に以下を追記します。-proto_dist inet_tls でErlang distributionをTLS化し、-ssl_dist_optfile で先ほどの設定ファイルを指定します。-pa でssl applicationのパスをコードパスに追加します(distributionの起動時に必要になるため)。
Erlangのssl applicationのパスは環境によってバージョンが異なるので、まず確認します。
$ ls -d /usr/lib64/erlang/lib/ssl-*/ebin
/usr/lib64/erlang/lib/ssl-11.2.12.7/ebin
rabbitmq-env.conf に追記します。SERVER_ADDITIONAL_ERL_ARGS はRabbitMQサーバ本体に、RABBITMQ_CTL_ERL_ARGS は rabbitmqctl などのCLIツールに渡る引数です。
cat <<'EOF' | sudo tee -a /etc/rabbitmq/rabbitmq-env.conf
ERL_SSL_PATH=$(ls -d /usr/lib64/erlang/lib/ssl-*/ebin | tail -n1)
SERVER_ADDITIONAL_ERL_ARGS="-pa $ERL_SSL_PATH -proto_dist inet_tls -ssl_dist_optfile /etc/rabbitmq/inter_node_tls.config"
RABBITMQ_CTL_ERL_ARGS="-pa $ERL_SSL_PATH -proto_dist inet_tls -ssl_dist_optfile /etc/rabbitmq/inter_node_tls.config"
EOF
このように動的な設定にすることでErlangのマイナーアップデートで ssl のバージョンが上がってもパスが自動追随できます。
クラスタの停止・起動
全ノードで証明書と設定ファイル(inter_node_tls.config および rabbitmq-env.conf)を配置したら、全ノードでサービスを停止し、その後に 3ノードを並行起動 します。
# 全ノードで停止
sudo systemctl stop tanzu-rabbitmq-server
# 全ノードで起動(並行に。各ノードで実行)
sudo systemctl start --no-block tanzu-rabbitmq-server
Warning
RabbitMQ のメタデータストア (Khepri) は Raft で過半数(3ノードなら2ノード)の合意を必要とします。1ノードずつ順番に起動すると、先発ノードが quorum を待ってタイムアウト (timeout_waiting_for_leader) し、ブートに失敗します。3ノードを並行起動してください(例: ターミナルを3つ開いて同時に実行する、または各ノードで systemctl start --no-block を実行して起動コマンドが quorum 待ちでブロックしないようにする)。また前述のとおり、平文設定のままのノードと TLS 設定済みのノードは通信できないため、systemctl restart を1台ずつ実行することもできません。
確認
クラスタが3ノードで再形成されていることを確認します。rabbitmq-env.conf で RABBITMQ_CTL_ERL_ARGS を設定済みなので、rabbitmqctl はそのままTLS経由で通信します。このコマンドが成功すること自体が、CLIツール↔ノード間とノード↔ノード間のTLSハンドシェイクが成立している証拠です。
$ sudo rabbitmqctl cluster_status | grep -A4 "Running Nodes"
Running Nodes
rabbit@rmq-1
rabbit@rmq-2
rabbit@rmq-3
rabbitmq-diagnostics listeners の出力で、25672 ポートのプロトコルが clustering/ssl (TLSあり)になっていることを確認します。平文のままだとここが clustering になります。
$ sudo rabbitmq-diagnostics listeners
Asking node rabbit@rmq-1 to report its protocol listeners ...
Interface: [::], port: 15671, protocol: https, purpose: HTTP API over TLS (HTTPS)
Interface: [::], port: 25672, protocol: clustering/ssl, purpose: inter-node and CLI tool communication over TLS
Interface: [::], port: 5672, protocol: amqp, purpose: AMQP 0-9-1 and AMQP 1.0
Interface: [::], port: 5671, protocol: amqp/ssl, purpose: AMQP 0-9-1 and AMQP 1.0 over TLS
openssl s_client で25672ポートを確認します。fail_if_no_peer_cert を有効にしているため、クライアント証明書なしの接続はハンドシェイクに失敗します。
$ openssl s_client -connect rmq-1:25672 -CAfile /etc/rabbitmq/certs/ca_certificate.pem </dev/null; echo "exit=$?"
...
00FEEDFEFF7F0000:error:0A000410:SSL routines:ssl3_read_bytes:ssl/tls alert handshake failure:ssl/record/rec_layer_s3.c:916:SSL alert number 40
...
exit=1
Note
出力の途中に Verify return code: 0 (ok) という行が表示されますが、これは「openssl が受け取ったサーバ証明書のCA検証は成功した」という意味であり、TLSハンドシェイク全体が成功したという意味ではありません。TLS 1.3 ではサーバが先に証明書を送り、後から CertificateRequest を送るため、クライアントが証明書を返せない場合でも openssl は Verify return code: 0 (ok) を表示します。SSL alert number 40 (handshake failure) と非ゼロの終了コード (exit=1) でハンドシェイク全体が失敗したことを判断してください。
証明書を指定すると、TLSハンドシェイクが成功します(distributionのクライアントロール用には、配置済みの client_certificate.pem / client_key.pem を使います)。
$ openssl s_client -connect rmq-1:25672 \
-CAfile /etc/rabbitmq/certs/ca_certificate.pem \
-cert /etc/rabbitmq/certs/client_certificate.pem \
-key /etc/rabbitmq/certs/client_key.pem </dev/null 2>/dev/null | grep -E 'subject=|Verify return code'; echo "exit=$?"
subject=O=server, CN=rmq-cluster
Verify return code: 0 (ok)
exit=0
この時点ではAMQP (5671) はまだmTLSではないので、クライアント向けの動作確認は前述の「AMQPS (5671) の有効化」と同じく rabbitmq_cluster_check.py(--port 5671 --ca ...)で行えます。
Note
epmd(4369ポート、Erlang Port Mapper Daemon)はノード名とdistributionポートのマッピングのみを扱い、TLS化されません。機密データは流れませんが、必要であればファイアウォールでアクセス元を制限してください。また、/var/lib/rabbitmq/.erlang.cookie によるErlang cookie認証はTLSとは別に引き続き必要です(記事前半で配置済み)。
mTLS(クライアント証明書の検証)の有効化
最後に、AMQPクライアントの証明書をサーバ側で検証するようにします(mTLS)。全ノードの /etc/rabbitmq/rabbitmq.conf の ssl_options.verify と ssl_options.fail_if_no_peer_cert を次のように変更します。
ssl_options.verify = verify_peer
ssl_options.fail_if_no_peer_cert = true
次のコマンド
sudo sed -i \
-e 's/^\(ssl_options\.verify[[:space:]]*=[[:space:]]*\)verify_none/\1verify_peer/' \
-e 's/^\(ssl_options\.fail_if_no_peer_cert[[:space:]]*=[[:space:]]*\)false/\1true/' \
/etc/rabbitmq/rabbitmq.conf
変更後の /etc/rabbitmq/rabbitmq.conf 全体は次のようになります(Management UIのHTTPS設定も含む)。
listeners.ssl.default = 5671
ssl_options.cacertfile = /etc/rabbitmq/certs/ca_certificate.pem
ssl_options.certfile = /etc/rabbitmq/certs/server_certificate.pem
ssl_options.keyfile = /etc/rabbitmq/certs/server_key.pem
ssl_options.verify = verify_peer
ssl_options.fail_if_no_peer_cert = true
management.ssl.port = 15671
management.ssl.cacertfile = /etc/rabbitmq/certs/ca_certificate.pem
management.ssl.certfile = /etc/rabbitmq/certs/server_certificate.pem
management.ssl.keyfile = /etc/rabbitmq/certs/server_key.pem
Note
Management UI (HTTPS) もクライアント証明書を必須にする場合は management.ssl.verify = verify_peer と management.ssl.fail_if_no_peer_cert = true を追加します。ただしブラウザにクライアント証明書をインポートする必要があるため、本記事ではAMQPのみmTLSを有効化します。
全ノードでサービスを再起動します。
sudo systemctl restart tanzu-rabbitmq-server
クライアント証明書なしの接続が拒否されることを確認します。openssl s_client ではハンドシェイクに失敗します。
$ openssl s_client -connect rmq-1:5671 -CAfile /etc/rabbitmq/certs/ca_certificate.pem </dev/null; echo "exit=$?"
...
40A7XXXXXX:error:0A000412:SSL routines:ssl3_read_bytes:ssl/tls alert certificate required:...:SSL alert number 116
exit=1
rabbitmq_cluster_check.py をクライアント証明書なし(--ca のみ)で実行しても接続に失敗します。
$ python rabbitmq_cluster_check.py --hosts rmq-1,rmq-2,rmq-3 --port 5671 --user admin --password 'VMware1!' --ca /etc/rabbitmq/certs/ca_certificate.pem --count 5 --cleanup
============================================================
Target: ['rmq-1', 'rmq-2', 'rmq-3'] port=5671 vhost=/
User: admin
Queue: test.quorum.check (quorum)
Messages: 5
TLS: enabled (CA=/etc/rabbitmq/certs/ca_certificate.pem)
============================================================
[WARN] Failed to connect to rmq-1: StreamLostError: ("Stream connection lost: SSLError(1, '[SSL: TLSV13_ALERT_CERTIFICATE_REQUIRED] tlsv13 alert certificate required (_ssl.c:2651)')",)
[WARN] Failed to connect to rmq-2: StreamLostError: ("Stream connection lost: SSLError(1, '[SSL: TLSV13_ALERT_CERTIFICATE_REQUIRED] tlsv13 alert certificate required (_ssl.c:2651)')",)
[WARN] Failed to connect to rmq-3: StreamLostError: ("Stream connection lost: SSLError(1, '[SSL: TLSV13_ALERT_CERTIFICATE_REQUIRED] tlsv13 alert certificate required (_ssl.c:2651)')",)
[ERROR] All nodes unreachable. Last error: StreamLostError: ("Stream connection lost: SSLError(1, '[SSL: TLSV13_ALERT_CERTIFICATE_REQUIRED] tlsv13 alert certificate required (_ssl.c:2651)')",)
次に、クライアント証明書を指定すると接続できることを確認します。openssl s_client には -cert と -key を渡します。
$ openssl s_client -connect rmq-1:5671 \
-CAfile /etc/rabbitmq/certs/ca_certificate.pem \
-cert /etc/rabbitmq/certs/client_certificate.pem \
-key /etc/rabbitmq/certs/client_key.pem </dev/null 2>/dev/null | grep 'Verify return code'; echo "exit=$?"
Verify return code: 0 (ok)
exit=0
「動作確認」セクションで作成した rabbitmq_cluster_check.py は --cert と --key でクライアント証明書を指定できます。これを使って実行します。
python rabbitmq_cluster_check.py \
--hosts rmq-1,rmq-2,rmq-3 --port 5671 \
--user admin --password 'VMware1!' \
--ca /etc/rabbitmq/certs/ca_certificate.pem \
--cert /etc/rabbitmq/certs/client_certificate.pem \
--key /etc/rabbitmq/certs/client_key.pem \
--count 5 --cleanup
[PASS] ... で終わればmTLSの構成は完了です。
============================================================
Target: ['rmq-1', 'rmq-2', 'rmq-3'] port=5671 vhost=/
User: admin
Queue: test.quorum.check (quorum)
Messages: 5
TLS: enabled (CA=/etc/rabbitmq/certs/ca_certificate.pem)
============================================================
[OK] Connected to rmq-1:5671
[INFO] Declaring quorum queue: test.quorum.check
[INFO] Publishing 5 messages (run_id=50d0b2e9)
-> SENT [50d0b2e9] msg-001 ts=1779167063
-> SENT [50d0b2e9] msg-002 ts=1779167064
-> SENT [50d0b2e9] msg-003 ts=1779167064
-> SENT [50d0b2e9] msg-004 ts=1779167064
-> SENT [50d0b2e9] msg-005 ts=1779167064
[INFO] Consuming up to 5 messages
<- RECV [50d0b2e9] msg-001 ts=1779167063
<- RECV [50d0b2e9] msg-002 ts=1779167064
<- RECV [50d0b2e9] msg-003 ts=1779167064
<- RECV [50d0b2e9] msg-004 ts=1779167064
<- RECV [50d0b2e9] msg-005 ts=1779167064
------------------------------------------------------------
Sent: 5
Received: 5
------------------------------------------------------------
[INFO] Deleting queue: test.quorum.check
[PASS] RabbitMQ cluster check succeeded.
クラスタが3ノードのまま動作していることを確認します。今回のmTLS化はAMQP (5671) に対する設定なので、ノード間通信(25672)には影響しません。
$ sudo rabbitmqctl cluster_status | grep -A4 "Running Nodes"
Running Nodes
rabbit@rmq-1
rabbit@rmq-2
rabbit@rmq-3
以上で、クライアント向け通信(AMQPS / Management UI HTTPS)とノード間通信(Erlang distribution)のTLS化が完了しました。