セキュアなクラスターリンク
クラスターリンクは内部的に標準の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/コントロール名前空間にトラフィックを注入してクラスター間通信を妨害または盗聴できます。
クラスターリンクで一般的に使われる認証方式は以下の2つです。
- TLS相互認証(mTLS):ピアクラスターは管理下のCA発行のクライアント証明書を提示し、リスナーは
verify = verify_peerおよびfail_if_no_peer_cert = trueで検証します。これはトランスポート層でピアのIDを固定し、認証チェックを兼ねるため最も強力です。パスワード認証は不要になります。X.509証明書認証を参照してください。 - ユーザー名とパスワード:リンクに
usernameとpasswordを設定し、ピア側リスナーに対応する認証器を構成します。資格情報は安全に保管し、定期的にローテーションしてください。
両者を組み合わせることも可能で、トランスポート層でmTLSを使い、その上にパスワード認証を重ねる形です。
信頼できないネットワーク(パブリックインターネット、クラウド間ピアリング、パートナーネットワーク)を経由するリンクではTLSが必須です。mTLSはさらにトランスポート層でピアクラスターのIDを固定し、上記の資格情報チェックを補完します。
サポートされる認証方式の全リストは認証の概要を参照してください。リンク接続自体のTLS設定はMQTT接続の設定に記載があります。
認可の有効化
認証後、クラスターリンククライアントは$LINK/名前空間に制限される必要があり、他のクライアントがこれを使用することは許可されてはいけません。この境界がなければ、認証済みだが無関係なクライアントが偽造されたルート更新や転送メッセージをリンクに注入できます。
ピアクラスターは以下のコントロールトピックでローカルブローカーと通信します。<Cluster>はピア自身のcluster.name(リンクを開始した側の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/#"]}.
%% ... アプリケーションの許可ルールをここに追加 ...
%% キャッチオール:先の許可にマッチしないものはすべて拒否
{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つの値は独立しており、新しいピアを命名する際は同期を保つ必要があります。
利用可能な認可ソースと設定オプションについては、認可の概要を参照してください。