Test connectivity to MQTT broker with MQTT clients

Important

Azure IoT Operations Preview – enabled by Azure Arc is currently in preview. You shouldn't use this preview software in production environments.

You'll need to deploy a new Azure IoT Operations installation when a generally available release becomes available. You won't be able to upgrade a preview installation.

For legal terms that apply to Azure features that are in beta, in preview, or otherwise not yet released into general availability, see the Supplemental Terms of Use for Microsoft Azure Previews.

This article shows different ways to test connectivity to MQTT broker with MQTT clients in a nonproduction environment.

By default, MQTT broker:

  • Deploys a TLS-enabled listener on port 18883 with ClusterIp as the service type. ClusterIp means that the broker is accessible only from within the Kubernetes cluster. To access the broker from outside the cluster, you must configure a service of type LoadBalancer or NodePort.

  • Accepts Kubernetes service accounts for authentication for connections from within the cluster. To connect from outside the cluster, you must configure a different authentication method.

Caution

For production scenarios, you should use TLS and service accounts authentication to secure your IoT solution. For more information, see:

Before you begin, install or configure IoT Operations. Use the following options to test connectivity to MQTT broker with MQTT clients in a nonproduction environment.

Connect to the default listener inside the cluster

The first option is to connect from within the cluster. This option uses the default configuration and requires no extra updates. The following examples show how to connect from within the cluster using plain Alpine Linux and a commonly used MQTT client, using the service account and default root CA certificate.

First, create a file named client.yaml with the following configuration:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: mqtt-client
  namespace: azure-iot-operations
---
apiVersion: v1
kind: Pod
metadata:
  name: mqtt-client
  # Namespace must match MQTT broker BrokerListener's namespace
  # Otherwise use the long hostname: aio-broker.azure-iot-operations.svc.cluster.local
  namespace: azure-iot-operations
spec:
  # Use the "mqtt-client" service account created from above
  # 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: broker-sat
      mountPath: /var/run/secrets/tokens
    - name: trust-bundle
      mountPath: /var/run/certs
  volumes:
  - name: broker-sat
    projected:
      sources:
      - serviceAccountToken:
          path: broker-sat
          audience: aio-internal # Must match audience in BrokerAuthentication
          expirationSeconds: 86400
  - name: trust-bundle
    configMap:
      name: azure-iot-operations-aio-ca-trust-bundle # Default root CA cert

Then, use kubectl to deploy the configuration. It should only take a few seconds to start.

kubectl apply -f client.yaml

Once the pod is running, use kubectl exec to run commands inside the pod.

For example, to publish a message to the broker, open a shell inside the pod:

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

Inside the pod's shell, run the following command to publish a message to the broker:

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)

The output should look similar to the following:

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

The mosquitto client uses the service account token mounted at /var/run/secrets/tokens/broker-sat to authenticate with the broker. The token is valid for 24 hours. The client also uses the default root CA cert mounted at /var/run/certs/ca.crt to verify the broker's TLS certificate chain.

Tip

You can use kubectl to download the default root CA certificate to use with other clients. For example, to download the default root CA certificate to a file named ca.crt:

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

To subscribe to the topic, run the following command:

mosquitto_sub --host aio-broker --port 18883 --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)

The output should look similar to the following:

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

The mosquitto client uses the same service account token and root CA cert to authenticate with the broker and subscribe to the topic.

To remove the pod, run kubectl delete pod mqtt-client -n azure-iot-operations.

Connect clients from outside the cluster

Since the default broker listener is set to ClusterIp service type, you can't connect to the broker from outside the cluster directly. To prevent unintentional disruption to communication between internal Azure IoT Operations components, we recommend keeping the default listener unmodified and dedicated for AIO internal communication. While it's possible to create a separate Kubernetes LoadBalancer service to expose the cluster IP service, it's better to create a separate listener with different settings, like more common MQTT port 1883 and 8883, to avoid confusion and potential security risks.

Node port

The easiest way to test connectivity is to use the NodePort service type in the listener. With that, you can use <nodeExternalIP>:<NodePort> to connect like in Kubernetes documentation.

