MQTT ブローカー認証を構成する

重要

Azure Arc によって実現されている Azure IoT Operations プレビューは、現在プレビュー段階です。 運用環境ではこのプレビュー ソフトウェアを使わないでください。

Azure IoT Operations の一般公開リリースが提供されたときには、新規インストールをデプロイすることが必要になります。 プレビュー インストールからのアップグレードはできません。

ベータ版、プレビュー版、または一般提供としてまだリリースされていない Azure の機能に適用される法律条項については、「Microsoft Azure プレビューの追加使用条件」を参照してください。

MQTT ブローカーでは、クライアントに対する複数の認証方法がサポートされており、それぞれのリスナーが BrokerAuthentication リソースを使用した独自の認証システムを持つように構成できます。 使用可能な設定の一覧については、Broker Authentication API リファレンスを参照してください。

BrokerListener と BrokerAuthentication の関係には、次のルールが適用されます。

  • 各 BrokerListener は複数のポートを持つことができます。 各ポートは、BrokerAuthentication リソースにリンクできます。
  • BrokerAuthentication は一度に複数の認証方法をサポートできます。

BrokerListener を BrokerAuthentication リソースにリンクするには、BrokerListener リソースの ports 設定で authenticationRef フィールドを指定します。 詳細については、BrokerListener リソースに関するページを参照してください。

既定の BrokerAuthentication リソース

Azure IoT Operations プレビューでは、azure-iot-operations 名前空間内の listener という名前の "既定の" リスナーとリンクされた、authn という名前の既定の BrokerAuthentication リソースがデプロイされます。 これは、認証に Kubernetes サービス アカウント トークン (SAT) のみを使用するように構成されています。 これを検査するには、次を実行します。

kubectl get brokerauthentication authn -n azure-iot-operations -o yaml

出力には、簡潔にするためにメタデータが除外された既定の BrokerAuthentication リソースが示されます。

apiVersion: mqttbroker.iotoperations.azure.com/v1beta1
kind: BrokerAuthentication
metadata:
  name: authn
  namespace: azure-iot-operations
spec:
  authenticationMethods:
    - method: ServiceAccountToken
      serviceAccountTokenSettings:
        audiences:
          - aio-internal

重要

Azure IoT Operations のコンポーネントが正常に機能するには、既定の BrokerAuthentication リソースでのサービス アカウント トークン (SAT) 認証方法が必要です。 既定の BrokerAuthentication リソースを更新または削除しないでください。 変更を加える必要がある場合は、aio-internal 対象ユーザーとの SAT 認証方法を維持しながら、このリソースの authenticationMethods フィールドを変更します。 必要に応じて、別の名前で新しい BrokerAuthentication リソースを作成し、kubectl apply を使用してデプロイできます。

構成を変更するには、この BrokerAuthentication リソースの authenticationMethods 設定を編集するか、別の名前で新しい BrokerAuthentication リソースを作成します。 次に、kubectl apply を使用してそれをデプロイします。

Authentication flow

配列内の認証方法の順序により、MQTT ブローカーでのクライアントの認証方法が決まります。 MQTT ブローカーは、最初に指定された方法を使用してクライアントの資格情報の認証を試み、一致するものが見つかるか、末尾に達するまで配列を反復処理します。

それぞれの方法では、MQTT ブローカーにより、クライアントの資格情報がその方法に "該当する" かどうかがチェックされます。 たとえば、SAT 認証では K8S-SAT から始まるユーザー名が必要であり、X.509 認証ではクライアント証明書が必要です。 クライアントの資格情報が該当する場合、MQTT ブローカーにより、有効かどうかが検証されます。 詳しくは、認証方法の構成に関するセクションをご覧ください。

カスタム認証の場合、MQTT ブローカーでは、カスタム認証サーバーとの通信エラーは "資格情報の該当なし" として扱われます。 この動作により、カスタム サーバーに到達できない場合、MQTT ブローカーは他の方法にフォールバックします。

