-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
Observed behavior
(I started to write this as a comment in nats-io/nats.go#1089 but ended up tracing it into nats-server, so putting it here instead)
It is not possible to grant only permissions for JetStream with the domain included, as JS.API.>
is the real path and something like JS.cloud.API.>
is the mapped path, but ACLs seem to only be validated against the real path. When trying to have very tightly controlled ACLs on both the hub side and for a leafnode, this leads to some really weird behavior (that I still haven't figured out how to work around).
When the messages come in to nats-server, this code maps the subject back:
Lines 497 to 506 in 24924da
if (c.kind == CLIENT || c.kind == LEAF) && c.in.flags.isSet(hasMappings) { | |
changed := c.selectMappedSubject() | |
if changed { | |
if trace { | |
c.traceInOp("MAPPING", []byte(fmt.Sprintf("%s -> %s", c.pa.mapped, c.pa.subject))) | |
} | |
// c.pa.subject is the subject the original is now mapped to. | |
mt.addSubjectMappingEvent(c.pa.subject) | |
} | |
} |
and then
Line 4018 in 24924da
if c.perms != nil && (c.perms.pub.allow != nil || c.perms.pub.deny != nil) && !c.pubAllowedFullCheck(string(c.pa.subject), true, true) { |
c.pa.subject
rather than c.pa.mapped
.
If you change the ACLs in the conf file from the example below to remove the .cloud
, the stream creation works ... but then if you create a mirror stream in a leafnode (domain=leaf, Mirror.Domain=cloud), the leafnode logs will keep getting debug logs like this during sync:
Not permitted to deliver to "$JS.API.CONSUMER.MSG.NEXT....
Not permitted to deliver to "$JS.API.CONSUMER.MSG.NEXT....
Not permitted to deliver to "$JS.API.DIRECT.GET....
Not permitted to deliver to "$JS.API.STREAM.PURGE....
Which is also strange since both the hub client code and the leafnode code were explicit about domains, and neither ever tried to use JS.API.>
, but both are now running into the default leafnode deny of JS.API.>
, despite the attempts to properly use fully qualified JS subjects on both sides of the mirror sync.
So, while I understand that ACLs really should apply to the actual subject rather than a mapped subject, it seems like there should be some special case here for JetStream domains since all the docs encourage using the JS.cloud.API.>
style (WithDomain
) when a leafnode is involved.
... probably also the root of #6901
Expected behavior
I would actually expect the fully qualified JS.cloud.API
to be the real subject and JS.API
to be the mapping, since the fully qualified is used for imports/exports, when a leafnode is in play, and seems to be more specific ... but, even if the mapping direction stays as is, I would expect ACLs to work on JetStream subjects that include the domain.
Server and client version
- nats.go v1.42.0
- server v2.11.3
Host environment
No response
Steps to reproduce
- Give a client connected to the hub (with domain=cloud) only access to
JS.cloud.API
(specifically no access to the unnamedJS.API.>
):# cloud.conf server_name: "cloud" listen: "0.0.0.0:4222" http: 8222 max_payload: 8388608 leafnodes { no_advertise: true port: 7422 } websocket { port: 7443 no_tls: true } jetstream { store_dir: "./cloud_data" domain: "cloud" max_memory_store: 512MB max_file_store: 2GB } accounts { SYS: { users: [ { user: "admin" password: "admin" permissions { publish: [">"] subscribe: [">"] } } ] } TEST: { jetstream: enabled users: [ { user: "test" password: "test" permissions { publish: [ "$JS.cloud.API.STREAM.CREATE.>", "$JS.cloud.API.STREAM.INFO", "$JS.cloud.API.STREAM.INFO.>", ] subscribe: [ "_INBOX.>", ] allow_responses: true } }, ] } } system_account: "SYS"
- Try to create a stream with the domain set:
The nats.go debug console will show:
import ( "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/jetstream" ) func main() { nc, _ := nats.Connect("nats://test:test@localhost:4222", nats.Name("test")) defer nc.Drain() js, _ := jetstream.NewWithDomain(nc, "cloud") if _, err := js.Stream(ctx, "TEST_STREAM"); err != nil { if _, err := js.CreateStream(ctx, jetstream.StreamConfig{ Name: "TEST_STREAM", Subjects: []string{"my.test.subject"}, Retention: jetstream.WorkQueuePolicy, Replicas: 1, Storage: jetstream.FileStorage, Compression: jetstream.S2Compression, }); err != nil { panic(err) } } }
The nats-server debug console will show:nats: permissions violation: Permissions Violation for Publish to "$JS.API.STREAM.INFO.TEST_STREAM" on connection [13] nats: permissions violation: Permissions Violation for Publish to "$JS.API.STREAM.CREATE.TEST_STREAM" on connection [13]
Publish Violation - User "test", Subject "$JS.API.STREAM.INFO.TEST_STREAM" Publish Violation - User "test", Subject "$JS.API.STREAM.INFO.TEST_STREAM"
- Change
jetstream.NewWithDomain(nc, "cloud")
tojetstream.NewWithAPIPrefix(nc, "$JS.cloud.API")
... note the same errors.