セキュアなクラスターリンク
クラスターリンクは内部的に標準のMQTTを使用しています。各クラスターは、1つ以上のMQTTクライアントとしてピアに接続し、転送されたユーザーメッセージやコントロールプレーントラフィック(ルート同期や応答チャネル)を運びます。これらの接続はクラスター間のネットワーク境界を越えるため、接続を受け入れるリスナーは他のパブリック向けMQTTリスナーと同様の堅牢なセキュリティ対策が必要です。
以下の設定はすべての本番環境で推奨されます。各クラスターは、ピアからのリンク接続を受け入れるリスナーに対してこれを適用しなければなりません。接続先のクラスターがこれらのチェックを強制します。
ClientIDとユーザー名の計画
すべてのクラスターリンクMQTT接続は、リンクで設定されたclientidプレフィックスから派生したClientIDを使用します。EMQXは:msg:<node>などのサフィックスを付加して最終的なClientIDを形成します。プレフィックスは以下の要件を満たす必要があります。
- ソースクラスターに固有であること(例:
cluster.nameがAのクラスターではclink-A-)。 -のような区切り文字で終わり、^clink-A-のようなアンカー付き正規表現が、誤ってclink-AB-...のようなピアをマッチしないこと。- アプリケーションクライアントで使用されるプレフィックスと重複しないこと。
パスワード認証を使用する場合は、各ピアクラスターに専用のユーザー名(例:clink-user:A)を割り当て、通常のMQTTクライアントで再利用しないでください。
これらのClientIDとユーザー名は認証・認可ルールの識別子となるため、両レイヤーの設定前に決定してください。
認証の有効化
クラスターリンク接続を受け入れるリスナーで認証を有効にしてください。認証がない場合、リスナーに到達可能な任意の者がピアクラスターを偽装し、$LINK/コントロール名前空間にトラフィックを注入してクラスター間通信を妨害または盗聴できます。
クラスターリンクで一般的に使われる認証方式は以下の通りです。
- TLS相互認証(mTLS):ピアクラスターが管理するCA発行のクライアント証明書を提示し、リスナー側で
verify = verify_peerおよびfail_if_no_peer_cert = trueで検証します。これはトランスポート層でピアの身元を固定し、認証チェックを兼ねるため最も強力です。パスワード認証は不要になります。X.509証明書認証を参照してください。 - ユーザー名とパスワード:リンクに
usernameとpasswordを設定し、ピアのリスナーに対応する認証器を構成します。資格情報は安全に保管し、定期的にローテーションしてください。
両者を組み合わせて、トランスポート層でmTLSを使用し、その上にパスワード認証を重ねることも可能です。
信頼できないネットワーク(パブリックインターネット、クロスクラウドピアリング、パートナーネットワーク)を経由するリンクにはTLSが必須です。mTLSはトランスポート層でピアクラスターの身元を固定し、上記の資格情報チェックを補完します。
サポートされる認証方式の一覧は認証の概要を参照してください。リンク接続自体のTLS設定はMQTT接続の設定に記載されています。
認可の有効化
認証後、クラスターリンククライアントは$LINK/名前空間に制限され、他のクライアントがこれを使用することは許可されてはなりません。この境界がなければ、認証済みだが無関係なクライアントが偽造されたルート更新や転送メッセージをリンクに注入できます。
ピアクラスターは以下のコントロールトピックでローカルブローカーと通信します。<Cluster>はピア側のcluster.name(リンクを開始した側で設定された値)であり、トピック内にそのまま現れ、ワイルドカードや実行時置換ではありません。<Actor>はレプリケーションアクターごとに割り当てられる内部サブ識別子で、ルールでは+でマッチします。
| 操作 | トピック | 用途 |
|---|---|---|
| パブリッシュ | $LINK/cluster/msg/<Cluster> | 転送されたユーザーメッセージ |
| パブリッシュ | $LINK/cluster/route/<Cluster> | ルート(サブスクリプション)同期 |
| サブスクライブ | $LINK/cluster/resp/<Cluster>/<Actor> | ローカルブローカーからの応答 |
ACL設定例
以下の例はACLファイルソースを使用しています。他の認可器でも同様のルールが適用されます。
すべての現在および将来のコントロールトピックをカバーするワイルドカード$LINK/#に対してパブリッシュとサブスクライブを許可することが推奨されます。これによりEMQXのアップグレード時にルールを更新する必要がありません。
このブローカーがcluster.nameがAとCの2つのピアクラスターからリンクを受け入れ、各ピアのリンク設定でclientidをそれぞれclink-A-とclink-C-にしているとします。以下のルールは各ピアに$LINK/名前空間の使用を許可し、他のクライアントのアクセスを拒否し、最後にデフォルト拒否で関連しないクライアントのパブリッシュやサブスクライブを防ぎます。
%% 各ピアクラスターに$LINKコントロール名前空間の使用を許可
{allow, {clientid, {re, "^clink-A-"}}, all, ["$LINK/#"]}.
{allow, {clientid, {re, "^clink-C-"}}, all, ["$LINK/#"]}.
%% その他のクライアントによる$LINK名前空間へのアクセスを拒否
{deny, all, all, ["$LINK/#"]}.
%% ... アプリケーションのallowルールをここに追加 ...
%% キャッチオール:先のallowにマッチしないものはすべて拒否
{deny, all}.キャッチオールの{deny, all}は、認可器のdeny-by-default設定と組み合わせて使用し、マッチしない認可チェックを閉じた状態で失敗させます。
authorization {
no_match = deny
}ワイルドカードよりも許可リストを明示的に列挙したい場合(より制限的ですが将来のEMQXバージョンで新しいコントロールトピックが追加された際に手動で追加が必要になるため脆弱)、同じ2つのピアAとCに対する等価なルールは以下のようになります。
{allow, {clientid, {re, "^clink-A-"}}, publish, ["$LINK/cluster/msg/A", "$LINK/cluster/route/A"]}.
{allow, {clientid, {re, "^clink-A-"}}, subscribe, ["$LINK/cluster/resp/A/+"]}.
{allow, {clientid, {re, "^clink-C-"}}, publish, ["$LINK/cluster/msg/C", "$LINK/cluster/route/C"]}.
{allow, {clientid, {re, "^clink-C-"}}, subscribe, ["$LINK/cluster/resp/C/+"]}.
{deny, all}.トピック表の各<Cluster>はピアの実際のcluster.name(ここではAとC)に置き換えられ、各ClientID正規表現はピアのclientidフィールドに設定したプレフィックスです。これら2つの値は独立しており、新しいピアを命名する際は自分で同期を保つ必要があります。
利用可能な認可ソースと設定オプションについては認可の概要を参照してください。