認証フローは、次の場合に終了します。

  • 以下のいずれかの条件が満たされている場合。
    • クライアントの資格情報が以下のいずれかのメソッドに対して該当し、有効である場合。
    • クライアントの資格情報がいずれのメソッドにも該当しない場合。
    • クライアントの資格情報が以下のいずれかのメソッドに対して該当するが、無効である場合。
  • MQTT ブローカーにより、認証フローの結果に基づいて、クライアントへのアクセスが許可または拒否されます。

MQTT ブローカーには、複数の認証方法を使用することによるフォールバック メカニズムが備わっています。 次に例を示します。

apiVersion: mqttbroker.iotoperations.azure.com/v1beta1
kind: BrokerAuthentication
metadata: 
  name: authn
  namespace: azure-iot-operations
spec:
  authenticationMethods:
    - method: Custom
      customSettings:
        # ...
    - method: ServiceAccountToken
      serviceAccountTokenSettings:
        # ...
    - method: X509
      x509Settings:
        # ...

前の例では、カスタムと SAT が指定されています。 クライアントに接続すると、MQTT ブローカーは、"カスタム" の次に SAT という順番で指定した方法を使用してクライアントの認証を試みます。

  1. MQTT ブローカーにより、クライアントの資格情報がカスタム認証に対して有効かどうかがチェックされます。 カスタム認証では、外部サーバーに依存して資格情報の有効性を判断しているため、ブローカーによってすべての資格情報がカスタム認証に該当すると見なされ、カスタム認証サーバーに転送されます。

  2. カスタム認証サーバーから Pass または Fail の結果で応答が返された場合、認証フローは終了します。 ただし、カスタム認証サーバーが使用できない場合、MQTT ブローカーは、指定された残りの方法にフォールバックします。ここでは SAT が次の方法です。

  3. MQTT ブローカーは、SAT 資格情報として資格情報の認証を試みます。 MQTT ユーザー名が K8S-SAT で始まる場合、MQTT ブローカーでは MQTT パスワードが SAT として評価されます。

カスタム認証サーバーが使用できず、すべての後続のメソッドで提供された資格情報が該当しないと判断された場合は、ブローカーによってクライアントの接続が拒否されます。

認証を無効にする

テストの場合は、BrokerListener リソースの ports 設定で authenticationRef を省略することで、認証を無効にすることができます。

認証方法を構成する

各認証オプションの詳細については、次のセクションの各メソッドを参照してください。

Azure Key Vault を構成し、ワークロード ID を有効にすることで、安全な設定を有効にする方法の詳細については、「Azure IoT Operations Preview 展開で安全な設定を有効にする」を参照してください。

X.509

信頼されたルート CA 証明書は、クライアント証明書を検証するために必要です。 クライアント証明書は、MQTT ブローカーで認証されるよう、この CA でルートする必要があります。 EC キーと RSA キーの両方がサポートされていますが、チェーン内のすべての証明書で同じキー アルゴリズムを使用する必要があります。 独自の CA 証明書をインポートする場合、クライアント証明書は必ず CA と同じキー アルゴリズムを使う必要があります。 クライアント証明書の検証に使用できるルート証明書をインポートするには、client_ca.pem キーの下で、証明書の PEM を ConfigMap としてインポートします。 次に例を示します。

kubectl create configmap client-ca --from-file=client_ca.pem -n azure-iot-operations

ルート CA 証明書が正常にインポートされているかどうかをチェックするには、kubectl describe configmap を実行します。 結果には、同じ PEM 証明書ファイルの base64 エンコードが示されます。

kubectl describe configmap client-ca -n azure-iot-operations
Name:         client-ca
Namespace:    azure-iot-operations

Data
====
client_ca.pem:
----
-----BEGIN CERTIFICATE-----
<Certificate>
-----END CERTIFICATE-----


BinaryData
====

信頼されたクライアント ルート CA 証明書と証明書から属性へのマッピングをインポートしたら、X.509 クライアント認証を認証方法の 1 つとして追加することで有効にします。これは、TLS 対応リスナーにリンクされた BrokerAuthentication リソースの一部として行います。 次に例を示します。

