Skip to content

Commit 6927ee5

Browse files
Adding new means for brigde authentication.
This patch introduces a new way to provide credentials for bridge authentication. Currently the only way to provide bridge authentication credentials is to set remote_username and remote_password in the configuration file. There was no other way to provide these credentials. But for example on some platforms these credentials are stored in a TPM. There is no direct way to pull these credentials from TPM and use it for bridge authentication. With this patch these credentials can be provided by a plugin that yields username/password on bridge setup and will be used for broker authentication. Customers can provide this plugin without publishing their source code. For more details see: doc/brigde-authentication-by-plugin.md Signed-off-by: Thorsten Wendt <thorsten.wendt@ebblo.com>
1 parent 88b90dd commit 6927ee5

File tree

19 files changed

+1063
-1
lines changed

19 files changed

+1063
-1
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
---
2+
title: 'Mosquitto: using a plugin to authenticate a bridge'
3+
tags:
4+
- Bridge
5+
- MQTT
6+
- Authentication
7+
authors:
8+
- name: Thorsten Wendt
9+
date: 22 December 2025
10+
---
11+
12+
# Introduction
13+
14+
This patch introduces a new way to provide credentials for bridge
15+
authentication.
16+
17+
Currently the only way to provide authentication credentials was to
18+
set __remote\_username__ and __remote\_password__ in the configuration
19+
file. There was no other way to provide these credentials.
20+
21+
But for example on some platforms these credentials are stored in
22+
a TPM. There is no direct way to pull these credentials from TPM and
23+
use it for bridge authentication.
24+
25+
With this patch these credentials can be provided by a **plugin** that
26+
yields _username_/_password_ on bridge setup and will be used for
27+
broker authentication. Customers can provide this **plugin**
28+
without publishing their source code.
29+
30+
# Plugin interface
31+
32+
The plugin have to register for event **MOSQ_EVT_LOAD_BRIDGE_CRED**.
33+
34+
The callback then will be called before the bride is going to send credentials
35+
to the configured broker. The plugin can set the entries for username and password in the
36+
structure **mosquitto_evt_load_bridge_cred** delivered with the parameter _event\_data_.
37+
38+
The callback can return one of the following value
39+
40+
* MOSQ_ERR_SUCCESS - Plugin provided username and password
41+
* MOSQ_ERR_PLUGIN_DEFER - Plugin is not able to provide username/password
42+
* MOSQ_ERR_AUTH_DENIED - Authentication is denied at all
43+
* MOSQ_ERR_NOT_FOUND - There will be no username/password at all
44+
* MOSQ_ERR_NOMEM - Out of memory
45+
46+
# Bridge's behavior
47+
48+
For every bridge the callback(s) of configured auth bridge plugins will be called.
49+
50+
**MOSQ_ERR_SUCCESS:** Username/password is set by the plugin will be used to send it to the broker.
51+
52+
**MOSQ_ERR_PLUGIN_DEFER:** Will call the next plugin if configured otherwise existing remote_username/remote_password will be used to send it to the broker.
53+
54+
**MOSQ_ERR_AUTH_DENIED:** No other plugin will be called and even existing remote_username/remote_password will be ignored. No username/password will be send to the broker.
55+
56+
**MOSQ_ERR_NOT_FOUND:** No further bridge auth plugin will be called even when configured, instead configured remote_username/remote_password will be used to send it to the broker.
57+
58+
**MOSQ_ERR_NOMEM:** Any action to handle bridge configuration will be aborted immediately.
59+
60+
# Examples
61+
62+
Examples can be found in
63+
64+
- plugins/examples/auth-by-bridge/mosquitto_auth_by_bridge.c
65+
- test/broker/c/bridge_auth_v1.c
66+
- test/broker/c/bridge_auth_v2.c
67+
68+
# What's changed inside
69+
70+
The whole algorithm is hooked in function _bridge\_\_new_ in file _bridge.c_.
71+
To implement this new feature the following new constants and a new structure was introduced.
72+
73+
**MOSQ_ERR_AUTH_DENIED** in _enum mosq\_err\_t_.
74+
75+
**MOSQ_EVT_LOAD_BRIDGE_CRED** in _mosquitto\_plugin\_event_ for registering on this new event.
76+
77+
**load_bridge_cred** in structure _plugin\_\_callbacks_ to store auth bridge plugin callbacks.

include/mosquitto/broker.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ enum mosquitto_plugin_event {
168168
MOSQ_EVT_CLIENT_OFFLINE = 28,
169169
MOSQ_EVT_PERSIST_WILL_ADD = 29,
170170
MOSQ_EVT_PERSIST_WILL_DELETE = 30,
171+
MOSQ_EVT_LOAD_BRIDGE_CRED = 31,
171172
};
172173

