Skip to content

認証 ​

認証はクライアントの身元を検証するプロセスです。ほとんどのアプリケーションにおいて不可欠な要素であり、不正なクライアント接続からサービスを保護するのに役立ちます。

EMQXは複数の認証機構をサポートしており、さらにTLS X.509証明書認証およびTLS-PSK認証もサポートしています。これによりクライアントとサーバー間の認証要求にオプションを提供します。

本節では、身元認証の基本概念と設定について説明します。

TIP

デフォルトでは、EMQXは認証機能を有効にしておらず、すべてのクライアントの接続を許可します。実運用環境で使用する場合は、少なくとも1つの認証方式を事前に設定してください。

認証機構 ​

EMQXがサポートする認証機構は以下の通りです。

  • X.509証明書認証
  • JWT認証
  • ユーザー名/パスワード認証
  • MQTT 5.0の拡張認証
  • PSK認証

X.509証明書認証 ​

EMQXはクライアント認証のためにX.509証明書認証をサポートしています。EMQXでX.509証明書認証を使用すると、クライアントとサーバーはTLS/SSLを介して信頼できる接続を確立し、通信相手の真正性と送信データの整合性を保証します。EMQXは片方向認証(クライアントがサーバーのみを認証)と双方向認証(クライアントとサーバーがお互いの証明書を検証)をサポートし、さまざまなセキュリティ要件や展開シナリオに対応可能です。

JWT認証 ​

JSON Web Token (JWT)は、サーバーがクライアントの認証情報やセッション情報を保持しないトークンベースの認証機構です。

クライアントは接続要求時にJWTを携帯し、EMQXは事前設定されたシークレットまたは公開鍵を使ってJWT署名を検証します。ユーザーがJWKSエンドポイントを設定している場合、JWT認証器はJWKSエンドポイントから取得した公開鍵リストを用いてJWT署名を検証します。

パスワード認証 ​

EMQXは最もシンプルで一般的なパスワード認証をサポートしています。これはクライアントがユーザー名、クライアントID、対応するパスワードなどの認証情報を提供する方式です。場合によってはTLS証明書の一部フィールド(例:証明書のCommon Name)をクライアントの身元認証情報として利用することもあります。いずれの場合も、これらの認証情報は事前にデータベースに保存されており、パスワードは通常ソルト付きハッシュ形式で保存されます。

EMQXのパスワード認証の仕組みは以下の通りです。クライアントは接続要求時に認証情報を携帯し、EMQXはクライアントから提供された認証情報に対応するハッシュ化されたパスワードをデータベースから照会し、照合が成功した場合にのみ接続を許可します。 emqx-authn-flow

組み込みデータベースのほか、EMQXはMySQL、PostgreSQL、MongoDB、Redisなどの各種バックエンドデータベースとの連携もサポートしています。

また、EMQXはユーザーが開発したHTTPサーバーなど外部サービスに認証処理を委譲する設定も可能です。

MQTT 5.0 拡張認証 ​

MQTT 5.0の拡張認証は、基本認証にチャレンジ/レスポンス方式の認証を追加したものです。この拡張認証の実装により、SCRAM認証やKerberos認証など、より安全な認証機構を利用可能になります。EMQXの拡張認証は、組み込みデータベースおよび外部HTTPサービスを通じたSCRAMユーザー管理をサポートしています。

PSK認証 ​

EMQXのPSK認証は、証明書ベースのTLSに代わるシンプルかつ安全な方式を提供します。クライアントとサーバーが共有する秘密鍵を用い、デジタル証明書を必要としません。この方式は、証明書の管理にかかるオーバーヘッドが大きいリソース制約のある環境で特に有用です。

EMQX認証器 ​

EMQXは認証機構およびバックエンドデータベースに基づき、以下の認証方式(以下「認証器」と呼びます)をサポートしています。