spec:
  authenticationMethods:
    - method: X509
      x509Settings:
        trustedClientCaCert: client-ca
        authorizationAttributes:
        # ...

認可用の証明書の属性

X.509 属性は、BrokerAuthentication リソースで指定でき、証明書のプロパティに基づいてクライアントを承認するために使用されます。 属性は authorizationAttributes フィールドで定義されます。 次に例を示します。

spec:
  authenticationMethods:
    - method: X509
      x509Settings:
        authorizationAttributes:
          root:
            subject = "CN = Contoso Root CA Cert, OU = Engineering, C = US"
            attributes:
              organization = contoso
          intermediate:
            subject = "CN = Contoso Intermediate CA"
            attributes:
              city = seattle
              foo = bar
          smart-fan:
            subject = "CN = smart-fan"
            attributes:
              building = 17

この例では、ルート CA CN = Contoso Root CA Cert, OU = Engineering, C = US または中間 CA CN = Contoso Intermediate CA によって発行された証明書を持つすべてのクライアントが、リストされている属性を受け取ります。 さらに、スマート ファンはそれに固有の属性を受け取ります。

属性の照合は、常にリーフ クライアント証明書から始まり、チェーンに沿って進みます。 属性の割り当ては、最初の一致後に停止します。 前の例では、smart-fan に中間証明書 CN = Contoso Intermediate CA がある場合でも、関連付けられている属性は取得されません。

これらの属性を持つ X.509 証明書を使用して、認可規則をクライアントに適用できます。 署は、「X.509 認証を使用するクライアントを承認する」を参照してください。

X.509 クライアント証明書を使用して Mosquitto クライアントを MQTT ブローカーに接続する

Mosquitto のようなクライアントが TLS と X.509 クライアント認証を使用して MQTT ブローカーに接続できるようにするには、3 つのファイルが必要です。 次に例を示します。

mosquitto_pub -q 1 -t hello -d -V mqttv5 -m world -i thermostat \
-h "<IOT_MQ_EXTERNAL_IP>" \
--cert thermostat_cert.pem \
--key thermostat_key.pem \
--cafile chain.pem

この例では次のとおりです。

  • --cert パラメータは、クライアント証明書 PEM ファイルを指定します。
  • --key パラメータは、クライアント秘密キー PEM ファイルを指定します。
  • 3 つ目のパラメータ --cafile は最も複雑な信頼された証明書データベースであり、2 つの目的で使用されます。
    • Mosquitto クライアントが TLS 経由で MQTT ブローカーに接続する場合、サーバー証明書が検証されます。 データベース内のルート証明書を検索し、サーバー証明書への信頼されたチェーンを作成します。 そのため、このファイルにサーバー ルート証明書をコピーする必要があります。
    • MQTT ブローカーによって Mosquitto クライアントのクライアント証明書が要求されるときには、サーバーに送信するための有効な証明書チェーンも必要になります。 --cert パラメータは Mosquitto が送信する証明書を指定しますが、これだけでは十分ではありません。 MQTT ブローカーには中間証明書も必要であるため、この証明書だけでは検証することができません。 Mosquitto はデータベース ファイルを使用して必要な証明書チェーンを作成します。 これをサポートするため、cafile には中間証明書とルート証明書の両方が含まれている必要があります。

MQTT ブローカー X.509 クライアント認証フローを理解する

X.509 クライアント認証フローの図。