For example, create a new broker listener with node port service type listening on port 1883:

  1. In the Azure portal, go to your IoT Operations instance.

  2. Under Azure IoT Operations resources, select MQTT Broker.

  3. Select MQTT broker listener for NodePort > Create. You can only create one listener per service type. If you already have a listener of the same service type, you can add more ports to the existing listener.

    Caution

    Setting authentication to None and not configuring TLS turns off authentication and TLS for testing purposes only.

    Enter the following settings:

    Setting Value
    Name nodeport
    Service name aio-broker-nodeport
    Port 1883
    Authentication Choose default or None
    Authorization Choose default
    Protocol Choose MQTT
    Node port 31883
  4. Add TLS settings to the listener by selecting TLS on the port.

    Setting Description
    TLS Select the Add button.
    TLS mode Choose Manual or Automatic.
    Issuer name Name of the cert-manager issuer. Required.
    Issuer kind Kind of the cert-manager issuer. Required.
    Issuer group Group of the cert-manager issuer. Required.
    Private key algorithm Algorithm for the private key.
    Private key rotation policy Policy for rotating the private key.
    DNS names DNS subject alternate names for the certificate.
    IP addresses IP addresses of the subject alternate names for the certificate.
    Secret name Kubernetes secret containing an X.509 client certificate.
    Duration Total lifetime of the TLS server certificate Defaults to 90 days.
    Renew before When to begin renewing the certificate.
  5. Select Apply to save the TLS settings.

  6. Select Create to create the listener.

Get the node's external IP address:

kubectl get nodes -o yaml | grep ExternalIP -C 1

The output should look similar to the following:

    - address: 104.197.41.11
      type: ExternalIP
    allocatable:
--
    - address: 23.251.152.56
      type: ExternalIP
    allocatable:
...

Use the external IP address and the node port to connect to the broker. For example, to publish a message to the broker:

mosquitto_pub --host <EXTERNAL_IP> --port 31883 --message "hello" --topic "world" --debug # Add authentication and TLS options matching listener settings

If there's no external IP in the output, you might be using a Kubernetes setup that doesn't expose the node's external IP address by default, like many setups of k3s, k3d, or minikube. In that case, you can access the broker with the internal IP along with the node port from machines on the same network. For example, to get the internal IP address of the node:

kubectl get nodes -o yaml | grep InternalIP -C 1

The output should look similar to the following:

    - address: 172.19.0.2
      type: InternalIP
    allocatable:

Then, use the internal IP address and the node port to connect to the broker from a machine within the same cluster. If Kubernetes is running on a local machine, like with single-node k3s, you can often use localhost instead of the internal IP address. If Kubernetes is running in a Docker container, like with k3d, the internal IP address corresponds to the container's IP address, and should be reachable from the host machine.

Load balancer

Another way to expose the broker to the internet is to use the LoadBalancer service type. This method is more complex and might require additional configuration, like setting up port forwarding.

For example, to create a new broker listener with load balancer service type listening on port 1883:

  1. In the Azure portal, go to your IoT Operations instance.

  2. Under Azure IoT Operations resources, select MQTT Broker.

  3. Select MQTT broker listener for NodePort > Create. You can only create one listener per service type. If you already have a listener of the same service type, you can add more ports to the existing listener.

    Caution

    Setting authentication to None and not configuring TLS turns off authentication and TLS for testing purposes only.

    Enter the following settings:

    Setting Value
    Name loadbalancer
    Service name aio-broker-loadbalancer
    Port 1883
    Authentication Choose default
    Authorization Choose default or None
    Protocol Choose MQTT
  4. You can add TLS settings to the listener by selecting TLS on the port.

    Setting Description
    TLS Select the Add button.
    TLS mode Choose Manual or Automatic.
    Issuer name Name of the cert-manager issuer. Required.
    Issuer kind Kind of the cert-manager issuer. Required.
    Issuer group Group of the cert-manager issuer. Required.
    Private key algorithm Algorithm for the private key.
    Private key rotation policy Policy for rotating the private key.
    DNS names DNS subject alternate names for the certificate.
    IP addresses IP addresses of the subject alternate names for the certificate.
    Secret name Kubernetes secret containing an X.509 client certificate.
    Duration Total lifetime of the TLS server certificate Defaults to 90 days.
    Renew before When to begin renewing the certificate.
  5. Select Apply to save the TLS settings.

  6. Select Create to create the listener.

