MQTT クライアントを使用して MQTT ブローカーへの接続をテストする

重要

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

一般公開されたリリースが利用可能になった場合に、新しい Azure IoT Operations を表示する必要があります。プレビュー段階のインストールをアップグレードすることはできません。

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

この記事では、MQTT クライアントを使用して非運用環境で MQTT ブローカーへの接続をテストするさまざまな方法について説明します。

既定値の MQTT ブローカー:

  • サービスの種類として ClusterIp を指定し、ポート 8883 に TLS 対応リスナーを配置します。 ClusterIp は、ブローカーが Kubernetes クラスター内からのみアクセス可能であることを意味します。 クラスターの外部からブローカーにアクセスするには、サービスの種類を LoadBalancer または NodePort として構成する必要があります。

  • クラスター内からの接続に対して認証用の Kubernetes サービス アカウントのみを受け入れます。 クラスターの外部から接続するには、別の認証方法を構成する必要があります。

注意事項

運用環境のシナリオでは、TLS とサービス アカウント認証を使用して IoT ソリューションをセキュリティで保護する必要があります。 詳細については、以下を参照してください:

始める前に、IoT Operations をインストールまたは構成します。 非運用環境で MQTT クライアントを使用して MQTT ブローカーへの接続をテストするには、次のオプションを使用します。

既定の構成でクラスター内のポッドから接続する

最初のオプションは、クラスター内から接続するものです。 このオプションでは既定の構成が使用され、追加の更新は必要ありません。 次の例は、プレーンな Alpine Linux と一般的に使用される MQTT クライアントを使用してクラスター内から接続する方法を示しています。サービス アカウントと既定のルート CA 証明書を使用します。

  1. 次の構成を使用して client.yaml という名前のファイルを作成します。

    apiVersion: v1
    kind: Pod
    metadata:
      name: mqtt-client
      # Namespace must match MQTT broker BrokerListener's namespace
      # Otherwise use the long hostname: aio-mq-dmqtt-frontend.azure-iot-operations.svc.cluster.local
      namespace: azure-iot-operations
    spec:
      # Use the "mqtt-client" service account which comes with default deployment
      # Otherwise create it with `kubectl create serviceaccount mqtt-client -n azure-iot-operations`
      serviceAccountName: mqtt-client
      containers:
        # Mosquitto and mqttui on Alpine
      - image: alpine
        name: mqtt-client
        command: ["sh", "-c"]
        args: ["apk add mosquitto-clients mqttui && sleep infinity"]
        volumeMounts:
        - name: mq-sat
          mountPath: /var/run/secrets/tokens
        - name: trust-bundle
          mountPath: /var/run/certs
      volumes:
      - name: mq-sat
        projected:
          sources:
          - serviceAccountToken:
              path: mq-sat
              audience: aio-mq # Must match audience in BrokerAuthentication
              expirationSeconds: 86400
      - name: trust-bundle
        configMap:
          name: aio-ca-trust-bundle-test-only # Default root CA cert
    
  2. kubectl apply -f client.yaml を使用して構成をデプロイします。 わずか数秒で開始します。

  3. ポッドが実行されたら、kubectl exec を使用してポッド内でコマンドを実行します。

    たとえば、ブローカーにメッセージを発行するには、ポッド内でシェルを開きます。

    kubectl exec --stdin --tty mqtt-client --namespace azure-iot-operations -- sh
    
  4. ポッドのシェルで次のコマンドを実行し、ブローカーにメッセージを発行します。

    mosquitto_pub --host aio-mq-dmqtt-frontend --port 8883 --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/mq-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/mq-sat にマウントされたサービス アカウント トークンを使用してブローカーで認証します。 トークンの有効期間は 24 時間です。 また、クライアントは、/var/run/certs/ca.crt にマウントされている既定のルート CA 証明書を使用して、ブローカーの TLS 証明書チェーンを検証します。

  5. トピックにサブスクライブするには、次のコマンドを実行します。

    mosquitto_sub --host aio-mq-dmqtt-frontend --port 8883 --topic "world" --debug --cafile /var/run/certs/ca.crt -D CONNECT authentication-method 'K8S-SAT' -D CONNECT authentication-data $(cat /var/run/secrets/tokens/mq-sat)
    

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

    Client (null) sending CONNECT
    Client (null) received CONNACK (0)
    Client (null) sending SUBSCRIBE (Mid: 1, Topic: world, QoS: 0, Options: 0x00)
    Client (null) received SUBACK
    Subscribed (mid: 1): 0
    

    mosquitto クライアントは、同じサービス アカウント トークンとルート CA 証明書を使用してブローカーで認証し、トピックにサブスクライブします。

  6. ポッドを削除するには、kubectl delete pod mqtt-client -n azure-iot-operations を実行します。

クラスターの外部からクライアントを接続して、既定の TLS ポートに接続します

TLS 信頼チェーン