クライアント認証フローの手順を次に示します。

  1. X.509 クライアント認証が有効になっている場合、接続しているクライアントは、クライアント証明書と、MQTTブローカーが構成済みの信頼できる証明書のうちの 1 つをルートとする証明書チェーンを作成するために使用する中間証明書を提示する必要があります。
  2. ロード バランサーにより、通信がいずれかのフロントエンド ブローカーに送られます。
  3. フロントエンド ブローカーは、クライアント証明書を受信すると、構成済み証明書のうちの 1 つをルートとする証明書チェーンの作成を試みます。 TLS ハンドシェイクには証明書が必要です。 フロントエンド ブローカーによって正常にチェーンが作成され、提示されたチェーンが検証されると、TLS ハンドシェイクが完了します。 接続しているクライアントは、作成した TLS チャネルを通じて MQTT パケットをフロントエンドに送信できます。
  4. TLS チャネルは開かれていますが、クライアントの認証または認可はまだ完了していません。
  5. 次に、クライアントにより CONNECT パケットが MQTT ブローカーに送信されます。
  6. CONNECT パケットはフロントエンドにもう一度ルートされます。
  7. フロントエンドでは、ユーザー名とパスワード フィールド、CONNECT パケットからの認証データ、TLS ハンドシェイク中に提示されたクライアント証明書チェーンなど、これまでに提示されたすべての資格情報が収集されます。
  8. フロントエンドにより、これらの資格情報が認証サービスに送信されます。 認証サービスにより、証明書チェーンがもう一度チェックされ、チェーン内のすべての証明書のサブジェクト名が収集されます。
  9. 認証サービスは、構成済みの認可規則を使用して、接続しているクライアントが持つ属性を判断します。 これらの属性により、CONNECT パケット自体を含め、クライアントが実行できる操作が決定されます。
  10. 認証サービスにより、決定がフロントエンド ブローカーに返されます。
  11. フロントエンド ブローカーにより、クライアント属性と、接続が許可されているかどうかが認識されます。 その場合、MQTT 接続が完了し、クライアントは認可規則によって決定された MQTT パケットの送受信を続行できます。

Kubernetes サービス アカウント トークン

Kubernetes サービス アカウント トークン (SAT) は、Kubernetes サービス アカウントに関連付けられている JSON Web トークンです。 クライアントによって、自身を認証するために、MQTT ブローカーに SAT が提示されます。

MQTT ブローカーでは、"バインド サービス アカウント トークン" が使用されます。これについては、「Kubernetes の新しいサービス アカウント トークンについて GKE ユーザーが知っておくべきこと」の投稿で詳しく説明されています。 投稿で説明されている代表的な機能を次に示します。

Kubernetes 1.13 で導入され、1.21 で既定の形式となったバインド トークンは、レガシ トークンの制限された機能すべてに対処するものです。他にも次のような特徴があります。

  • トークン自体が盗用、誤用しずらいものです。これらは、時間、対象ユーザー、オブジェクトに対してバインドされています。
  • OpenID Connect (OIDC) という標準化された形式を採用し、完全な OIDC 検出により、サービス プロバイダーが受け入れやすくなります。
  • 新しい Kubelet 予測ボリュームの種類を使用して、ポッドにより安全に配布されます。

ブローカーでは、Kubernetes Token Review APIを使用してトークンが検証されます。 Kubernetes の TokenRequestProjection 機能を有効にして、audiences を指定します (1.21 以降では既定)。 この機能が有効になっていない場合、SAT は使用できません。

サービス アカウントの作成

SAT を作成するには、最初にサービス アカウントを作成します。 次のコマンドを使用すると、mqtt-client というサービス アカウントが作成されます。

kubectl create serviceaccount mqtt-client -n azure-iot-operations

認可用の属性を追加する

SAT 経由のクライアント認証では、必要に応じて、カスタム認可ポリシーで使用する属性で注釈が付けられた SAT を持つことができます。 詳細については、Kubernetes サービス アカウント トークンを使用するクライアントの認可に関するページを参照してください。

サービス アカウント トークン (SAT) 認証を有効にする

BrokerAuthentication リソースの authenticationMethods 設定を変更して、有効な認証方法として ServiceAccountToken を指定します。 audiences により、トークンの有効な対象ユーザーの一覧が指定されます。 MQTT ブローカー サービスを識別する一意の値を選択します。 1 つ以上の対象ユーザーを指定し、すべての SAT が指定された対象ユーザーのいずれかと一致する必要があります。

