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.passwdpluginfile-user1file-user2
authentication via postgres database -
vmq.diversityplugindb-user1db-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 beok. The response can also benextto tell the broker that the current plugin cannot authenticate the client, so the broker should just go check thenextplugin.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_diversityplugin, though, as you can adapt the Lua scripts.Right now, yes, see above explanation
By making the plugins return
nextinstead 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 typicalplugin_chain_exhaustedlogged as a reason for a rejected client. Let me know when you need more help with that.They are checked in load order (whether you load the plugins internally via
vernemq.conffile or externally via the plugin enable command). The order should be visible in thevmq-admin plugin showcommand too.