機構データベース説明
パスワードベース組み込みデータベースMnesiaデータベースを認証情報ストレージとして使用する認証
パスワードベースMySQLMySQLデータベースを認証情報ストレージとして使用する認証
パスワードベースPostgreSQLPostgreSQLデータベースを認証情報ストレージとして使用する認証
パスワードベースMongoDBMongoDBデータベースを認証情報ストレージとして使用する認証
パスワードベースRedisRedisデータベースを認証情報ストレージとして使用する認証
パスワードベースLDAPLDAPサーバーを認証情報ストレージとして使用する認証
パスワードベースHTTPサーバー外部HTTP APIを用いた認証情報検証による認証
JWTJWTを用いた認証
SCRAM組み込みデータベースSCRAMを用いた認証
SCRAMHTTPサーバーRESP APIベースのSCRAM認証
GSSAPIKerberosKerberosを用いたGSSAPI認証
ルールベースClient-infoを用いた認証

認証チェーン ​

EMQXは認証チェーンの作成をサポートしており、複数の認証器を定義された順序で評価できます。チェーン内の各認証器は異なるタイプである必要があります(例:1つはHTTP、1つはLDAP、1つは組み込みデータベース)。

TIP

現時点でEMQXはMQTTクライアント向けの認証チェーンのみサポートしています。ゲートウェイは認証チェーンをサポートせず、単一の認証器を使用してください。

X.509証明書ベースの認証が適用される場合、認証チェーンの実行前に必ず実行されます。

認証チェーンの動作 ​

認証チェーンが設定されている場合、EMQXはまず最初の認証器から認証情報の取得を試み、失敗したら次の認証器へ切り替えて処理を続行します。

パスワードベース認証を例に動作を説明します。

  1. 前提条件の評価(設定されている場合)
    認証器に前提条件がある場合、EMQXはクライアント属性情報(例:listener、clientid、username)に基づき式を評価します。
    • 式がtrueの場合、認証器を呼び出します。
    • そうでなければ認証器をスキップします。
  2. 認証器の実行
    • 認証情報が見つかり有効(例:パスワードが正しい)なら、クライアントは正常に認証され接続が許可されます。
    • 認証情報が見つかったが無効なら、クライアントはアクセス拒否されます。
    • 認証情報が見つからなければ、EMQXは次の認証器に移ります。
  3. エラーまたは無効時のスキップ
    認証器が無効化されているか、実行中に内部エラー(例:データベースが利用不可)が発生した場合もスキップされます。
  4. フォールバック動作
    すべての認証器がスキップされるか、いずれもクライアントを認証できなかった場合、EMQXはデフォルトで接続を拒否します。

認証器の前提条件 ​

EMQX 5.9以降、各認証器に前提条件を割り当てることができ、特定のクライアントに対して認証器を呼び出すか制御可能です。前提条件はVariform式で、クライアント属性(listener、username、clientidなど)を評価します。式がtrueと評価されなければ認証器はスキップされます。

この機能により認証チェーン内で条件分岐が可能となり、異なるリスナー経由のクライアントに異なる認証器を適用するなど細かな制御が可能です。EMQXは適切な場合にのみ認証器を呼び出し、外部システムへの不要なリクエストを回避します。

前提条件でサポートされるクライアント属性 ​

前提条件で利用可能なクライアント属性は以下の通りです。

  • username: クライアントのユーザー名
  • password: クライアントのパスワード
  • clientid: クライアントID
  • client_attrs.*: クライアント属性
  • cert_common_name: クライアントTLS証明書のSubjectフィールド
  • cert_subject: クライアントTLS証明書のCommon Name (CN)
  • peersni: TLSクライアントが送信したSNI(Server Name Indication)
  • listener: リスナーID(例:tcp:default)
  • zone: 関連付けられた設定ゾーン

前提条件の例 ​

異なるリスナー経由のクライアントを異なる認証器で認証する例:

  • tcp:defaultのクライアントに対するHTTP認証器:

    str_eq(listener, 'tcp:default')
  • ssl:defaultのクライアントに対するPostgreSQL認証器:

    str_eq(listener, 'ssl:default')

JWT認証器とパスワードベース認証器を1つの認証チェーンで組み合わせる場合、JWT認証器に以下の前提条件を設定します。

is_jwt(password)