spec:
  authenticationMethods:
    - method: ServiceAccountToken
      serviceAccountTokenSettings:
        audiences:
        - "aio-internal"
        - "my-audience"

kubectl apply を使用して変更を適用します。 変更が有効になるまで、数分かかる場合があります。

SAT 認証をテストする

SAT 認証は、MQTT ブローカーと同じクラスター内のクライアントから使用する必要があります。 拡張した認証フィールドのみが許可されます。 認証方法を K8S-SAT に、認証データをトークンに設定します。

次のコマンドにより、Mosquitto クライアントを持つポッドが指定され、前の手順で作成した SAT がポッドにマウントされます。

apiVersion: v1
kind: Pod
metadata:
  name: mqtt-client
  namespace: azure-iot-operations
spec:
  serviceAccountName: mqtt-client
  containers:
  - image: efrecon/mqtt-client
    name: mqtt-client
    command: ["sleep", "infinity"]
    volumeMounts:
    - name: mqtt-client-token
      mountPath: /var/run/secrets/tokens
  volumes:
  - name: mqtt-client-token
    projected:
      sources:
      - serviceAccountToken:
          path: mqtt-client-token
          audience: my-audience
          expirationSeconds: 86400

ここで、ポッド構成の serviceAccountName フィールドは、使用されているトークンに関連付けられているサービス アカウントと一致する必要があります。 また、ポッド構成の serviceAccountToken.audience フィールドは、BrokerAuthentication リソースで構成された audiences のいずれかである必要があります。

ポッドが作成されたら、ポッドでシェルを開始します。

kubectl exec --stdin --tty mqtt-client -n azure-iot-operations -- sh

ポッドのシェルで次のコマンドを実行し、ブローカーにメッセージを発行します。

mosquitto_pub --host aio-broker --port 18883 --message "hello" --topic "world" --debug --cafile /var/run/certs/ca.crt -D CONNECT authentication-method 'K8S-SAT' -D CONNECT authentication-data $(cat /var/run/secrets/tokens/broker-sat)

出力は次のようになります。

Client (null) sending CONNECT
Client (null) received CONNACK (0)
Client (null) sending PUBLISH (d0, q0, r0, m1, 'world', ... (5 bytes))
Client (null) sending DISCONNECT

mosquitto クライアントは、/var/run/secrets/tokens/broker-sat にマウントされたサービス アカウント トークンを使用してブローカーで認証します。 トークンの有効期間は 24 時間です。 また、クライアントは、/var/run/certs/ca.crt にマウントされている既定のルート CA 証明書を使用して、ブローカーの TLS 証明書チェーンを検証します。

サービス アカウント トークンを更新する

サービス アカウント トークンは、限られた期間有効であり、expirationSeconds を使用して構成されます。 ただし、Kubernetes は、有効期限が切れる前にトークンを自動的に更新します。 トークンはバックグラウンドで更新されます。クライアントは、それを再フェッチする以外に何も行う必要はありません。

たとえば、「SAT 認証をテストする」の例にあるように、クライアントが、ボリュームとしてマウントされたトークンを使用するポッドである場合、最新のトークンは同じパス /var/run/secrets/tokens/mqtt-client-token で取得できます。 新しい接続を作成するときに、クライアントは最新のトークンをフェッチし、それを使用して認証を行うことができます。 クライアントには、最新のトークンをフェッチして接続を再試行することで MQTT 未承認エラーを処理するメカニズムも必要です。

カスタム認証

カスタム認証を使用して、指定された認証方法以外にもクライアント認証を拡張します。 サービスは API に準拠している限り任意であるため、"プラグ可能" です。

クライアントが MQTT ブローカーに接続し、カスタム認証が有効になっている場合、MQTT ブローカーは、クライアントが提示するすべての資格情報と共に HTTP 要求を使用して、クライアント資格情報の検証をカスタム認証サーバーに委任します。 カスタム認証サーバーは、クライアントの認可用の属性を使用して、クライアントに対して承認または拒否で応答します。