173174
/* Data for the MOSQ_EVT_RELOAD event */
@@ -375,6 +376,16 @@ struct mosquitto_evt_persist_will_msg {
375376
void *future2[8];
376377
};
377378

379+
/* Data for the MOSQ_EVT_LOAD_BRIDGE_CRED event */
380+
/* NOTE: This interface is currently marked as unstable, which means
381+
* it may change in a future minor release. */
382+
struct mosquitto_evt_load_bridge_cred {
383+
char* username;
384+
char* password;
385+
const char* bridge_name;
386+
const struct bridge_address* bridge_address;
387+
};
388+
378389

379390
/* Callback definition */
380391
typedef int (*MOSQ_FUNC_generic_callback)(int, void *, void *);
@@ -489,6 +500,9 @@ mosq_EXPORT int mosquitto_plugin_set_info(
489500
* * MOSQ_EVT_PERSIST_CLIENT_MSG_UPDATE
490501
* Called when a persistence plugin must update a client message
491502
* in its store.
503+
* * MOSQ_EVT_LOAD_BRIDGE_CRED
504+
* Called when a bridge is about to created to get
505+
* username/password from a loaded plugin
492506
*
493507
* cb_func - the callback function
494508
* event_data - event specific data
@@ -542,6 +556,9 @@ mosq_EXPORT int mosquitto_callback_register(
542556
* * MOSQ_EVT_PERSIST_CLIENT_MSG_ADD
543557
* * MOSQ_EVT_PERSIST_CLIENT_MSG_DELETE
544558
* * MOSQ_EVT_PERSIST_CLIENT_MSG_UPDATE
559+
* * MOSQ_EVT_PERSIST_WILL_ADD
560+
* * MOSQ_EVT_PERSIST_WILL_DELETE
561+
* * MOSQ_EVT_LOAD_BRIDGE_CRED
545562
* cb_func - the callback function
546563
* event_data - event specific data
547564
*

include/mosquitto/defs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ enum mosq_err_t {
8585
MOSQ_ERR_ALREADY_EXISTS = 31,
8686
MOSQ_ERR_PLUGIN_IGNORE = 32,
8787
MOSQ_ERR_HTTP_BAD_ORIGIN = 33,
88+
MOSQ_ERR_AUTH_DENIED = 34,
8889

8990
/* MQTT v5 direct equivalents 128-255 */
9091
MOSQ_ERR_UNSPECIFIED = 128,

plugins/examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ if(NOT WIN32)
33
add_subdirectory(client-lifetime-stats)
44
add_subdirectory(message-timestamp)
55
endif()
6+
add_subdirectory(auth-by-bridge)
67
add_subdirectory(auth-by-env)
78
add_subdirectory(auth-by-ip)
89
add_subdirectory(client-properties)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
set (PLUGIN_NAME mosquitto_auth_by_bridge)
2+
3+
add_mosquitto_plugin_no_install("${PLUGIN_NAME}" "${PLUGIN_NAME}.c" "" "libmosquitto_common")
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
R=../../..
2+
include ${R}/config.mk
3+
4+
PLUGIN_NAME=mosquitto_auth_by_bridge
5+
LOCAL_CFLAGS+=
6+
LOCAL_CPPFLAGS+=
7+
LOCAL_LDFLAGS+=
8+
LOCAL_LIBADD+=
9+
10+
all : binary
11+
12+
OBJS:=${PLUGIN_NAME}.o
13+
14+
PLUGIN_NOINST:=1
15+
include ${R}/plugins/plugin.mk
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
Copyright (C) by ebblo Switzerland GmbH, CH-8212 Neuhausen, Switzerland
3+
4+
This project and the accompanying materials are made available under the
5+
terms of the Eclipse Public License 2.0 and 3-Clause BSD License which
6+
accompany this distribution.
7+
8+
The Eclipse Public License is available at
9+
https://www.eclipse.org/legal/epl-2.0
10+
and the 3-Clause BSD License is available at
11+
https://opensource.org/license/BSD-3-Clause
12+
13+
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
14+
15+
Contributors:
16+
- Thorsten Wendt <thorsten.wendt@ebblo.com> - Adding means for brigde authentication.
17+
*/
18+
19+
/*
20+
* This is an example plugin showing how to use the authentication
21+
* callback to provide username and password for connecting a bridge to a broker.
22+
*
23+
* This callback is called when a new bridge is going to be established.
24+
*
25+
* The callback have to provide username/passwort. The broker (in bridge mode)
26+
* is going to use these credentials for authentication against the broker.
27+
*
28+
* The following return values are supported:
29+
*
30+
* MOSQ_ERR_SUCCESS - This plugin provided username and password
31+
* MOSQ_ERR_PLUGIN_DEFER - This plugin is not able to provide username/password
32+
* MOSQ_ERR_AUTH_DENIED - Authentication is denied by the plugin and
33+
* MOSQ_ERR_NOMEM - Out of memory
34+
*
35+
*
36+
* Compile with:
37+
* gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_auth_by_bridge.c -o mosquitto_auth_by_bridge.so
38+
*
39+
* Use in config with:
40+
*
41+
* plugin /path/to/mosquitto_auth_by_bridge.so
42+
*
43+
* Note that this only works on Mosquitto 2.1 or later.
44+
*/
45+
#include <stdio.h>
46+
#include <stdlib.h>
47+
#include <string.h>
48+
49+
#include "mosquitto.h"
50+
51+
#include <config.h>
52+
53+
#define PLUGIN_NAME "auth-by-bridge"
54+
#define PLUGIN_VERSION "0.0.1"
55+
56+
#define ENV_AUTH_PLUGIN_USER "AUTH_PLUGIN_USER"
57+
#define ENV_AUTH_PLUGIN_PASS "AUTH_PLUGIN_PASS"
58+
59+
MOSQUITTO_PLUGIN_DECLARE_VERSION( 5 );
60+
61+
static mosquitto_plugin_id_t *mosq_pid = NULL;
62+
63+
static int basic_auth_callback(int event, void *event_data, void *userdata)
64+
{
65+
struct mosquitto_evt_load_bridge_cred* ed = event_data;
66+
67+
UNUSED( event );
68+
UNUSED( userdata );
69+
70+
char* env_auth_plugin_user = getenv( ENV_AUTH_PLUGIN_USER );
71+
char* env_auth_plugin_pass = getenv( ENV_AUTH_PLUGIN_PASS );
72+
73+
if ( ( env_auth_plugin_user && strlen( env_auth_plugin_user ) > 0 ) &&
74+
( env_auth_plugin_pass && strlen( env_auth_plugin_pass ) > 0 ) )
75+
{
76+
char* auth_plugin_username = mosquitto_strdup( env_auth_plugin_user );
77+
if ( !auth_plugin_username )
78+
{
79+
mosquitto_log_printf( MOSQ_LOG_ERR, "Out of memory." );
80+
return MOSQ_ERR_NOMEM;
81+
}
82+
83+
char* auth_plugin_password = mosquitto_strdup( env_auth_plugin_pass );
84+
if ( !auth_plugin_password )
85+
{
86+
mosquitto_free( auth_plugin_username );
87+
mosquitto_log_printf( MOSQ_LOG_ERR, "Out of memory." );
88+
return MOSQ_ERR_NOMEM;
89+
}
90+
91+
ed->username = auth_plugin_username;
92+
ed->password = auth_plugin_password;
93+
return MOSQ_ERR_SUCCESS;
94+
}
95+
96+
return MOSQ_ERR_NOT_FOUND;
97+
}
98+
99+
100+
int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)
101+
{
102+
UNUSED( user_data );
103+
UNUSED( opts );
104+
UNUSED( opt_count );
105+
106+
mosq_pid = identifier;
107+
mosquitto_plugin_set_info( identifier, PLUGIN_NAME, PLUGIN_VERSION );
108+
return mosquitto_callback_register( mosq_pid, MOSQ_EVT_LOAD_BRIDGE_CRED, basic_auth_callback, NULL, NULL );
109+
}
110+
111+
112+
/* mosquitto_plugin_cleanup() is optional in 2.1 and later. Use it only if you have your own cleanup to do */
113+
int mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)
114+
{
115+
UNUSED( user_data );
116+
UNUSED( opts );
117+
UNUSED( opt_count );
118+
119+
return MOSQ_ERR_SUCCESS;
120+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
plugin ./mosquitto_auth_by_bridge.so
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh
2+
3+
../../../src/mosquitto -c test.conf -v

plugins/examples/plugin-event-stats/mosquitto_plugin_event_stats.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ const char evt_topics[][60] = {
8080
TOPIC_BASE "persist/message/client/delete", /* MOSQ_EVT_PERSIST_CLIENT_MSG_DELETE */
8181
TOPIC_BASE "persist/message/client/update", /* MOSQ_EVT_PERSIST_CLIENT_MSG_UPDATE */
8282
TOPIC_BASE "message/out", /* MOSQ_EVT_MESSAGE_OUT */
83+
TOPIC_BASE "init/cred", /* MOSQ_EVT_LOAD_BRIDGE_CRED */
8384
};
8485

8586

0 commit comments

Comments
 (0)