ブローカーは TLS を使用するため、クライアントはブローカーの TLS 証明書チェーンを信頼する必要があります。 ブローカーによって使用されるルート CA 証明書を信頼するようにクライアントを構成する必要があります。

既定のルート CA 証明書を使用するには、aio-ca-trust-bundle-test-only ConfigMap からダウンロードします。

kubectl get configmap aio-ca-trust-bundle-test-only -n azure-iot-operations -o jsonpath='{.data.ca\.crt}' > ca.crt

ダウンロードした ca.crt ファイルを使用して、ブローカーの TLS 証明書チェーンを信頼するようにクライアントを構成します。

ブローカーで認証する

既定では、MQTT ブローカーはクラスター内からの接続に対して認証用の Kubernetes サービス アカウントのみを受け入れます。 クラスターの外部から接続するには、X.509 などの別の認証方法を構成する必要があります。 詳しくは、「認証の構成」をご覧ください。

資格情報をオフにできるのは、テスト目的の場合のみです

テスト目的で認証をオフにするには、BrokerListener リソースを編集し、authenticationEnabled フィールドを falseに設定します。

注意事項

認証をオフにする場合は、インターネットからアクセスできないテスト クラスターを使用し、使用をテスト目的のみに限る必要があります。

kubectl patch brokerlistener listener -n azure-iot-operations --type='json' -p='[{"op": "replace", "path": "/spec/authenticationEnabled", "value": false}]'

ポート接続

一部の Kubernetes ディストリビューションでは、ホスト システム (localhost) 上のポートで MQTT ブローカーを公開できます。 同じホスト上のクライアントが MQTT ブローカーにアクセスしやすくなるため、この方法を使用する必要があります。

たとえば、MQTT ブローカーの既定の MQTT ポート 8883 を localhost:8883 にマッピングした K3d クラスターを作成するには、次のようにします。

k3d cluster create --port '8883:8883@loadbalancer'

ただし、この方法で MQTT ブローカーを使用するには、クラスター IP ではなくロード バランサーを使用するように構成する必要があります。 これを行うには、2 つの方法があります。ロード バランサーを作成するか、既存の既定の BrokerListener リソース サービスの種類にパッチを適用してロード バランサーに適用します。

オプション 1: ロード バランサーを作成する

  1. 次の構成を使用して loadbalancer.yaml という名前のファイルを作成します。

    apiVersion: v1
    kind: Service
    metadata:
        name: iotmq-public-svc
    spec:
        type: LoadBalancer
        ports:
        - name: mqtt1
          port: 8883
          targetPort: 8883
        selector:
          app: broker
          app.kubernetes.io/instance: broker
          app.kubernetes.io/managed-by: dmqtt-operator
          app.kubernetes.io/name: dmqtt
          tier: frontend
    
  2. 構成を適用してロード バランサー サービスを作成します。

    kubectl apply -f loadbalancer.yaml
    

オプション 2: 既定のロード バランサーにパッチを適用する

  1. BrokerListener リソースを編集し、serviceType フィールドを loadBalancer に変更します。

    kubectl patch brokerlistener listener --namespace azure-iot-operations --type='json' --patch='[{"op": "replace", "path": "/spec/serviceType", "value": "loadBalancer"}]'
    
  2. サービスが更新されるまで待ちます。

    kubectl get service aio-mq-dmqtt-frontend --namespace azure-iot-operations
    

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

    NAME                    TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
    aio-mq-dmqtt-frontend   LoadBalancer   10.43.107.11   XXX.XX.X.X    8883:30366/TCP   14h
    
  3. 外部 IP アドレスを使用して、インターネット経由で MQTT ブローカーに接続できます。 localhost の代わりに外部 IP アドレスを使用してください。

    mosquitto_pub --qos 1 --debug -h XXX.XX.X.X --message hello --topic world --username client1 --pw password --cafile ca.crt
    

ヒント

外部 IP アドレスを使用して、クラスターの外部から MQTT ブローカーに接続できます。 ポート転送オプションで K3d コマンドを使用した場合は、localhost を使用して MQTT ブローカーに接続できます。 たとえば、mosquitto クライアントと接続するには、次のようにします。

mosquitto_pub --qos 1 --debug -h localhost --message hello --topic world --username client1 --pw password --cafile ca.crt --insecure

この例では、mosquitto クライアントは、ユーザー名とパスワードを使用して、ブローカーを認証します。その際、ブローカーの TLS 証明書チェーンを確認するために、ルート CA 証明書も使用します。 ここでは、ロード バランサーに発行される既定の TLS 証明書が、ロード バランサーの既定のサービス名 (aio-mq-dmqtt-frontend) と割り当てられた IP (localhost ではなく) に対してのみ有効であるため、--insecure フラグが必要です。

認証と TLS なしで MQTT ブローカーポートをインターネットに公開しないでください。 これは危険であり、IoT デバイスへの不正アクセスを招いたり、未承諾のトラフィックがクラスターに送信されたりする恐れがあります。

