Mosquitto ignores bind_interface on a websockets listener
I put an MQTT web client in front of Mosquitto and tried to keep the WebSocket port cluster-only. bind_interface does nothing for websockets listeners in 2.0.22, an interface name as host hangs the broker, and a pod IP rendered by an init container is what works.
I wanted a browser MQTT client for the cluster’s Mosquitto. Browser clients can only speak MQTT over WebSocket, so the broker needed a second listener, and the decision was that this listener stays inside the cluster. The broker also sits on the LAN through a macvlan interface, and I did not want a WebSocket port open there, on a broker that allows anonymous access.
What I expected
bind_interface exists in Mosquitto’s config and restricts a listener to
one network interface. I set it to the pod’s own interface, deployed, and
called it done.
What happened
The UI worked, and so did the secure WebSocket through the ingress. Then I
checked the LAN address and port 9001 answered there too. Reading
/proc/net/tcp inside the pod showed why: the socket was bound to
0.0.0.0. In Mosquitto 2.0.22, bind_interface has no effect on a
protocol websockets listener, though it works for the plain MQTT one.
Two things that don’t work
Putting the interface name where the address goes, listener 9001 eth0,
looks plausible and is far worse. The broker logs “Opening websockets listen
socket”, never logs that it is running, and every handshake times out. I
would not deploy that.
What does work
listener 9001 <IP address> does restrict a websockets listener. I tested
it with two Docker networks: the handshake succeeds on the bound address,
gets no answer on the other, and port 1883 is unaffected. The problem is
that a pod does not know its address when you write the ConfigMap.
The fix is a template in the ConfigMap, with a placeholder such as
listener 9001 POD_IP, and an init container that renders it into a
config in an emptyDir using the downward API value status.podIP.
Mosquitto reads the rendered file. After the change 9001 was bound to the
pod address only, the secure WebSocket returned 101, the LAN address no
longer answered, and 1883 was untouched.
Smaller facts from the same job
- Without
per_listener_settings,allow_anonymous trueapplies to the new listener as well. Convenient, and a reason to keep the port private. - Mosquitto ignores the URL path on a websockets listener, so the ingress
rule decides it.
/mqttis the default in MQTTX. - The MQTTX Web image is a Node image running
http-serveras root on port 80. Override the command to use port 8080 and it runs as an unprivileged user with a read-only root filesystem. - MQTTX Web keeps connections in the browser. There is no way to pre-seed a default one from the deployment.