EMQX 6.2.3以降、この前提条件はパスワードが構造的にJWTである場合のみtrueを返します。パスワードがJWTでないか存在しない場合、EMQXはJWT認証器をスキップし、次の認証器に処理を移します。

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

EMQXはMySQL、MongoDB、Redisなどの外部バックエンドから取得した認証結果をノードレベルでキャッシュする仕組みを提供しています。このキャッシュは認証結果の検索性能を向上させ、特に高スループット環境での外部リソースへの繰り返しアクセスを削減します。

注意

外部リソースキャッシュは外部データソースにのみ適用されます。組み込みデータベース認証器などローカルソースにはEMQXはこのキャッシュを使用しません。

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

外部リソースキャッシュはノード単位で認証結果を保存し、同じノード上のすべてのクライアントセッションで共有され、外部認証バックエンドへの冗長な問い合わせを回避します。

  1. クライアントが接続し認証をトリガーする。
  2. EMQXはキャッシュに以前の結果があるか確認する。
    • 有効な結果があればキャッシュヒットとなり、外部バックエンドへの呼び出しは行われない。
    • 結果がなければキャッシュミスとなり、EMQXは外部バックエンドに問い合わせる。
  3. バックエンドから返された結果はキャッシュに保存され、キャッシュ挿入メトリクスが増加する。

この仕組みによりレイテンシ削減、バックエンド負荷軽減、システム応答性維持が可能となります。

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

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 Usage、Cache Entries、および Cache Hits の概要を確認できます。

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

スーパーユーザー ​

通常、認証はクライアントの身元認証情報の検証のみを行い、特定のトピックへのパブリッシュ/サブスクライブ権限は認可システムで判断します。しかしEMQXはスーパーユーザーロールと権限プリセット機能を提供し、後続のパブリッシュ/サブスクライブ認可処理を容易にします。

TIP

権限プリセットはJWT認証およびHTTP認証でサポートされています。現在のクライアントが所有するパブリッシュ/サブスクライブ権限のアクセス制御リスト(ACL)はJWTペイロードやHTTPレスポンスボディに含まれ、認証成功後にクライアントへプリセットされます。

ユーザーがスーパーユーザーかどうかは、データベースクエリ、HTTPレスポンス、JWTクレームのis_superuserフィールドで確認できます。

認証結果によるクライアントIDの上書き ​

MQTTクライアントの場合、認証バックエンドは認証成功時にclientid_overrideを返すことができます。この値は完全な置換用クライアントIDを含む空でない文字列でなければなりません。EMQXは認証後、クライアントセッション開始前にこの値を適用します。フィールドが存在しないか空の場合は、従来のクライアントIDを保持します。

この仕組みは、テナント識別子など認証後にのみ利用可能な信頼情報に基づきクライアントIDを置換したい場合に有用です。マルチテナント展開では、EMQXが有効なクライアントIDにテナントIDを含め、異なるテナントのクライアントが同一の元クライアントIDを使ってもセッション競合を回避できます。認証前に接続情報から置換クライアントIDを構築可能な場合は、代わりにmqtt.clientid_overrideを使用してください。仕組みの選択、実行順序、マルチテナント展開での失敗時の挙動についてはクライアントID分離を参照してください。

MQTT接続に対してはクライアントID上書きの仕組みを1つだけ使用してください。両方設定されている場合、認証結果による上書きが後から実行され、mqtt.clientid_overrideで生成されたクライアントIDを置換します。

WARNING

EMQX 6.3.1以降、ゲートウェイプロトコルは認証結果によるクライアントID上書きをサポートしません。ゲートウェイ経由のクライアントではclientid_overrideは無視され、ゲートウェイプロトコルで決定されたクライアントIDを保持し、gateway_authn_clientid_override_not_supportedの警告ログを出力します。

認証バックエンドの設定 ​

clientid_overrideは認証結果のフィールドであり、すべての認証器共通のダッシュボード設定ではありません。各認証バックエンドで以下のように設定します。

