Skip to content

認可

EMQXにおける認可とは、MQTTクライアントのパブリッシュ/サブスクライブ操作に対する権限管理を指します。クライアントがパブリッシュ/サブスクライブ操作を行う際、EMQXは特定の手順に従うか、ユーザー指定のクエリ文を使用して、設定されたデータソースからクライアントの権限リストを照会します。照会結果に基づき、EMQXは現在の操作を許可または拒否します。

クライアントの単一の権限データは以下の要素で構成されます。

権限クライアント操作操作の詳細
許可/拒否クライアントID/ユーザー名/IPパブリッシュ/サブスクライブ/パブリッシュ・サブスクライブトピック/QoS/保持メッセージ

TIP

EMQX 5.1.1以降、操作の詳細におけるQoSおよび保持メッセージのチェックがサポートされています。

クライアントの権限リストは、事前に特定のデータソース(データベース、ファイルなど)に保存しておく必要があります。対応するデータレコードを更新することで、実行時にリストを更新可能です。

EMQXではデフォルトでファイルベースのオーソライザーが設定されており、そのまま利用できます。認可はACLファイルに設定された事前定義ルールに基づいて処理されます。

データストレージオブジェクトとの統合

EMQXの認可機構は、組み込みデータベース、ファイル、MySQL、PostgreSQL、MongoDB、Redisなど、多様なデータストレージオブジェクトとの統合をサポートしています。REST APIやEMQXダッシュボードを通じて権限データを管理可能です。

さらに、ユーザーが開発したHTTPサービスに接続し、異なる認可要件に対応することも可能です。

バックエンドのデータストレージに応じて、以下のような種類のEMQXオーソライザーが存在します。各オーソライザーにはそれぞれ独自の設定オプションがあります。詳細は表中のリンクを参照してください。

データベース説明
ACLファイルファイルに設定された静的ルールによる認可
組み込みデータベース組み込みデータベースをルールストレージとした認可
MySQLMySQLをルールストレージとした認可
PostgreSQLPostgreSQLをルールストレージとした認可
MongoDBMongoDBをルールストレージとした認可
RedisRedisをルールストレージとした認可
LDAPLDAPディレクトリをルールストレージとした認可
HTTP外部HTTPサービスによる認可

以下はEMQXのMySQLオーソライザーの設定例です。

例:

bash
{

    type = mysql
    database = "mqtt"
    username = "root"
    password = "public"

    query = "SELECT permission, action, topic FROM mqtt_acl WHERE username = ${username}"
    server = "10.12.43.12:3306"
}

認可チェーン

EMQXでは、単一のオーソライザーではなく複数のオーソライザーを設定して認可チェーンを作成し、認可処理の柔軟性を高めることができます。EMQXはチェーン内のオーソライザーの順序に従って認可を順次実行します。認可チェーンが設定されている場合、最初のオーソライザーで該当する認可情報が取得できないときは、次のオーソライザーに切り替えて処理を続行します。

オーソライザーはプリコンディション(前提条件)もサポートしています。オーソライザーにpreconditionが設定されている場合、EMQXはオーソライザーのデータソースを呼び出す前にその条件式を評価します。条件式がtrueと評価された場合のみオーソライザーを呼び出します。trueでない場合や評価時にエラーが発生した場合は、そのオーソライザーをスキップし、認可チェーン内の次の有効なオーソライザーに処理を移します。

認可チェックの処理フローは以下の通りです。

  1. 現在のオーソライザーにpreconditionが設定されている場合、EMQXはまず条件式を評価します。結果がtrueでなければ、そのオーソライザーをスキップします。

  2. EMQXがクライアントの権限情報を正常に取得できた場合、クライアントの操作と取得した権限リストを照合します。

    • 一致すれば、権限設定に基づいて操作を許可または拒否します。
    • 一致しなければ、次のオーソライザーに切り替えて処理を続行します。
  3. EMQXがクライアントの権限情報を取得できなかった場合、他に設定されたオーソライザーがあるか確認します。

    • ある場合は、次のオーソライザーに切り替えて処理を続行します。
    • 最後のオーソライザーの場合は、no_matchの設定に従ってクライアント操作を許可または拒否します。

