# 認可

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

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

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

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

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

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

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

EMQXの認可機構は、組み込みデータベース、ファイル、MySQL、PostgreSQL、MongoDB、Redisなど、多様なデータストレージオブジェクトとの統合をサポートしています。REST APIやEMQXダッシュボードを通じて権限データを管理可能です。<!-- CSVやJSONファイルを用いた一括インポートは現時点で未対応です。 -->

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

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

| データベース       | 説明                                                        |
| ----------------- | ------------------------------------------------------------ |
| ACLファイル        | [ファイルに設定された静的ルールによる認可](./file.md)         |
| 組み込みデータベース | [組み込みデータベースをルールストレージとした認可](./mnesia.md) |
| MySQL             | [MySQLをルールストレージとした認可](./mysql.md)               |
| PostgreSQL        | [PostgreSQLをルールストレージとした認可](./postgresql.md)     |
| MongoDB           | [MongoDBをルールストレージとした認可](./mongodb.md)           |
| Redis             | [Redisをルールストレージとした認可](./redis.md)               |
| LDAP              | [LDAPディレクトリをルールストレージとした認可](./ldap.md)      |
| HTTP              | [外部HTTPサービスによる認可](./http.md)                       |

以下は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`の設定に従ってクライアント操作を許可または拒否します。

::: warning 注意

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

:::

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

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

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

プリコンディションは[Variform式](../../configuration/configuration.md#variform-expressions)で、`listener`、`username`、`clientid`、`action`、`topic`などのクライアントおよび認可リクエスト情報を評価します。式が`true`と評価されなければ、そのオーソライザーはスキップされます。

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

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

- `username`: クライアントのユーザー名
- `clientid`: クライアントID
- `client_attrs.*`: クライアント属性（例: `client_attrs.tenant`）。クライアント属性の詳細は[MQTTクライアント属性](../../../develop/client-attributes/client-attributes.md)を参照してください。
- `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.biz`が`orders`であり、現在の認可アクションが`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. **保存**をクリックして設定を反映します。

これらの設定は設定ファイルでも可能です。詳細は[設定ファイル](../../configuration/configuration.md)を参照してください。

::: tip

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

:::

## 外部リソースキャッシュ

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

::: tip 注意

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

:::

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

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

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

::: tip 注意

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

:::

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

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

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

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

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

   | 項目名                       | 説明                                                      |
   | ---------------------------- | --------------------------------------------------------- |
   | **キャッシュ最大件数**        | ノードごとのキャッシュエントリ最大数。デフォルトは`1,000,000`。 |
   | **最大メモリ使用量**          | キャッシュのメモリ使用上限。デフォルトは`100 MB`。        |
   | **キャッシュTTL**             | キャッシュエントリの有効期間。デフォルトは`1分`。          |

4. **更新**をクリックして設定を反映します。

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

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

<!--@include: ../monitor-cache-status.md-->

## 認可プレースホルダー

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_clientid`や`peer_cert_as_clientid`が有効な場合はユーザー名や証明書のフィールド・内容で上書きされます。
* `${peerhost}`: 実行時にクライアントのIPアドレスに置換されます。EMQXは[Proxy Protocol](http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)をサポートしており、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クライアント属性](../../../develop/client-attributes/client-attributes.md)を参照してください。
* `${zone}`: 実行時にクライアントのゾーンに置換されます。`${zone}`プレースホルダーは認可テンプレート内で直接使用可能です。ゾーン設定の詳細は[ゾーンオーバーライド](../../configuration/configuration.md#zone-override)を参照してください。

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

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

* `${clientid}`
* `${username}`
* `${client_attrs.NAME}`: クライアント属性。`NAME`は`mqtt.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_failures`が`true`の場合はルールをマッチしないものとして扱います。

EMQX 6.3.0のデフォルトセキュリティプロファイルは`legacy`で、`authorization.ignore_backend_failures`は`false`です。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/1`や`t/2`にはマッチしません。

:::

## 認可チェックの優先度

キャッシュや認可チェッカーに加え、認可結果は認証フェーズでの[スーパーユーザーロールと権限セット](../authn/authn.md#super-user)にも影響されます。

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

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

## 認可機構の設定

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

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

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

<img src="./assets/authentication-with-dashboard.png" alt="ダッシュボードによる認証設定" style="zoom:80%;" />

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

設定ファイルの`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`を指定可能。デフォルトは`ignore`。`ignore`は操作を静かに無視し、`disconnect`はクライアント接続を切断。

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

  * `cache.enable`：クライアント認可キャッシュを有効にするかどうか。デフォルトは`true`。JWTパケットのみで認可する場合は`false`推奨。
    
  * `cache.max_size`：キャッシュ内の最大要素数。デフォルトは32。超過時は古いレコードから削除。
    
  * `cache.excludes`：キャッシュ生成を除外するトピックリスト。デフォルトは空リスト`[]`。
    
  * `cache.ttl`：キャッシュの有効期間。デフォルトは`1m`（1分）。

::: tip

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

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

:::

### HTTP APIでの認可設定

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

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

詳細な操作手順は[HTTP API](../../api.md)を参照してください。

## オーソライザーの管理

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

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

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

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

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

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

### 実行中のメトリクス

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

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

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

認可全体の実行メトリクスを確認したい場合は、[メトリクス - 認証＆認可](../../observability/metrics-and-stats.md#Metrics+-+Authentication+%26+Authorization)を参照してください。
