Metrika
Ivy Knob

VerneMQ: ACLs and auth in MQTT broker from external JSON API

VerneMQ: ACLs and auth in MQTT broker from external JSON API

MQTT broker is essential thing for connecting Internet of Thing devices to the cloud. To securely connect the device to the broker a number of ways can be used. I will talk about authentication and authorization of the device with my favorite VerneMQ broker.

Why VerneMQ?

There are a lot of solutions on the market including open source, but not all of them are great for industrial applications.

Mosquitto broker is C-based software, its the most popular for test purpuses, easy to use, but lacking MQTT5.0 and clustering capabilities.

HiveMQ is commercially available Java-based broker. Java can do anything, but its not made for connection handling. And I personally dont like Java.

EMQTT / EMQ X is open source Erlang-based solution from China. Erlang is perfect for message broker (heard of RabbitMQ?), but this solution is a mess when it comes to license/features. It does support all modern features, like MQTT5.0 and clustering though.

VerneMQ is based on Erlang language, open source and made in Switzerland. It supports clustering, MQTT5.0 and lua-based plugin system. It's the newest and fastly growthing project without "surprises".

Little bit of terminology and history

Authentication is the process of knowing if a device is lying or not about its identity. For example, in case of username and password, if device provides valid username and valid password - it is authentified as of valid identity.

Authorization is the process of knowing if device has rights to perform some action. For example, publishing or subscribing to the channel.

All brokers have username/password authentication and ACL (access-list) authorization built in. But it the real world, these methods can't be applied due to limited capabilities.

Ideally, all the logic about authentication and authorization should be separated to IAM (identity and access management) service.

Almost all the brokers support separate process of authentication and authorization via http request. But this approach may be bad in some situations. Every time device wants to publish something to the channel, http request is made to the IAM service. And authorization rules for devices does not change frequently. But is it possible to use one request for both processes? It's possible with VerneMQ.

Requirements

  1. Docker. I will run VerneMQ in docker.
  2. Backend that will handle auth/auth process and respond with json. Backend should run on 3000 port on your local machine.

Instruction

1. Create auth_http.lua file in the directory you will run VerneMQ docker command with content:

require "auth/auth_commons"

URL = "http://docker.for.mac.localhost:3000/api/v1/vernemqs"

function auth_on_register(reg)
if reg.username ~= nil and reg.password ~= nil then
key = json.encode({
mountpoint = reg.mountpoint,
client_id = reg.client_id,
username = reg.username,
password = reg.password
})
headers = {}
headers["x_post_header"] = "X-POST-HEADER"
headers['Accept'] = "application/json"
headers["Content-Type"] = "application/json"
ret = http.post(pool, URL, key, headers)
if ret.status == 200 and ret.ref then
body = http.body(ret.ref)
json = json.decode(body)
cache_insert(
reg.mountpoint,
reg.client_id,
reg.username,
json.publish_acl,
json.subscribe_acl
)
return true
else
return false
end
end
return false
end

pool = "auth_http"
config = {
pool_id = pool
}

http.ensure_pool(config)

hooks = {
auth_on_register = auth_on_register,
auth_on_publish = auth_on_publish,
auth_on_subscribe = auth_on_subscribe,
on_unsubscribe = on_unsubscribe,
on_client_gone = on_client_gone,
on_client_offline = on_client_offline,

auth_on_register_m5 = auth_on_register_m5,
auth_on_publish_m5 = auth_on_publish_m5,
auth_on_subscribe_m5 = auth_on_subscribe_m5,
}

2. Run this command in the same directory as auth_http.lua. It disables `vmq_passwd` and vmq_acl plugins.

docker run -p 1883:1883 \
--rm --name vernemq \
-e "DOCKER_VERNEMQ_ALLOW_ANONYMOUS=off" \
-e "DOCKER_VERNEMQ_PLUGINS__VMQ_PASSWD=off" \
-e "DOCKER_VERNEMQ_PLUGINS__VMQ_ACL=off" \
-e "DOCKER_VERNEMQ_PLUGINS__VMQ_DIVERSITY=on" \
-e "DOCKER_VERNEMQ_VMQ_DIVERSITY__HTTP_AUTH__file=/auth_http.lua" \
-v $(pwd)/auth_http.lua:/auth_http.lua \
erlio/docker-vernemq

3. Your backend should generate json-response like this:

{
"publish_acl": [
{
"pattern": "data/sensors"
}
],
"result": "ok",
"subscribe_acl": [
{
"pattern": "data/sensors"
}
]
}

What's next?

This simple example is only the beginning. Not every device needs uniq ACLs. For example, node devices requires static ACL with wildcard and have similar rules. Sensors should only send to they own channel with ClientID (or username) in it's name, subscribe only to its local administration commands channel and to general sensor commands channel.

In the next series we will provide a mixed solution with static and dynamic ACLs for different types of users and devices. Stay tuned.