注意

認可に問題が生じないよう、必要に応じてACLファイルオーソライザーを無効化または削除してください。ACLファイルオーソライザーはデフォルトで末尾に{allow, all}があり、すべての認可要求を許可してしまいます。

オーソライザーの順序変更方法や実行中のメトリクス確認方法については、オーソライザーの管理を参照してください。

オーソライザーのプリコンディション

EMQX 6.3以降、各オーソライザーにプリコンディションを割り当て、特定の認可リクエストに対して呼び出すかどうかを制御できます。

プリコンディションはVariform式で、listenerusernameclientidactiontopicなどのクライアントおよび認可リクエスト情報を評価します。式がtrueと評価されなければ、そのオーソライザーはスキップされます。

例えば、ビジネスラインやクライアント属性、パブリッシュ/サブスクライブの操作、トピック範囲に基づいて認可リクエストを異なるバックエンドに振り分けることが可能です。空のpreconditionはプリコンディションなしを意味し、認可チェーン内の位置に従って通常通り実行されます。

preconditionで利用可能なクライアント変数は以下の通りです。

  • username: クライアントのユーザー名
  • clientid: クライアントID
  • client_attrs.*: クライアント属性(例: client_attrs.tenant)。クライアント属性の詳細はMQTTクライアント属性を参照してください。
  • cert_common_name: クライアントTLS証明書のCommon Name(CN)
  • cert_subject: クライアントTLS証明書のSubject
  • peersni: TLSクライアントが送信したSNI(Server Name Indication)
  • listener: クライアントが使用するリスナーID(例: tcp:default
  • zone: クライアントに関連付けられた設定ゾーン

認可リクエスト変数は以下の通りです。

  • action: 現在の認可アクション。値はpublishまたはsubscribe
  • topic: 現在チェック中のパブリッシュトピックまたはサブスクリプショントピックフィルター

以下の例はpreconditionに関するフィールドのみを示しています。HTTPオーソライザーはordersビジネスクライアントのパブリッシュ要求のみ処理し、Redisオーソライザーはdevices/${clientid}/#トピックフィルターにマッチする要求のみ処理します。

hcl
authorization {
  sources = [
    {
      type = http
      precondition = "iif(str_eq(client_attrs.biz, 'orders'), str_eq(action, 'publish'), false)"
      ...
    },
    {
      type = redis
      precondition = "topic_match(topic, topic_join(['devices', clientid, '#']))"
      ...
    }
  ]
}

この例の説明:

  • iif(str_eq(client_attrs.biz, 'orders'), str_eq(action, 'publish'), false): クライアント属性client_attrs.bizordersであり、現在の認可アクションがpublishの場合にtrueとなる式。
  • topic_match(topic, topic_join(['devices', clientid, '#'])): 現在の認可リクエストのトピックがdevices/${clientid}/#トピックフィルターにマッチする場合にtrueとなる式。

クライアント認可キャッシュ

EMQXはセッションベースの認可データキャッシュ機構を提供しています。このキャッシュはクライアントのセッション状態に認可結果を保存し、同一接続中の認可ルール評価の繰り返しを減らします。クライアント認可キャッシュ機構は、クライアントのパブリッシュ/サブスクライブ操作に対する権限チェックの効率を向上させ、多数のクライアント要求による認可データバックエンドへのアクセス負荷を軽減します。

クライアント認可キャッシュの動作

クライアントが接続しパブリッシュ/サブスクライブ操作を行う際:

  1. EMQXは現在のセッションに保存された認可キャッシュを確認します。
  2. セッションキャッシュに一致するルールがあれば、それを直接使用します。
  3. キャッシュされたルールが存在しない(または期限切れの場合)、EMQXは設定されたオーソライザーを用いて完全な認可チェックを実行します。
  4. 結果は接続中の再利用のためセッションにキャッシュされます。

TIP

キャッシュはクライアントセッション固有であり、クライアントが切断または再接続するとクリアされます。

ダッシュボードでのクライアント認可キャッシュ設定

EMQXダッシュボードでクライアント認可キャッシュを有効化・設定できます。

  1. アクセス制御 -> 認可 -> 設定 に移動します。

  2. 以下のオプションを設定します。

    項目名説明
    キャッシュを有効にするクライアントセッションごとの認可キャッシュを有効/無効に切り替えます。
    キャッシュの最大件数クライアントごとのキャッシュエントリ最大数。デフォルトは32
    キャッシュの有効期限各キャッシュエントリの有効期間。デフォルトは1分
    除外トピックキャッシュ無効化対象のトピックリスト。
    マッチしない場合の動作オーソライザーがマッチしなかった場合の動作。allow(許可)/deny(拒否)。デフォルトはallow
    拒否時の動作操作拒否時の動作。ignore(操作を無視)/disconnect(クライアント切断)。デフォルトはignore
    キャッシュクリアアクティブなセッション認可キャッシュを手動で全てクリアするボタン。
  3. 保存をクリックして設定を反映します。

これらの設定は設定ファイルでも可能です。詳細は設定ファイルを参照してください。

TIP

適切に設定すればキャッシュはパフォーマンスを大幅に向上させます。システムのパフォーマンスに応じて適宜調整することを推奨します。

外部リソースキャッシュ

セッションベースのキャッシュに加え、EMQXはMySQL、MongoDB、Redisなどの外部バックエンドから取得した認可結果をノードレベルでキャッシュする機能も備えています。これによりリモートデータソースへのアクセスを減らし、パフォーマンスを向上させます。

注意

外部リソースキャッシュは外部データソースにのみ適用されます。組み込みデータベースやファイルベースのオーソライザーには適用されません。

外部リソースキャッシュの動作

パブリッシュ/サブスクライブ操作が外部バックエンドへのクエリをトリガーした際:

  1. EMQXは外部リソースキャッシュ(ノード全体で共有)を確認します。
  2. キャッシュに有効な結果があれば、キャッシュヒットとなり外部バックエンド呼び出しは行われません。
  3. 結果がなければ、キャッシュミスとなり外部バックエンドに問い合わせます。
  4. バックエンドから返された結果はキャッシュに保存され、キャッシュ挿入メトリクスが増加します。

注意

セッションベースの認可キャッシュと異なり、外部リソースキャッシュはノード全体で共有され、クライアントセッションを跨いで持続します。

外部リソースキャッシュの有効化と設定

EMQXダッシュボードで外部リソースキャッシュを有効化・設定できます。

  1. アクセス制御 -> 認可 に移動します。

  2. 右上の 外部リソースキャッシュ設定 ボタンをクリックすると、右側からパネルが表示されます。

  3. パネル内の 外部リソースキャッシュを有効にする ボタンで機能をオン/オフ切り替えます。有効化後、以下の設定を行います。

    項目名説明
    キャッシュ最大件数ノードごとのキャッシュエントリ最大数。デフォルトは1,000,000
    最大メモリ使用量キャッシュのメモリ使用上限。デフォルトは100 MB
    キャッシュTTLキャッシュエントリの有効期間。デフォルトは1分
  4. 更新をクリックして設定を反映します。

これらの設定はクラスター全体に適用され、全ノードで一貫した動作を保証します。

外部リソースキャッシュの状態監視

キャッシュメトリクスを表示し、リアルタイムで使用状況を監視するには:

  1. External Resource Cache Settings の横にある矢印をクリックし、External Resource Cache Status を選択します。サイドパネルが表示され、キャッシュメトリクスが確認できます。
  2. ドロップダウンメニューを使って、ノード単位またはクラスター全体のメトリクスを表示できます。

メトリクスには以下が含まれます:

  • Memory Usage:キャッシュが現在使用している合計メモリ量。
  • Cache Entries:保存されているキャッシュ結果の総数。
  • Cache Hits:EMQXがキャッシュ内で有効な結果を見つけ、外部バックエンドへの呼び出しを回避した回数。
    • 表示されるメトリクス:現在のレート、5分間平均、最大レート
  • Cache Misses:EMQXがキャッシュ内で結果を見つけられず、バックエンドクエリが発生した回数。
    • 表示されるメトリクス:現在のレート、5分間平均、最大レート
  • Cache Inserts:ミス後にキャッシュに新しい結果が追加された回数。
    • 表示されるメトリクス:現在のレート、5分間平均、最大レート

パネル下部にはノードリストがあり、クラスター内の各ノードごとの Memory UsageCache EntriesCache Hits の概要を確認できます。

パネル右上のボタンで統計情報の更新やリセットが可能です。

認可プレースホルダー

EMQXオーソライザーは設定内でプレースホルダーを使用可能です。認可処理時にこれらは実際のクライアント情報に置換され、現在のクライアントにマッチするクエリやHTTPリクエストを構築します。

有効なプレースホルダーは${PATH.TO.VALUE}形式で、PATH.TO.VALUEはオブジェクト内の値へのドット区切りパスです。使用可能な文字は英数字、ドット(.)、アンダースコア(_)です。サポート外の文字を含むプレースホルダーはそのままのテキストとして扱われます。

データクエリ内のプレースホルダー

プレースホルダーはクエリ文の構築に使用されます。例えば、EMQXのMySQLオーソライザーのデフォルトクエリSQLは${username}プレースホルダーを使用しています。

sql
SELECT action, permission, topic FROM mqtt_acl where username = ${username}

クライアント(名前:emqx_u)が接続要求を送ると、構築されるクエリ文は以下のようになります。

sql
SELECT action, permission, topic FROM mqtt_acl where username = 'emqx_u'

クエリ文でサポートされるプレースホルダーは以下の通りです。

  • ${username}: 実行時にユーザー名に置換されます。ユーザー名はCONNECTパケットのUsernameフィールドから取得されます。peer_cert_as_usernameが有効な場合は証明書のフィールドや内容で上書きされます。
  • ${clientid}: 実行時にクライアントIDに置換されます。通常はCONNECTパケットで明示的に指定されます。use_username_as_clientidpeer_cert_as_clientidが有効な場合はユーザー名や証明書のフィールド・内容で上書きされます。
  • ${peerhost}: 実行時にクライアントのIPアドレスに置換されます。EMQXはProxy Protocolをサポートしており、TCPプロキシやロードバランサーの背後にある場合でも実際のIPアドレスを取得可能です。
  • ${peername}: 実行時にクライアントのIPアドレスとポートに置換され、IP:PORT形式になります。
  • ${cert_common_name}: 実行時にクライアントTLS証明書のCommon Nameに置換されます。ロードバランサーがTCPリスナーにクライアント証明書情報を送る場合はProxy Protocol v2の利用を推奨します。
  • ${cert_subject}: 実行時にクライアントTLS証明書のSubjectに置換されます。ロードバランサーがTCPリスナーにクライアント証明書情報を送る場合はProxy Protocol v2の利用を推奨します。
  • ${client_attrs.NAME}: クライアント属性。NAMEは事前設定された属性名に置換されます。クライアント属性の詳細はMQTTクライアント属性を参照してください。
  • ${zone}: 実行時にクライアントのゾーンに置換されます。${zone}プレースホルダーは認可テンプレート内で直接使用可能です。ゾーン設定の詳細はゾーンオーバーライドを参照してください。

トピック内のプレースホルダー

EMQXはトピック内でもプレースホルダーを使用でき、動的トピックをサポートします。サポートされるプレースホルダーは以下の通りです。

  • ${clientid}
  • ${username}
  • ${client_attrs.NAME}: クライアント属性。NAMEmqtt.client_attrs_initで設定された属性名抽出ルールに置換されます。

プレースホルダーはトピックのセグメントとして使用可能です。例: a/b/${username}/c/d

EMQX 6.3.0以降、認可トピックテンプレートに補間される値の検証が行われます。デフォルトでは、これらの値にトピックレベル区切り文字(/)やMQTTトピックフィルターのワイルドカード(+#)は含められません。この制限はテンプレート内に直接記述された区切り文字やワイルドカードには適用されません。

例として、ユーザー名がaliceの場合、tenant/${username}/#tenant/alice/#に展開されます。ユーザー名がtenant/alice+の場合、補間値に許可されていない文字が含まれるためテンプレートの展開はできません。

補間値に許可されていない文字が含まれる場合、EMQXは有効なセキュリティプロファイルに従い認可ルールを処理します。

  • legacyプロファイルではルールはマッチせず、残りの認可ルールやソースで処理が継続されます。
  • hardenedプロファイルではパブリッシュまたはサブスクライブ操作を拒否します。authorization.ignore_backend_failurestrueの場合はルールをマッチしないものとして扱います。

EMQX 6.3.0のデフォルトセキュリティプロファイルはlegacyで、authorization.ignore_backend_failuresfalseです。6.3.0にアップグレード後、補間値に許可されていない文字を含むルールはデフォルトのlegacyプロファイル下でマッチしなくなります。最終的な結果は残りの認可ルール、認可ソース、authorization.no_matchの設定に依存します。アップグレード前にこれらの設定を確認し、期待するフォールバック動作を確認してください。

authorization.topic_template_allow設定は補間値に許可する文字を制御します。すべての設定はデフォルトでfalseです。

hocon
authorization.topic_template_allow {
  plus = false
  hash = false
  slash = false
}

対応する文字を含める必要がある場合のみtrueに設定してください。これらを有効にすると、クライアント由来の値が認可ルールのトピックフィルターを広げる可能性があります。ユーザー名、クライアントID、クライアント属性の値はトピックテンプレートで使用する前に検証してください。

プレースホルダーの補間を避けるには、EMQX 5.4以降、$${$}とエスケープできます。例: t/${$}{username}は補間されず文字通りt/${username}として扱われます。

TIP

クエリ文でeq構文を使用する場合、eqの後のトピックはプレースホルダー補間をサポートしない点に注意してください。この挙動は将来のバージョンで変更される可能性があります。

eq構文はトピックフィルターと完全一致するものをマッチさせるためのもので、フィルターにマッチする任意のトピックをマッチさせるものではありません。例えば、eq t/#t/#にのみマッチし、t/1t/2にはマッチしません。

認可チェックの優先度

キャッシュや認可チェッカーに加え、認可結果は認証フェーズでのスーパーユーザーロールと権限セットにも影響されます。

スーパーユーザーの場合、すべての操作は認可チェックをスキップします。アクセス制御リスト(ACL)が設定されている場合は、EMQXは認可チェッカー実行前にクライアントの権限データを優先します。優先度は以下の通りです。

bash
スーパーユーザー > 権限データ > 認可チェック

認可機構の設定

EMQXは認可を設定する方法として、ダッシュボード、設定ファイル、HTTP APIの3つを提供しています。

ダッシュボードでの認可設定

EMQXダッシュボードは直感的にEMQXオーソライザーを設定できるインターフェースで、関連パラメータの設定、動作状況の確認、認可チェーン内の位置調整が可能です。

ダッシュボードによる認証設定

設定ファイルでの認可設定

設定ファイルのauthorizationフィールドで認可を設定することも可能です。一般的な設定構造は以下の通りです。

bash
authorization {
  sources = [
    { ...   },
    { ...   }
  ]
  no_match = deny
  deny_action = ignore
  cache {
    max_size = 32
    excludes = ["t/1", "t/2"]
    ttl = 1m
  }
}

各項目の説明:

  • sources(任意):順序付き配列。各要素は対応するオーソライザーのデータソースを定義します。詳細な設定は各オーソライザーの設定ファイルを参照してください。

    • sources[].precondition:オプションのVariform式。オーソライザー呼び出し前にスキップ判定に使用。空の場合はプリコンディションなし。
  • no_match:設定されたオーソライザーのいずれも認可ルールを見つけられなかった場合のデフォルト動作。allowまたはdenyを指定可能。EMQX 6.0以降のデフォルトはdeny

  • deny_action:パブリッシュ/サブスクライブ操作が拒否された場合の次の動作。ignoreまたはdisconnectを指定可能。デフォルトはignoreignoreは操作を静かに無視し、disconnectはクライアント接続を切断。

  • cache:クライアント認可キャッシュの設定。内容は以下の通り。

    • cache.enable:クライアント認可キャッシュを有効にするかどうか。デフォルトはtrue。JWTパケットのみで認可する場合はfalse推奨。

    • cache.max_size:キャッシュ内の最大要素数。デフォルトは32。超過時は古いレコードから削除。

    • cache.excludes:キャッシュ生成を除外するトピックリスト。デフォルトは空リスト[]

    • cache.ttl:キャッシュの有効期間。デフォルトは1m(1分)。

TIP

信頼できないネットワークやパブリックネットワークに公開するブローカーでは、deny_actiondisconnectに設定すると、同一接続上での不正なパブリッシュやサブスクライブ試行を停止できます。フラッピング検出と組み合わせると、繰り返し再接続して認可拒否を引き起こすクライアントを一定期間自動で遮断可能です。

deny_actionはグローバル設定でリスナーごとには設定できません。また、正当なクライアントの拒否操作も切断するため、クライアントが通常は許可されたトピックのみで操作する場合に使用してください。正常な再接続の嵐で誤遮断しないよう、フラッピング検出の閾値調整も推奨します。

HTTP APIでの認可設定

認可管理用のAPIエンドポイントは以下の通りです。

  • /api/v5/authorization/settings:一般パラメータ、no_matchdeny_actioncacheの管理
  • /api/v5/authorization/sources:オーソライザーの管理と順序調整
  • /api/v5/authorization/cache:クライアント認可キャッシュのクリア
  • /api/v5/authorization/sources/built_in_databasebuilt_in_databaseオーソライザーの認可ルール管理

詳細な操作手順はHTTP APIを参照してください。

オーソライザーの管理

ダッシュボードのアクセス制御->認可ページでオーソライザーの閲覧と管理が可能です。

オーソライザーの順序調整

認可チェーンで述べたように、オーソライザーは設定された順序で実行されます。その他ドロップダウンから上へ移動下へ移動先頭へ移動末尾へ移動を選択して順序を変更できます。authorization.sources設定項目でも位置調整可能です。

オーソライザーの状態確認

状態列で接続状況を確認できます。

状態意味トラブルシューティング
接続済みすべてのノードがデータソースに正常接続済み-
未接続一部または全ノードがデータソース(データベース、ファイル)に未接続データソースの稼働確認;
問題解決後、オーソライザーを手動で再起動(無効化有効化
接続中一部または全ノードがデータソースに再接続中データソースの稼働確認;
問題解決後、オーソライザーを手動で再起動(無効化有効化

実行中のメトリクス

オーソライザーの概要ページで統計メトリクスを確認できます。以下のメトリクスが表示されます。

  • 許可数:認可成功回数
  • 拒否数:認可失敗回数
  • マッチなし数:クライアント認可データが見つからなかった回数
  • 無視数:認可クエリが無視された回数(例:オーソライザーのpreconditiontrueでない場合や、認可ソースが適用外またはエラーで判定不能な場合)
  • レート(tps):認可実行の処理率

また、ノードステータスから各ノードの認可状態や実行状況も確認可能です。

認可全体の実行メトリクスを確認したい場合は、メトリクス - 認証&認可を参照してください。