セキュリティで保護されていないフラグを使用しないように、証明書のサブジェクトの別名 (SAN) に localhost を追加する方法については、「サーバー証明書パラメーターの構成」を参照してください。

ポート フォワーディングの使用

minikubekind、およびその他のクラスター エミュレーション システムでは、外部 IP が自動的に割り当てられない可能性があります。 たとえば、保留中状態として表示される場合があります。

  1. ブローカーにアクセスするには、ブローカー リスニング・ポート 8883 をホストに転送します。

    kubectl port-forward --namespace azure-iot-operations service/aio-mq-dmqtt-frontend 8883:mqtts-8883
    
  2. ポート フォワーディングを行わないで、例と同じ認証と TLS 構成を使用する場合、127.0.0.1 を使用してポート 8883 のブローカーに接続します。

ポート フォワーディングは、ブローカーの構成を変更することなく、開発用マシンで MQTT ブローカーをローカルでテストする場合にも役立ちます。 minikube の詳細については、ポート フォワーディングを使用してクラスターにあるアプリケーションにアクセスすることに関する記事をご覧ください

AKS Edge Essentials でのポート フォワーディング

Azure Kubernetes Services Edge Essentials の場合は、いくつかの手順を追加で実行する必要があります。 ポート フォワーディングの詳細については、Expose Kubernetes サービスを外部デバイスに公開するに関する記事をご覧くださいください。

  1. ブローカーのサービスが、ロード バランサーを使用して外部 IP に公開されているとします。 たとえば、既定のロード バランサー aio-mq-dmqtt-frontend にパッチを適用した場合は、サービスの外部 IP アドレスを取得します。

    kubectl get service aio-mq-dmqtt-frontend --namespace azure-iot-operations
    

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

    NAME                    TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
    aio-mq-dmqtt-frontend   LoadBalancer   10.43.107.11   192.168.0.4   8883:30366/TCP   14h
    
  2. 外部 IP アドレス 192.168.0.4 とポート 8883 で、aio-mq-dmqtt-frontend サービスへのポート フォワーディングを設定します。

    netsh interface portproxy add v4tov4 listenport=8883 connectport=8883 connectaddress=192.168.0.4
    
  3. ファイアウォールでポートを開き、ブローカーのサービスへのトラフィックを許可します。

    New-NetFirewallRule -DisplayName "AIO MQTT Broker" -Direction Inbound -Protocol TCP -LocalPort 8883 -Action Allow
    
  4. ホストのパブリック IP アドレスを使用して MQTT ブローカーに接続します。

TLS と認証なし

MQTT ブローカーが既定で TLS とサービス アカウント認証を使用する理由は、最初からセキュリティで保護されたエクスペリエンスを提供することで、IoT ソリューションが攻撃者に対して不注意にさらされるのを最小限に抑えるためです。 運用環境では TLS と認証をオフにしないでください。

注意事項

運用環境では使用しないでください。 認証と TLS なしで MQTT ブローカーをインターネットに公開すると、不正アクセスや DDOS 攻撃につながる可能性があります。

リスクを理解したうえで、必要があり、管理の行き届いた環境で安全でないポートを使用する場合は、次の手順に従ってテスト目的で TLS と認証をオフにすることができます。

  1. TLS 設定なしで新しい BrokerListener リソースを作成します。

    apiVersion: mqttbroker.iotoperations.azure.com/v1beta1
    kind: BrokerListener
    metadata:
      name: non-tls-listener
      namespace: azure-iot-operations
    spec:
      brokerRef: broker
      serviceType: loadBalancer
      serviceName: my-unique-service-name
      authenticationEnabled: false
      authorizationEnabled: false
      port: 1883
    

    authenticationEnabledauthorizationEnabled フィールドを false に設定すると、認証と承認がオフにされます。 port フィールドを 1883 に設定すると、共通の MQTT ポートが使用されます。

  2. サービスが更新されるまで待ちます。

    kubectl get service my-unique-service-name --namespace azure-iot-operations
    

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

    NAME                     TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
    my-unique-service-name   LoadBalancer   10.43.144.182   XXX.XX.X.X    1883:31001/TCP   5m11s
    

    新しいポート 1883 が使用できるようになります。

  3. mosquitto クライアントを使用してブローカーに接続します。

    mosquitto_pub --qos 1 --debug -h localhost --message hello --topic world
    

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

    Client mosq-7JGM4INbc5N1RaRxbW sending CONNECT
    Client mosq-7JGM4INbc5N1RaRxbW received CONNACK (0)
    Client mosq-7JGM4INbc5N1RaRxbW sending PUBLISH (d0, q1, r0, m1, 'world', ... (5 bytes))
    Client mosq-7JGM4INbc5N1RaRxbW received PUBACK (Mid: 1, RC:0)
    Client mosq-7JGM4INbc5N1RaRxbW sending DISCONNECT