認証
認証とは、クライアントの身元を検証するプロセスです。ほとんどのアプリケーションにおいて不可欠な要素であり、不正なクライアント接続からサービスを保護するのに役立ちます。
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はMySQL、PostgreSQL、MongoDB、Redisなどの各種バックエンドデータベースとの統合もサポートしています。
また、EMQXはユーザーが開発したHTTPサーバーなど外部サービスに認証処理を委譲する設定も可能です。
MQTT 5.0 拡張認証
MQTT 5.0拡張認証は、基本認証を拡張しチャレンジ/レスポンス方式の認証を含みます。拡張認証の実装により、SCRAM認証やKerberos認証など、より安全な認証機構の利用が可能になります。EMQXの拡張認証実装は、組み込みデータベースおよび外部HTTPサービスを通じたSCRAMユーザー管理をサポートしています。
PSK認証
EMQXのPSK認証は、証明書ベースのTLSに代わるよりシンプルかつ安全な方法を提供します。クライアントとサーバーが共有する秘密鍵に基づくため、デジタル証明書を必要としません。この機構は、証明書処理のオーバーヘッドが大きいリソース制約環境で特に有効です。
EMQX認証器
EMQXは、認証機構およびバックエンドデータベースに基づき、以下の認証方式(以下「認証器」と呼びます)をサポートしています。
機構 | データベース | 説明 |
---|---|---|
パスワードベース | 組み込みデータベース | Mnesiaデータベースを認証情報ストレージとして利用する認証 |
パスワードベース | MySQL | MySQLデータベースを認証情報ストレージとして利用する認証 |
パスワードベース | PostgreSQL | PostgreSQLデータベースを認証情報ストレージとして利用する認証 |
パスワードベース | MongoDB | MongoDBデータベースを認証情報ストレージとして利用する認証 |
パスワードベース | Redis | Redisデータベースを認証情報ストレージとして利用する認証 |
パスワードベース | LDAP | LDAPサーバーを認証情報ストレージとして利用する認証 |
パスワードベース | HTTPサーバー | 外部HTTP APIを利用した認証情報検証認証 |
JWT | JWTを利用した認証 | |
SCRAM | 組み込みデータベース | SCRAMを利用した認証 |
SCRAM | HTTPサーバー | RESP APIベースのSCRAM認証 |
GSSAPI | Kerberos | Kerberosを用いたGSSAPI認証 |
ルールベース | Client-infoを利用した認証 |
認証チェーン
EMQXは認証チェーンの作成をサポートしており、複数の認証器を定義された順序で評価できます。チェーン内の各認証器は異なるタイプ(例:HTTP、LDAP、組み込みデータベース)である必要があります。
TIP
現時点でEMQXはMQTTクライアント向けの認証チェーンのみサポートしています。ゲートウェイは認証チェーンをサポートせず、単一の認証器を使用してください。
X.509証明書ベースの認証が適用されている場合、認証チェーンの実行前に必ず実行されます。
認証チェーンの動作
認証チェーンが設定されている場合、EMQXはまず最初の認証器から認証情報の取得を試み、失敗した場合は次の認証器へ処理を移します。
パスワード認証を例に動作を説明します:
前提条件の評価(設定されている場合):
認証器に前提条件が設定されている場合、EMQXはクライアント属性情報(例:listener
、clientid
、username
)に基づいて式を評価します。- 式が
true
の場合、認証器を呼び出します。 - そうでない場合、認証器はスキップされます。
- 式が
認証器の実行:
- 認証情報が見つかり有効(例:パスワードが正しい)なら、クライアントは認証成功となり接続が許可されます。
- 認証情報が見つかったが無効なら、クライアントはアクセス拒否されます。
- 認証情報が見つからなければ、EMQXは次の認証器へ処理を移します。
エラーまたは無効時のスキップ:
認証器が無効化されている場合や実行中に内部エラー(例:データベースが利用不可)が発生した場合もスキップされます。フォールバック動作:
すべての認証器がスキップされるか、いずれもクライアントを認証できなかった場合、EMQXはデフォルトで接続を拒否します。
認証器の前提条件
EMQX 5.9以降、各認証器に前提条件を割り当てて、特定のクライアントに対して認証器を呼び出すか制御できます。前提条件はVariform式であり、クライアント属性(listener
、username
、clientid
など)を評価します。式がtrue
でない場合、認証器はスキップされます。
この機能により認証チェーン内で条件分岐が可能となり、例えば異なるリスナー経由のクライアントに異なる認証器を適用するなど、細かな認証ロジック制御が可能です。EMQXは適切な場合にのみ認証器を呼び出し、不要な外部システムへのリクエストを回避します。
前提条件でサポートされるクライアント属性
前提条件で利用可能なクライアント属性は以下の通りです:
username
: クライアントのユーザー名password
: クライアントのパスワードclientid
: クライアントIDclient_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')
外部リソースキャッシュ
EMQXはMySQL、MongoDB、Redisなどの外部バックエンドから取得した認証結果をノードレベルでキャッシュする機構を提供しています。このキャッシュは認証結果の検索性能を向上させ、特に高スループット環境での外部リソースへの繰り返しアクセスを削減します。
注意
外部リソースキャッシュは外部データソースにのみ適用されます。組み込みデータベース認証器などローカルソースには適用されません。
外部リソースキャッシュの動作
外部リソースキャッシュはノード単位で認証結果を保存し、同一ノード上のすべてのクライアントセッションで共有されます。これにより、外部認証バックエンドへの冗長な問い合わせを回避します。
- クライアントが接続し認証をトリガーする。
- EMQXはキャッシュに以前の認証結果があるか確認する:
- 有効な結果があればキャッシュヒットとなり、外部バックエンドへの呼び出しは行われません。
- 結果がなければキャッシュミスとなり、EMQXは外部バックエンドに問い合わせます。
- バックエンドから返された結果は将来の利用のためキャッシュに保存され、キャッシュ挿入メトリクスが増加します。
この仕組みによりレイテンシが低減され、バックエンドの負荷を抑えつつシステムの応答性を維持します。
外部リソースキャッシュの有効化と設定
EMQXダッシュボードから外部リソースキャッシュを有効化および設定できます。
アクセス制御 -> 認可 に移動します。
右上の 外部リソースキャッシュ設定 ボタンをクリックします。右側からサイドパネルが表示されます。
パネル内で 外部リソースキャッシュを有効にする ボタンを使ってキャッシュ機能をオンまたはオフにします。有効化したら、以下のキャッシュ設定を行います。
フィールド名 説明 最大キャッシュアイテム数 ノードごとの最大キャッシュエントリ数。デフォルト: 1,000,000
。最大メモリ キャッシュメモリ使用量の上限。デフォルト: 100 MB
。キャッシュTTL キャッシュエントリの有効期間。デフォルト: 1分
。更新 をクリックして設定を適用します。
これらの設定はクラスター全体に適用され、すべてのノードで一貫した動作を保証します。
外部リソースキャッシュの状態監視
キャッシュメトリクスを表示し、リアルタイムで使用状況を監視するには:
- External Resource Cache Settings の横にある矢印をクリックし、External Resource Cache Status を選択します。サイドパネルが表示され、キャッシュメトリクスが確認できます。
- ドロップダウンメニューを使って、ノード単位またはクラスター全体のメトリクスを表示できます。
メトリクスには以下が含まれます:
- 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
フィールドで確認できます。
パスワードハッシュ化
パスワードを平文で保存すると、データベースを覗いた誰もがパスワードを読み取れてしまいます。したがって、パスワードはハッシュ化アルゴリズムを用いて生成されたハッシュ値として保存することが推奨されます。EMQXは様々なパスワードハッシュ化アルゴリズムをサポートし、多様なセキュリティ要件に対応しています。
また、EMQXはソルトをハッシュに加えることもサポートしており、ソルトを加えたユニークなハッシュ(password_hash)は様々な攻撃から保護します。
ワークフロー
パスワードハッシュ化のワークフローは以下の通りです:
- EMQX認証器は設定されたクエリ文を用いて、データベースからハッシュ化パスワードやソルト値を含む適格な認証情報を照会します。
- クライアントが接続を試みると、EMQX認証器はクライアントが提供したパスワードを設定されたハッシュアルゴリズムと照会したソルト値でハッシュ化します。
- 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アドレスを取得可能です。${peerport}
: 実行時にクライアントの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 Client Attributesを参照してください。${zone}
: 実行時にクライアントのゾーンに置換されます。${zone}
プレースホルダーは認証テンプレート内で直接使用可能です。ゾーン設定の詳細はZone Overrideを参照してください。例えば、以下の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>
例:
password_based:built_in_database
jwt
scram:built_in_database
リスナーIDも同様に以下の規則に従います:
<transport_protocol>:<name>
ゲートウェイリスナーIDはプロトコル名を前に付けた形式です:
<protocol>:<transport_protocol>:<name>
認証器IDおよびリスナーIDはURLで使用する際、URLエンコード規則に従う必要があります。例えば、:
は%3A
に置換します:
PUT /api/v5/authentication/password_based%3Abuilt_in_database
データ操作API
組み込みデータベースおよびMQTT 5.0拡張認証を利用する認証では、認証データの作成、更新、削除、一覧取得を行うHTTP APIを提供しています。詳細はHTTP APIでの認証データ管理を参照してください。
詳細なAPIリクエストやパラメータはHTTP APIを参照してください。