When it comes to authentication/authorization in Vernemq, I expected that multiple enabled plugins would be checked in order to determine if a connect/pub/sub is permitted or not.
However, I am experiencing different results in my deployment.
If only one of the auth plugins - either vmq.acl
or vmq.diversity
- is enabled, it works as expected.
But, with both plugins enabled, only vmq.diversity
seems to be working.
What I would like to ask is:
Is this the expected behaviour of vernemq
?
If not, how can multiple auth plugins be enabled?
With multiple auth plugins enabled, in what order are they checked?
The vernemq service is run via docker compose
as described below.
# docker-compose.yaml
services:
vernemq:
image: vernemq/vernemq
container_name: vernemq_broker
environment:
- DOCKER_VERNEMQ_ACCEPT_EULA=yes
- DOCKER_VERNEMQ_ALLOW_ANONYMOUS=off
- DOCKER_VERNEMQ_PLUGINS__VMQ_PASSWD=on
- DOCKER_VERNEMQ_PLUGINS__VMQ_ACL=on
- DOCKER_VERNEMQ_VMQ_ACL__ACL_RELOAD_INTERVAL=10
- DOCKER_VERNEMQ_PLUGINS__VMQ_DIVERSITY=on
- DOCKER_VERNEMQ_VMQ_DIVERSITY__AUTH_POSTGRES__ENABLED=on
- DOCKER_VERNEMQ_VMQ_DIVERSITY__POSTGRES__HOST=auth_db
- DOCKER_VERNEMQ_VMQ_DIVERSITY__POSTGRES__PORT=5432
- DOCKER_VERNEMQ_VMQ_DIVERSITY__POSTGRES__USER=postgres
- DOCKER_VERNEMQ_VMQ_DIVERSITY__POSTGRES__PASSWORD=password
- DOCKER_VERNEMQ_VMQ_DIVERSITY__POSTGRES__DATABASE=postgres
- DOCKER_VERNEMQ_VMQ_DIVERSITY__POSTGRES__PASSWORD_HASH_METHOD=crypt
# insecure approach to setting password - for testing/development only
- DOCKER_VERNEMQ_USER_file_user1=file_user1_password
- DOCKER_VERNEMQ_USER_file_user2=file_user2_password
- DOCKER_VERNEMQ_LOG__CONSOLE__LEVEL=debuga
volumes:
- ./vernemq/vmq.acl:/etc/vernemq/vmq.acl
ports:
- 1883:1883
depends_on:
- auth_db
auth_db:
image: postgres:latest
container_name: vernemq_auth_db
environment:
- POSTGRES_PASSWORD=password
- POSTGRES_USER=postgres
- POSTGRES_DB=postgres
volumes:
- ./auth_db/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql
ports:
- 5432:5432
expose:
- 5432
# vmq.acl
user file-user1
topic private/file/#
topic public/#
user file-user2
topic private/file/#
topic public/#
The authentication database is initialised with the script:
-- init-db.sql
CREATE EXTENSION pgcrypto;
CREATE TABLE IF NOT EXISTS vmq_auth_acl (
mountpoint character varying(10) NOT NULL,
client_id character varying(128) NOT NULL,
username character varying(128) NOT NULL,
password character varying(128),
publish_acl json,
subscribe_acl json,
CONSTRAINT vmq_auth_acl_primary_key PRIMARY KEY (mountpoint, client_id, username)
);
CREATE OR REPLACE PROCEDURE insert_user(user_ref text)
LANGUAGE SQL AS
$$
WITH x AS (
SELECT ''::text AS mountpoint,
CONCAT('db-', user_ref, '-client-id')::text AS client_id, -- eg: 'db-user1-client-id'
CONCAT('db-', user_ref)::text AS username, -- eg: 'db-user1'
CONCAT('db-', user_ref, '-password')::text AS password, -- eg: 'db-user1-password'
gen_salt('bf')::text AS salt,
'[{"pattern": "private/db/#"}, {"pattern": "public/#"}]'::json AS publish_acl,
'[{"pattern": "private/db/#"}, {"pattern": "public/#"}]'::json AS subscribe_acl
)
INSERT INTO vmq_auth_acl (
mountpoint,
client_id,
username,
password,
publish_acl,
subscribe_acl
)
SELECT
x.mountpoint,
x.client_id,
x.username,
crypt(x.password, x.salt),
publish_acl,
subscribe_acl
FROM x ON CONFLICT (mountpoint, client_id, username) DO NOTHING;
$$;
--
--
CALL insert_user('user1'); -- inserts row for user 'db-user1'
CALL insert_user('user2'); -- inserts row for user 'db-user2'
With the above configuration and files, there are the following users defined under the following plugins.
file-based authentication - vmq.passwd
plugin
file-user1
file-user2
authentication via postgres database - vmq.diversity
plugin
db-user1
db-user2
When both plugins are enabled, only the user defined in the database are able to authenticate and connect.
In general, an authentication plugin (implementing the auth_on_register
-hook) will give an internal response to the broker. If the plugin authenticates the client, the response will be ok
. The response can also be next
to tell the broker that the current plugin cannot authenticate the client, so the broker should just go check the next
plugin.
This was mostly intended a way to allow users to chain their own plugins. It was not implemented for the file-based plugin, for reasons of compatibility to the Mosquitto format.
You can easily implement a chain in a vmq_diversity
plugin, though, as you can adapt the Lua scripts.
1. Is this the expected behaviour of vernemq?
Right now, yes, see above explanation
2. If not, how can multiple auth plugins be enabled?
By making the plugins return next
instead of an authentication error. Verne will then just go through all the plugins until it runs out of plugins to ask. You will always see the typical plugin_chain_exhausted
logged as a reason for a rejected client. Let me know when you need more help with that.
3. With multiple auth plugins enabled, in what order are they checked?
They are checked in load order (whether you load the plugins internally via vernemq.conf
file or externally via the plugin enable command). The order should be visible in the vmq-admin plugin show
command too.