認証バックエンドclientid_overrideの提供方法
HTTP成功時のJSONレスポンスのトップレベル文字列フィールドとして返す。
JWTJWTペイロードのトップレベル文字列クレームとして追加する。
LDAPLDAP属性に保存し、Client ID Override Attributeに属性名を設定。デフォルトはclientIdOverride。
MongoDBドキュメントフィールドに保存し、Client ID Override Fieldにフィールド名を設定。デフォルトはclientid_override。
MySQLクエリ結果のカラム名clientid_overrideで返す。
PostgreSQLクエリ結果のカラム名clientid_overrideで返す。
Redisclientid_overrideフィールドに保存し、クエリコマンドに含める。

置換用クライアントIDをクライアントID分離に使用する場合は、グローバルに一意であることを保証してください。

パスワードハッシュ ​

パスワードを平文で保存すると、データベースを覗いた者がパスワードをそのまま読み取れてしまいます。したがって、パスワードはハッシュ化アルゴリズムを使って生成されたハッシュ値として保存することが推奨されます。EMQXはさまざまなセキュリティ要件に対応するため、多様なパスワードハッシュアルゴリズムをサポートしています。

また、EMQXはソルトをハッシュに加えることもサポートしており、ソルトを加えたユニークなハッシュ(password_hash)は各種攻撃から保護します。

ワークフロー ​

パスワードハッシュのワークフローは以下の通りです。

  1. EMQX認証器は設定されたクエリ文を使い、ハッシュ化パスワードやソルト値を含む認証情報をデータベースから取得する。
  2. クライアントが接続を試みると、EMQX認証器はクライアント提供のパスワードを設定されたハッシュアルゴリズムと取得したソルト値でハッシュ化する。
  3. EMQX認証器はステップ1で取得したハッシュパスワードとステップ2で計算したハッシュ値を比較し、一致すれば認証を許可する。

以下はEMQXがサポートするハッシュアルゴリズムの例です。

# シンプルなアルゴリズム
password_hash_algorithm {
  name = sha256             # plain, md5, sha, sha512
  salt_position = suffix    # prefix, disable
}

# bcrypt
password_hash_algorithm {
  name = bcrypt
}

# pbkdf2
password_hash_algorithm {
  name = pbkdf2
  mac_fun = sha256          # md4, md5, ripemd160, sha, sha224, sha384, sha512
  iterations = 4096
  dk_length = 32           # 任意、単位:バイト
}

ハッシュアルゴリズムによってパフォーマンスに大きな差があるため、用途に応じて選択してください。参考までに、4コア8GBマシンで各ハッシュアルゴリズムを100回実行した際の平均実行時間は以下の通りです。

認証プレースホルダー ​

EMQXはクエリ文やHTTPリクエスト内でプレースホルダーを使用できます。認証時にこれらのプレースホルダーは実際のクライアント情報に置換され、現在のクライアントに合致したクエリやHTTPリクエストが構築されます。

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

例えば、EMQXのMySQL認証器のデフォルトクエリSQLは${username}プレースホルダーを使用しています。

SELECT password_hash, salt FROM mqtt_user where username = ${username} LIMIT 1

クライアント名がemqx_uの場合、実際のクエリは以下のようになります。

SELECT password_hash, salt FROM mqtt_user where username = 'emqx_u' LIMIT 1

