nats-server
nats-server copied to clipboard
Deny wildcard subscriptions but allow literals
Feature Request
There should be a feature to deny wildcard subscriptions, but allow literal-variable subscriptions. This would be huge in preventing wiretap situations.
For example, I should be able to subscribe to: "client.69f3-432f-s234", but not to "client.*", without having to of had entered "69f3-432f-s234" already in the NATS configuration file for specifically "client.69f3-432f-s234".
Use Case:
This is useful for those of us who incorporate application-level GUIDs into topic names and do not want others to be able to easily listen to all communications to all GUIDs.
Proposed Change:
A change in the configuration file to allow for the denial of wildcard subscriptions, but acceptance of wildcard subscriptions that are entered literally.
Who Benefits From The Change(s)?
Everyone who wants to prevent wiretapping.
Alternative Approaches
I've seen other mention the JWT approach, but that is a lot more complicated and cumbersome to implement. A simple option to exclude wildcard subscriptions while allowing literal variable subscriptions
Allow by default is open, but deny can limit scope. So would not deny <client.*> work in your case for sub perms?
This is what happens with a simple deny of <client.*>, even if you specify <client.asd>, you are still disallowed from subscribing. And the key part is, the "asd" in <client.asd> is unknown, considering it is a dynamically generated GUID only good for the life of the client session.
joshc@DESKTOP-T5GGTU1:/mnt/c/Users/ec$ nats sub "client.*"
14:16:11 Subscribing on client.*
14:16:11 Unexpected NATS error: nats: Permissions Violation for Subscription to "client.*"
nats: error: nats: Permissions Violation for Subscription to "client.*", try --help
joshc@DESKTOP-T5GGTU1:/mnt/c/Users/ec$ nats sub "client.asd"
14:16:14 Subscribing on client.asd
14:16:14 Unexpected NATS error: nats: Permissions Violation for Subscription to "client.asd"
nats: error: nats: Permissions Violation for Subscription to "client.asd", try --help
This is with the following permissions:
authorization: {
users = [
{
user: test
password: test
permissions: {
publish: {
deny: ">"
},
subscribe: {
deny: "client.*"
}
}
}
]
}
Ah you are correct.. We will consider, thanks.
+1
I remember there is similar discussion on slack but so long ago that it gone. also https://github.com/nats-io/nats-server/issues/664 is similar in nature I guess, though this one raised before the deny feature
We ran into this recently. We have tens of thousands of clients, and would like them to be able to subscribe to their specific client ID clients.<id>
, but would like to disallow the snooping of clients.*
by any specific client.
+1
We are evaluating replacing mosquitto with nats and have quickly run into this issue. In mosquitto, we have read ACLs set up using client/%c/#, allowing clients by default to sub to their specific topic(s) but not others. Would be nice to enable this type of feature in nats.
You can do what you need today. I think this is specifically asking about disallowing wildcard subscriptions. For what you are trying to do @kumpfdp its not needed AFAIK.
Interesting @derekcollison, I may have misunderstood. I was thinking this is about avoiding wiretapping, or as i understand it, restricting clients from reading from other clients' topics but still allowing them access to their own.
If I currently have an ACL set up as read client/%c/# in mosquitto, this allows clients to read from their specific client id topics under client/. %c is a special character that allows this to work. Is there something similar in nats that I've overlooked?
In NATS you would need to be more explicit, but can achieve the same thing. Say you want all communications for a client to have a prefix of CLIENT.<ID>, where ID uniquely identifies the client.
It would follow that you could place allow perms for both pub and sub to CLIENT.<ID>.>
.
You would use the inbox prefix to make sure request / response followed same rules.
Lastly you could use account scoped subject mappings to make this a bit more transparent to the end applications if desired.
Is <ID> dynamic meaning it applies to any client’s id? Or do you have to know the client id before configuring the authorization within nats? If it’s the latter, that’s a deal breaker for our use case.
Yes you would need to know. NATS is not a single server system, you can arrange NATS servers into any topology you want. Meaning that the permissions would be beyond a single server.
Most of our users/customers who care deeply about this have an external identity of some sort, whether it be a client certificate or the credentials themselves used to connect to a NATS system, which also gives you a unique identity within our system.
Yes, we care very much about the identity of the connections, as well. However, these identities aren’t known upfront in my scenario. This is where having the system be capable of dynamically injecting a client id into an authorization subject definition would be beneficial.
Example would be client.%cid.> where %cid is replaced with the connected client’s id by the system automatically for the connecting client. This would allow for a single authorization definition that could apply without having to know the exact client id of the connecting client.
Appreciate the continued discussion @derekcollison.
How do you trust the connection? The CID in NATS world is an ephemeral identity that is scoped to the server. It's not used for AuthN nor AuthZ, in a NATS system.
Most folks use either NATS JWTs or client certificates. These for the AuthN and bind to the connection. For the NATS JWT, it has all the information included for AuthZ.
We do enforce trust of our connections and it is handled externally from our broker. This external component assigns the client id which is unique, per client connection. What I'm looking to replicate from mosquitto into nats is the pattern ACLs (https://mosquitto.org/man/mosquitto-conf-5.html). Specifically, %c and %u topic patterns.
I think your use case maybe could be implemented using the new Auth callout feature. That unique ID that is assigned by another system could be simply part of what the client application passes as an authentication token, which is then passed to your implementation of the authentication callout service that can generate the JWT on the fly allowing the subscription to "foo.<id>"
but disallowing subscriptions to "foo.>"
.