カスタム認証サービスを作成する

カスタム認証サーバーは、MQTT ブローカーとは別に実装およびデプロイされます。

サンプルのカスタム認証サーバーと手順は、GitHub で入手できます。 このサンプルをテンプレートとして使用し、独自のカスタム認証ロジックを実装するための開始点として使用します。

API

MQTT ブローカーとカスタム認証サーバーの間の API は、カスタム認証の API 仕様に従います。 OpenAPI 仕様は GitHub で入手できます。

TLS 暗号化を使用した HTTPS が必要

MQTT ブローカーは、機密性の高いクライアント資格情報を含む要求をカスタム認証サーバーに送信します。 これらの資格情報を保護するには、MQTT ブローカーとカスタム認証サーバー間の通信を TLS で暗号化する必要があります。

カスタム認証サーバーはサーバー証明書を提示する必要があり、MQTT ブローカーには、サーバー証明書を検証するための信頼されたルート CA 証明書が必要です。 必要に応じて、カスタム認証サーバーでは、自身を認証するためのクライアント証明書を提示するために MQTT ブローカーが必要になる場合があります。

リスナーのカスタム認証を有効にする

BrokerAuthentication リソースの authenticationMethods 設定を変更して、有効な認証方法として Custom を指定します。 次に、カスタム認証サーバーとの通信に必要なパラメータを指定します。

この例では、使用する可能性のあるすべてのパラメータを示します。 必要とされる正確なパラメータは、各カスタム サーバーの要件によって異なります。

spec:
  authenticationMethods:
    - method: Custom
      customSettings:
        # Endpoint for custom authentication requests. Required.
        endpoint: https://auth-server-template
        # Optional CA certificate for validating the custom authentication server's certificate.
        caCertConfigMap: custom-auth-ca
        # Authentication between MQTT broker with the custom authentication server.
        # The broker may present X.509 credentials or no credentials to the server.
        auth:
          x509:
            secretName: custom-auth-client-cert
            namespace: azure-iot-operations
        # Optional additional HTTP headers that the broker will send to the
        # custom authentication server.
        headers:
          header_key: header_value

資格情報の期限切れ後のクライアントの切断

MQTT ブローカーは、資格情報が期限切れになるとクライアントを切断します。 資格情報の期限切れ後の切断は、次のような MQTT ブローカー フロントエンドに接続するすべてのクライアントに適用されます。

  • SAT で認証されたクライアントは、SAT が期限切れになったときに切断されます
  • X.509 で認証されたクライアントは、クライアント証明書が期限切れになったときに切断されます
  • カスタム認証で認証されたクライアントは、カスタム認証サーバーから返された有効期限に基づいて切断されます。

切断すると、クライアントのネットワーク接続が閉じられます。 クライアントは MQTT DISCONNECT パケットを受信しませんが、ブローカーはクライアントを切断したというメッセージをログに記録します。

SAT とカスタム認証で認証された MQTT v5 クライアントは、最初の資格情報の期限切れ前に、新しい資格情報で再認証できます。 X.509 クライアントは再認証できず、認証を TLS レイヤーで行うために、接続を再確立する必要があります。

クライアントは、MQTT v5 AUTH パケットを送信することで再認証できます。

SAT クライアントは、method: K8S-SATdata: <token> フィールドを持つ AUTH クライアントを送信します。 カスタム認証クライアントは、カスタム認証サーバーで必要に応じてメソッドとデータ フィールドを設定します。

再認証が成功すると、クライアントの資格情報の有効期限が新しい資格情報の有効期限で更新され、ブローカーは Success AUTH パケットで応答します。 一時的な問題が原因で認証に失敗すると、ブローカーは ContinueAuthentication AUTH パケットで応答します。 たとえば、カスタム認証サーバーが使用できない場合などです。 クライアントは後で再試行することができます。 その他の認証エラーでは、ブローカーが DISCONNECT パケットを送信し、クライアントのネットワーク接続を閉じます。