EMQXが現在サポートするプレースホルダーは以下の通りです。

  • ${clientid}: 実行時にクライアントIDに置換されます。クライアントIDは通常CONNECTパケットで明示的に指定されますが、use_username_as_clientidやpeer_cert_as_clientidが有効な場合はユーザー名や証明書のフィールド、証明書の内容により上書きされます。

  • ${username}: 実行時にユーザー名に置換されます。ユーザー名はCONNECTパケットのUsernameフィールドから取得されます。peer_cert_as_usernameが有効な場合は証明書のフィールドや内容で上書きされます。

  • ${password}: 実行時にパスワードに置換されます。パスワードはCONNECTパケットのPasswordフィールドから取得されます。

  • ${peerhost}: 実行時にクライアントのIPアドレスに置換されます。EMQXはProxy Protocolをサポートしており、TCPプロキシやロードバランサーの背後に配置されていても実際のIPアドレスを取得可能です。

  • ${peername}: 実行時にクライアントのIPアドレスとポートに置換され、IP:PORT形式になります。

  • ${cert_subject}: 実行時にクライアントTLS証明書のSubjectに置換されます。ロードバランサーがクライアント証明書情報をTCPリスナーに送信する場合はProxy Protocol v2の使用を確認してください。

  • ${cert_common_name}: 実行時にクライアントTLS証明書のCommon Nameに置換されます。ロードバランサーがクライアント証明書情報をTCPリスナーに送信する場合はProxy Protocol v2の使用を確認してください。

  • ${client_attrs.NAME}: クライアント属性。NAMEは事前設定に基づく属性名に置換されます。クライアント属性の詳細はMQTTクライアント属性を参照してください。

  • ${zone}: 実行時にクライアントのゾーンに置換されます。${zone}プレースホルダーは認証テンプレートで直接使用可能です。ゾーン設定の詳細はゾーンオーバーライドを参照してください。

    例えば、以下のACLルールは${zone}を用いてクライアントの割り当てゾーンに基づき動的に権限を適用します。

    {allow, all, all, ["${zone}/${username}/#"]}

認証の設定 ​

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

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

EMQXダッシュボードは直感的に認証器を設定でき、状態確認やカスタマイズが可能です。以下のスクリーンショットのように、組み込みデータベースベースのパスワード認証とJWT認証の2つの認証器を設定しています。

設定ファイルによる認証設定 ​

設定ファイルでもEMQX認証器を設定可能です。

例えば、以下のauthenticationフィールドでは複数の認証器からなる認証チェーンを作成しており、設定ファイル内の順序で認証器が実行されます。

# base.hocon

# すべてのMQTTリスナーに対するグローバル認証チェーン
authentication = [
  ...
]

listeners.tcp.default {
  ...
  # 指定したMQTTリスナーに対する認証チェーン
  authentication = [
    ...
  ]
}

gateway.stomp {
  ...
  # すべてのSTOMPリスナーに対するグローバル認証器
  authentication = {
    ...
  }

}

認証器の種類により設定項目は異なります。詳細は設定章を参照してください。

HTTP APIによる認証設定 ​

設定ファイルに比べ、HTTP APIはより便利でランタイム更新をサポートし、設定変更をクラスター全体に自動同期できます。

EMQX認証APIを使い、グローバル認証器の作成や特定認証器の設定更新が可能です。

  • /api/v5/authentication: グローバルMQTT認証の管理用APIエンドポイント
  • /api/v5/gateway/{protocol}/authentication: 他アクセスプロトコルのグローバル認証管理用APIエンドポイント
  • /api/v5/gateway/{protocol}/listeners/{listener_id}/authentication: 他アクセスプロトコルのリスナー認証管理用APIエンドポイント

認証器ID ​

特定の認証器を操作するには、上記エンドポイントに認証器IDを付加します。例:/api/v5/authentication/{id}。メンテナンスを容易にするため、IDはEMQXが自動生成してAPIで返すのではなく、以下の規則に従います。

<mechanism>:<backend>

または

<mechanism>

例:

  1. password_based:built_in_database
  2. jwt
  3. scram:built_in_database

リスナーIDにも同様の規則があります。

bash
<transport_protocol>:<name>

ゲートウェイリスナーIDはプロトコル名を先頭に付加します。

bash
<protocol>:<transport_protocol>:<name>

認証器IDおよびリスナーIDはURLで使用する際、URLエンコード規則に従う必要があります。例えば、:は%3Aに置換します。

bash
PUT /api/v5/authentication/password_based%3Abuilt_in_database

データ操作API ​

組み込みデータベースおよびMQTT 5.0拡張認証の認証では、認証データの作成、更新、削除、一覧取得などを行うHTTP APIを提供しています。詳細はHTTP APIで認証データを管理するを参照してください。

詳細なAPIリクエストやパラメーターはHTTP APIを参照してください。