Closed ralight closed 3 years ago
Added group priority.
use_identity_as_username
or the user name in the CONNECT
message?JWT
as a 'header' that a client can send when publishing or sending SUBSCRIBE
for the first time? This will probably be too verbose for IoT scenarios.I assume the repsonses to the commands will follow the Request/Response scheme as described in the MQTTv5 specification. The client will specify a Response Topic and may pass some Correlation Data. Will there be a default Response Topic?
@chainhead
use_identity_as_username
is set, then any username specified in CONNECT is discarded. I would expect that behaviour to remain the same.@ckrey There are few ways I've been thinking about dealing with responses. First off, I don't really want to restrict these features to MQTT v5 only so have to consider MQTT v3 as well. That implies that a default response topic is required.
The two ways I was thinking about this are:
$CONTROL/user-management/v1/cmd
and $CONTROL/user-management/v1/response
for example, where the response
topic is a default that can be overridden. The default could also be $CONTROL/user-management/v1/response/<client-id>
. $CONTROL/user-management/v1
for both commands and responses. This would mean being a bit cleverer with topic control. By this I mean we could process the commands but not send them on to any other subscribing clients, and the response could be sent to the requesting client only, regardless of other subscriptions.In both cases I'm a little bit nervous about letting the client define their own response topic because of the possibility of sending sensitive data to public/topic
. Perhaps the response topic would have to reside in $CONTROL/user-management/v1/response/#
.
With respect to JWT, firstly, it is a solution for authorization and not authentication. Secondly, it is a tiny piece of JSON that is sent by the client as a set of 'claims'. These claims are verified by the server. The issuance of the JWT itself is typically handled by a different system e.g. Microsoft AD, Keycloak, etc.
For a MQTT broker, couple of use cases could be as follows:
One related question. Are you basing the security construct on user name and password? Why not client identifier? Perhaps, alongside username password? I mention this because, the client identifier is mandatory as per the spec (which you know already. :D )
Just thinking...
Should the configuration of security policy be via MQTT messages at all? I mean, how do we "police the police" given that, the entity publishing the configuration commands should also come under access control.
Using a configuration file with 'hot' reload is not a bad option at all. (Although, I have no idea if it is possible). Better yet, have the configuration driven off of a access control solution e.g. Microsoft AD, Keycloak, etc. (Ok, the latter option is more enterprise suited.)
That sounds great. I think its a good idea to rework mosquittos authentication. Projects such as https://github.com/iegomez/mosquitto-go-auth and https://github.com/jpmens/mosquitto-auth-plug seemingly filled some gap in the last years.
For me, authentication and authorization typically invovles integration. Typically, mqtt is used in a distributed setups, that also involve some protocols. I'd suggest:
Providing a clear separation between permissions, roles, users and groups. I think that this is vital, but I don't find it in your concept. Permissions define what somebody can do. Roles aggregate Permissions. Users (or accounts) are managed in some kind of repository, whereas Groups aggregate users:
For instance: hsimpson is user in group sector-7g-employees, whereas subscribe-radiation-alert is a permission, that could be aggregated in a role called safety-inspector.
Typically, users and groups are provided by 3rd party systems (or text files for KISS), whereas permissions and roles are defined in mosquitto. Mapping takes place.
One can think of a lot: You can map permissions to users (e.g. Homer Simpson is allowed to subscribe to radiation alerts), group to permission (e.g. all employees in section 7g get radiation alerts), user to role (Homer Simpson is a safety inspector) and group to roles (all employees in group sector 7g play the role of safety inspectors) … poor Springfield.
But… that’s quiet complex. IMHO, typically, it suffices to map groups to roles by names. E.g. that would require Homer Simpson to be member of a Group called safety-inspects as well. I guess that this kind of mapping suffices for mosquitto but some edge-case could require some explicit mappings. However, mapping permissions typically results in long and almost unreadable definitions.
When integrating with JWT or SAML (or both) you could also map roles to claims or attribute-values.
I think it's a good idea to rely on plugins for integrating 3rd party systems. Nevertheless, LDAP, SQL and JWT are really popular nowadays. Maybe, it could help to integrate some work from https://github.com/iegomez/mosquitto-go-auth. An plugin is then required to authenticate credentials of an arbitrary chosen format and to retrieve user as well as group information. This is then used by mosquitto (core) to apply permissions and roles.
Edit: I really do like to model of: https://github.com/stffn/declarative_authorization - it's solely authorization, nevertheles. But the ACL is elegant.
@chainhead Thank you for the JWT examples, those kind of capabilities are certainly appealing. Including the client id - yes absolutely if desired.
The current system uses a file with reloadable values and has been cited to me as a reason not to use Mosquitto.
@yanosz Thanks for your comments. You're right that the permissions part isn't as clear as it needs to be. With regards roles/users/groups, I would say that my "policy", which is a collection of permissions (and can include denials), is equivalent to a role. A group can have a policy attached to it, and likewise users can have policies attached to them. A user can be in multiple groups, which means multiple roles. I hadn't planned on having a user being able to have multiple policies attached to them directly, but this could be managed in the current draft by having the user in multiple groups, even if that group only has a single member.
With regards integrations, they are definitely important and must be considered in the design, but I think the first iteration will be limited to being a standalone implementation.
@ralight thanks for your feedback.
Regarding:
I would say that my "policy", which is a collection of permissions (and can include denials), is equivalent to a role.
I won't go for denials. I prefer to stick to either a 100% grant or 100% denial approach. Having both denial and grants can be a little bit confusing. I've to admit that policies looked to me like permissions in the first place. i guess, they are something in between.
Maybe, it could be better to seperate policies from grant / denial (i.e. topic stanzas) and keep references. That would allow to refer to a specific permission in different policies. When just referencing names in policies, the aggregation aspect would be clearer, IMHO. I'm also a little bit confused by "policyName":""
is the policy name empty or is it some kind of regexp-match?
With regards integrations, they are definitely important and must be considered in the design, but I think the first iteration will be limited to being a standalone implementation.
IMHO it'd be helpful to maintain API compatibility with https://github.com/iegomez/mosquitto-go-auth, as long as the 3rd party integration is not complete. It would be ok to deprecate parts on the API in the first iteration, but I guess that many peoply rely on this code.
The current system uses a file with reloadable values and has been cited to me as a reason not to use Mosquitto.
I wonder why.
Anyway, these are the options I see.
Broker relies on a simple YES
or NO
response from access control systems, e.g. Microsoft AD, Keycloak, etc., for a given combination of principal (username/password, client identifier, etc.), resource (topic strings) and action (publish, subscribe with QoS levels). Since the principal, resource and action are easily available from a client connection, existing clients will not be impacted.
Pros
mosquitto
code base (I hope!).Cons
The mechanisms to on-board or off-board a principal and also maintain its access (resource and action) is left to the enterprise systems. Whereas, only the verification is done by mosquitto
. For example, a JWT is issued by the enterprise system as part of provisioning a device. However, the verification of the token remains with the broker.
Pros
Cons
This is, of course, your proposal.
Pros
Cons
On the client side, sending a token maybe seen as an overhead by some clients.
I want to say that I love the idea of having a no-fuzz access control system available in mosquitto. Is simply a must in today's world, even for weekend projects.
Still, when I think of what other users of mosquitto might like, I am not sure that they will love it that much.
One of the things I like about mosquitto is it's simplicity. Maybe by choice, maybe by accident, but it's the only broker I know that adheres to the UNIX philosophy of:
"do one thing and do it well."
It is my humble opinion that adding the suggested changes, will make mosquitto leave a void in this category. And it will be painful for anyone keeping a "customized" version of the security implementation, as s/he will have to keep a customized version of the broker as a whole.
Again, don't take me wrong, I use mosquitto in my hobby projects and having reinvented the ACL wheel a few times already, I would definitely love to have it OOB.
I know that the original email said "without needing a separate plugin", but I would like to suggest a third option.
If you look at other software packages like NGINX, they offload client authorization to a 3rd party, by making a subrequest (using the same HTTP protocol). I imagine it would be possible to do the same, but over MQTT (maybe over a different, isolated listener using UNIX domain sockets or something?).
In case the overhead is an issue, maybe revisiting the plugin interface can be an alternative. Over the years I've used it and I think is adequate enough. It's only problem is being a second class citizen (or at least I feel it that way).
I believe the API you suggested can be implemented in isolation, and provided to the community as a "reference" that actually does work (with full official support). Everything will still adhere to the "do one thing and do it well", and advanced users will still be able to morph it to their needs.
I want to congratulate @ralight for starting this conversation, as I believe mosquitto can benefit a lot by having security available out of the box. Please count me in if you need help coding it.
I find this all a bit concerning. Using topics to configure access control for topics feels a bit circular, even though I think it could be done correctly.
The question about initial users who can set policy is excellent. Having mosquitto print a password does not seem reasonable. This all needs to work within the context of packaging systems and management schemes (e.g. ansible, but I broadly mean all such schemes including ad hoc ones). I would suggest that this is somewhat like postfresql's access model where a superuser is created from the command line and then sql is used, and it makes sense to look to that. So the user/password (or certificate) of the superuser needs to be put in a config file, or else there needs to be a program that can set that via e.g a priviliged socket.
Another question is how all of this is stored. Unlike normal topics, it seems obvious that it must be persistent. While json is easy for some, it also seems that a command-line program to send the json must be provided so that one can do normal operations like 'create this user'. I can see the allure of dynamic changing of users, but that feels like it is reinventing radius and an authorization system. I personally really like it that I have a config file with all my users and passwords in them. It's easy to understand and easy to change (emacs is my automation system!).
So I would lean to a file-based config - perhaps a separate authn/authz file - as the baseline, and allow plugins to be fancier. One would be a database of some kind, and that would then enable this publish-json mechanism. I would prefer to keep that off - if I don't have an articulated need for dynamic acls, then it's just one more thing to be sure about, and something along the lines of the Principle of Least Privilege says that such dynamic mechanisms must be off by default.
Finally, I would suggest stepping back and writing requirements first. I'm not really sure what problem this is solving for who. If I did understand that I'd probably say something different.
Thanks all for your comments, I haven't replied before because I've been on holiday and then dealing with other things.
@chainhead
Delegate all access control to enterprise solutions
Broker relies on a simple
YES
orNO
response from access control systems, e.g. Microsoft AD, Keycloak, etc., for a given combination of principal (username/password, client identifier, etc.), resource (topic strings) and action (publish, subscribe with QoS levels). Since the principal, resource and action are easily available from a client connection, existing clients will not be impacted.
This is already possible with the current system, through a plugin. As you say, this has a low impact on Mosquitto itself and just needs implementing where desired. I would view that as a parallel solution to what I'm proposing here.
Delegate only life-cycle methods to enterprise solutions
The mechanisms to on-board or off-board a principal and also maintain its access (resource and action) is left to the enterprise systems. Whereas, only the verification is done by
mosquitto
. For example, a JWT is issued by the enterprise system as part of provisioning a device. However, the verification of the token remains with the broker.
This approach has definite appeal to me. The most obvious way to add a JWT token is with a MQTT v5 user-property, which would limit where it could be used (no MQTT v3 clients), but for some situations this would be fine. There is no reason this couldn't be produced as another parallel solution, and I suspect that I will be looking at it in the future.
@abiliojr @gdt
Both of your replies highlight some aspects of where I haven't included enough of the things that are "obvious" to me in my head, and should have spelled out clearly.
This is absolutely not a proposal to remove the existing security setup and replace it with a new setup. This is a new security mechanism which would sit alongside the existing file based security, and other plugins. As such, it would be entirely optional but hopefully become a popular way of administering users.
@abiliojr - if I understand what you are saying correctly, your concerns are primarily that this would be a default replacement for what exists, and would prevent other security systems being developed. Is that correct? If so, I think I have addressed those points. When I said "without the need for a separate plugin", I meant without the need for a plugin separate to the project - a third party plugin.
@gdt - it took me a long time to come around to using topics for configuration purposes. When a requirement is that it should work remotely, I think it is the best choice however, rather than implementing something entirely new for the same purpose.
In terms of first setup, yes, having the broker print out admin details is not a good idea. Having a tool to add the first user would be a good plan, alternatively you could use mosquitto_passwd to create a user and then use an acl file to grant access, but that is not as nice.
The setup provided by this must of course be persistent, and would be a json file. That could obviously be manipulated by hand, but essentially it would be owned by the broker process and so it wouldn't be recommended. The json messages shown here to control everything are just the interface. I'm not expecting anybody to type them in manually, there definitely needs to be an easy way of producing them. I intend to provide a command line tool, but haven't started thinking about it too much yet.
Addressing requirements - a remotely configurable security mechanism that is self contained, allows on the fly updates to users, groups, and the rights that each of those have. Suitable for deployment in a similar amount of disk/memory overhead as a current Mosquitto installation
Addressing requirements - a remotely configurable security mechanism that is self contained, allows on the fly updates to users, groups, and the rights that each of those have. Suitable for deployment in a similar amount of disk/memory overhead as a current Mosquitto installation
Just to prove that this isn't an abstract idea, I am working on a commercial project which needs pretty much what is being discussed here. Without divulging anything which you can't find publicly about the LV-CAP project:
From our point of view, if we could re-configure the broker ACL by sending Mosquitto a JSON payload each time the Docker orchestrator runs up or stops a Docker container, this would be ideal. From this perspective:
For completeness, the issues using the existing file-and-reload scheme to change settings are:
In the details:
@richardash1981 Thanks for your comments, it's good to hear there are other people that would benefit from this. The systems you have sound quite capable compared to others I've been involved in :)
- we would want to use configuration files (current ones would be fine) to set up a "base" set of users and their access - this would allow the system to bootstrap itself, and the orchestrator process to connect to the broker.
In this situation you could also have a pre-created copy of the json file the dynamic security would use for storing its config, and just copy that to the appropriate place on provisioning.
- Not sure I understand the difference between "publish-read" and "subscribe-" rules - for us the ACL file syntax of just "read" and "write" is working fine.
The current ACL files have read
and write
rules, which act exclusively on PUBLISH messages. That means if you subscribe to #
, then you will only receive the messages which you are allowed. The proposed subscribe
rules (which are available to plugins now) allow you to deny SUBSCRIBE messages, so you could stop anybody subscribing to the literal #
, for example, or to deny subscribing to a pattern of topics like secret/#
. The subscribe rules aren't strictly necessary alongside the read and write rules, but if you can deny subscriptions then it means that messages that would have matched don't have to be processed for that client at all, which is an efficiency saving.
I'm sorry for the late reply, life kept me away from this topic. Hope is not too late.
@ralight, I think I have a clearer view of your idea. Am I getting it wrong, or part of this can be implemented using the plugin interface? (maybe with some extensions). Then, as a plus, this plugin could serve as a reference design for other people.
If that's the case, can we improve the API in the process? (or leave space for future improvement). Here are some ideas, mostly based on (mis)beliefs and memories:
More important, an improved documentation of the API, telling what can be done with it and it's limitations. Most of the beliefs listed here come from the lack of an official source of truth other than swimming into the code.
@ralight, how do I help?
This was released as part of 2.0.
Reading the thread here and the docs on Dynamic Security plugin, it appears that only username + password auth is possible for clients and not client certificates like is possible with an ACL? Or am I missing something in the docs?
Mosquitto Dynamic Security
This document describes a topic based mechanism for controlling security in Mosquitto. JSON commands are published to topics like
$CONTROL/<feature>/v1
Users
When a client connects to Mosquitto, it can optionally provide a username. The username maps the client to a user on the broker, if it exists. Multiple clients can make use of the same username, and hence the same broker user.
Groups
Broker users can be defined as belonging to zero or more broker groups.
Security Policies
Security policies can be applied to a user or a group, and define what that user/group is allowed to do, for example what topics it may or may not publish or subscribe to, or whether it is allowed to access different administrative features.
If a user is in a group, and both the user and the group have separate policies applied, then the user policy will override the group policy.
Users can have their groups assigned a priority. This means that if they are in multiple groups, the order in which policies are applied can be defined. Policy will be applied starting with the group with the lowest priority, moving to groups with higher priorities, and finally a user policy if defined.
If a user is not a member of any groups, and does not have a user policy, then all access will be denied.
Policy Features
Security policy management
Actions:
Topic:
$CONTROL/security-policy/v1
User/group management
Actions:
Topic:
$CONTROL/user-management/v1
Bridge control
Future
Listener control
Future
General preferences
Future, e.g.
max_keepalive
.Security policy management
Topic:
$CONTROL/security-policy/v1
Commands are JSON payloads as defined below. There can be any number of commands in a single message. The below schema defines all of the available commands.
User/group management
Topic:
$CONTROL/user-management/v1
Policy storage
How policies are stored on disk.
User and group storage