Get the external IP address for the broker's service:

kubectl get service aio-broker-loadbalancer --namespace azure-iot-operations

If the output looks similar to the following:

NAME                      TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
aio-broker-loadbalancer   LoadBalancer   10.x.x.x        x.x.x.x       1883:30382/TCP   83s

This means that an external IP has been assigned to the load balancer service, and you can use the external IP address and the port to connect to the broker. For example, to publish a message to the broker:

mosquitto_pub --host <EXTERNAL_IP> --port 1883 --message "hello" --topic "world" --debug # Add authentication and TLS options matching listener settings

If the external IP is not assigned, you might need to use port forwarding or a virtual switch to access the broker.

Use port forwarding

With minikube, kind, and other cluster emulation systems, an external IP might not be automatically assigned. For example, it might show as Pending state.

  1. To access the broker, forward the broker listening port 18883 to the host.

    kubectl port-forward --namespace azure-iot-operations service/aio-broker 18883:mqtts-18883
    
  2. Use 127.0.0.1 to connect to the broker at port 18883 with the same authentication and TLS configuration as the example without port forwarding.

For more information about minikube, see Use Port Forwarding to Access Applications in a Cluster

Port forwarding on AKS Edge Essentials

For Azure Kubernetes Services Edge Essentials, you need to perform a few additional steps. With AKS Edge Essentials, getting the external IP address might not be enough to connect to the broker. You might need to set up port forwarding and open the port on the firewall to allow traffic to the broker's service.

  1. First, get the external IP address of the broker's load balancer listener:

    kubectl get service broker-loadbalancer --namespace azure-iot-operations
    

    Output should look similar to the following:

    NAME                    TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
    broker-loadbalancer     LoadBalancer   10.x.x.x       192.168.0.4   1883:30366/TCP   14h
    
  2. Set up port forwarding to the broker-loadbalancer service on the external IP address 192.168.0.4 and port 1883:

    netsh interface portproxy add v4tov4 listenport=1883 connectport=1883 connectaddress=192.168.0.4
    
  3. Open the port on the firewall to allow traffic to the broker's service:

    New-NetFirewallRule -DisplayName "AIO MQTT Broker" -Direction Inbound -Protocol TCP -LocalPort 1883 -Action Allow
    
  4. Use the host's public IP address to connect to the MQTT broker.

For more information about port forwarding, see Expose Kubernetes services to external devices.

Access through localhost

Some Kubernetes distributions can expose MQTT broker to a port on the host system (localhost) as part of cluster configuration. Use this approach to make it easier for clients on the same host to access MQTT broker.

For example, to create a K3d cluster with mapping the MQTT broker's default MQTT port 1883 to localhost:1883:

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

Or to update an existing cluster:

k3d cluster edit <CLUSTER_NAME> --port-add '1883:1883@loadbalancer'

Then, use localhost and the port to connect to the broker. For example, to publish a message to the broker:

mosquitto_pub --host localhost --port 1883 --message "hello" --topic "world" --debug # Add authentication and TLS options matching listener settings

Only turn off TLS and authentication for testing

The reason that MQTT broker uses TLS and service accounts authentication by default is to provide a secure-by-default experience that minimizes inadvertent exposure of your IoT solution to attackers. You shouldn't turn off TLS and authentication in production. Exposing MQTT broker to the internet without authentication and TLS can lead to unauthorized access and even DDOS attacks.

Warning

If you understand the risks and need to use an insecure port in a well-controlled environment, you can turn off TLS and authentication for testing purposes by removing the tls and authenticationRef settings from the listener configuration.

  1. In the Azure portal, go to your IoT Operations instance.

  2. Under Azure IoT Operations resources, select MQTT Broker.

  3. Select MQTT broker listener for NodePort > Create. You can only create one listener per service type. If you already have a listener of the same service type, you can add more ports to the existing listener.

    Caution

    Setting authentication to None and not configuring TLS turns off authentication and TLS for testing purposes only.

    Enter the following settings:

    Setting Value
    Name Enter a name for the listener
    Service name Enter a service name
    Port 1883
    Authentication Choose None
    Authorization Choose None
    Protocol Choose MQTT
    Node port 31883 if using node port
  4. Select Create to create the listener.