Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue with ACL on MySQL #18

Closed
NoelzeN opened this issue May 6, 2019 · 19 comments
Closed

Issue with ACL on MySQL #18

NoelzeN opened this issue May 6, 2019 · 19 comments

Comments

@NoelzeN
Copy link

NoelzeN commented May 6, 2019

I have set up mosquitto-go-auth with mosquitto 1.6.0 and user checks are successful, however I have issues with the ACL checks. I took my knowledge from the Postgres part of the documentation and just transfered it to MySQL Queries:

and a single '$2' is
replaced with the integer value 1 signifying a read-only access attempt
(SUB) or 2 signifying a read-write access attempt (PUB).

(See https://github.com/iegomez/mosquitto-go-auth#postgresql )

Now I have a Table with 3 different Topics:

`MariaDB [mosquitto]> select * from acl;

+----+------+-------------+----+

| id | user | topic | rw |

+----+------+-------------+----+

| 1 | 2 | test/1/test | 2 |

| 2 | 2 | test/2/test | 1 |

| 3 | 2 | test/3/test | 3 |

+----+------+-------------+----+

3 rows in set (0.00 sec)
`

Now if I want to publish to topic "test/2/test", I get ACL deny which is correct.
If I want to publish to the other 2 topics, I get ACL allow which is also correct.
If I want to publish to any topic that is not existent, I get ACL deny which is what I expected.

Now my Issue is with subscribing.
If I want to subscribe to "test/3/test" I get ACL allow which is fine.
If I want to subscribe to the other 2 Topics I get ACL deny which is not what I expected.

My Query looks like this:
SELECT topic FROM acl INNER JOIN account ON account.id=acl.user WHERE (username = ?) AND (rw = ? OR rw=3)

Usually this should allow a user to subscribe to topics where the Query return 1 or 3 and publish to a topic where the Query returns 2 or 3. The second seems to work but for subscribe there seems to be some issue I don't understand.

In a different issue I read about MOSQ_ACL_SUBSCRIBE but don't see how that would be related?

Best Regards,
Nils

@iegomez
Copy link
Owner

iegomez commented May 6, 2019

Subscribing to test/1/test should work, but test/2/test not, as write privileges don't grant read.
Could you show any logs to see what's going on?

@NoelzeN
Copy link
Author

NoelzeN commented May 6, 2019

Edit: Note: I think you confused test/1/test and test/2/test, test/1/test has rw set to 2 and test/2/test set it to 1 ;)

Ok, that is good to know. Was not 100% clear to me from the Documentation but then Sub is 1, Pub is 2 and PubSub is 3, that's fine. However, I can only subscribe to the topic where PubSub (3) is set. Also for test/2/test I get a rejection. My Log looks like this (Subscribing to 1,2 and 3 after another. Only connecting to test/3/test is successful though the log doesn't show it)

1557152527: New connection from 212.101.61.123 on port 1883.
1557152528: New client connected from 212.101.61.123 as 08b0de31eaa04f949ac4f39fb576639f (p2, c1, k60, u'test').
1557152528: No will message specified.
1557152528: Sending CONNACK to 08b0de31eaa04f949ac4f39fb576639f (0, 0)
1557152534: Received SUBSCRIBE from 08b0de31eaa04f949ac4f39fb576639f
1557152534: test/1/test (QoS 0)
1557152534: Sending SUBACK to 08b0de31eaa04f949ac4f39fb576639f
1557152542: Received SUBSCRIBE from 08b0de31eaa04f949ac4f39fb576639f
1557152542: test/2/test (QoS 0)
1557152542: Sending SUBACK to 08b0de31eaa04f949ac4f39fb576639f
1557152550: Received SUBSCRIBE from 08b0de31eaa04f949ac4f39fb576639f

When I start mosquitto manually from the command line and see the stdout I can provide this log:

--- Subscribing to /test/1/test
DEBU[2019-05-06T16:24:31+02:00] checking auth cache for test
DEBU[2019-05-06T16:24:31+02:00] checking user test with backend Mysql
DEBU[2019-05-06T16:24:31+02:00] user test authenticated with backend Mysql
DEBU[2019-05-06T16:24:31+02:00] setting auth cache for test
DEBU[2019-05-06T16:24:37+02:00] checking acl cache for test
DEBU[2019-05-06T16:24:37+02:00] Superuser check with backend Mysql
DEBU[2019-05-06T16:24:37+02:00] Acl check with backend Mysql
DEBU[2019-05-06T16:24:37+02:00] setting acl cache (granted = false) for test
DEBU[2019-05-06T16:24:37+02:00] Acl is false for user test
--- Subscribing to test/2/test
DEBU[2019-05-06T16:24:43+02:00] checking acl cache for test
DEBU[2019-05-06T16:24:43+02:00] Superuser check with backend Mysql
DEBU[2019-05-06T16:24:43+02:00] Acl check with backend Mysql
DEBU[2019-05-06T16:24:43+02:00] setting acl cache (granted = false) for test
DEBU[2019-05-06T16:24:43+02:00] Acl is false for user test
--- Subscribing to test/3/test
DEBU[2019-05-06T16:24:47+02:00] checking acl cache for test
DEBU[2019-05-06T16:24:47+02:00] Superuser check with backend Mysql
DEBU[2019-05-06T16:24:47+02:00] Acl check with backend Mysql
DEBU[2019-05-06T16:24:47+02:00] user test acl authenticated with backend Mysql
DEBU[2019-05-06T16:24:47+02:00] setting acl cache (granted = true) for test
DEBU[2019-05-06T16:24:47+02:00] Acl is true for user test

Logging settings I have set in the config file:
log_dest file /var/log/mosquitto/mosquitto.log
log_type all
auth_opt_log_level debug

Do I need to set anything else to get more logging? When I run the query manually:

`MariaDB [mosquitto]> SELECT topic FROM acl INNER JOIN account ON account.id=acl.user WHERE (username = "test") AND (rw = 1 OR rw=3);

+-------------+

| topic |

+-------------+

| test/2/test |

| test/3/test |

+-------------+

2 rows in set (0.00 sec)
`

@iegomez
Copy link
Owner

iegomez commented May 6, 2019

That's odd. Could you try adding some debug printing to the CheckAcl method at backends/mysql.go? Maybe printing aclTopic and topic to what's getting compared, printing o.AclQuery and acc to check for typos or something weird, etc.

@NoelzeN
Copy link
Author

NoelzeN commented May 7, 2019

Thanks for your quick replies. I have added for debugging in backends/mysql.go function CheckAcl the following lines:

log.Debugf("ACL Query: %s\n", o.AclQuery) log.Debugf("Account int32: %d\n", acc) log.Debugf("Username: %s\n", username) log.Debugf("Topic: %s\n", topic)

Right at the start of the function and added 2 debugs for aclTopic:

log.Debugf("ACL Topic before strings.Replace: %s\n, acl) aclTopic := strings.Replace(acl, "%c", clientid, -1) aclTopic = strings.Replace(aclTopic, "%u", username, -1) log.Debugf("ACL Topic after strings.Replace: %s\n", aclTopic)

Output when subscribing to test/3/test (Permission set to 3, rw):

DEBU[2019-05-07T09:23:16+02:00] checking acl cache for test
DEBU[2019-05-07T09:23:16+02:00] Superuser check with backend Mysql
DEBU[2019-05-07T09:23:16+02:00] Acl check with backend Mysql
DEBU[2019-05-07T09:23:16+02:00] ACL Query: SELECT topic FROM acl INNER JOIN account ON account.id=acl.user WHERE (username = ?) AND (rw = ? OR rw=3)
DEBU[2019-05-07T09:23:16+02:00] Account int32: 4
DEBU[2019-05-07T09:23:16+02:00] Username: test
DEBU[2019-05-07T09:23:16+02:00] Topic: test/3/test
DEBU[2019-05-07T09:23:16+02:00] ACL Topic before strings.Replace: test/3/test
DEBU[2019-05-07T09:23:16+02:00] ACL Topic after strings.Replace: test/3/test
DEBU[2019-05-07T09:23:16+02:00] user test acl authenticated with backend Mysql
DEBU[2019-05-07T09:23:16+02:00] setting acl cache (granted = true) for test
DEBU[2019-05-07T09:23:16+02:00] Acl is true for user test

Output when subscribing to test/1/test (Permission set to 2, w):
DEBU[2019-05-07T09:23:42+02:00] checking acl cache for test
DEBU[2019-05-07T09:23:42+02:00] Superuser check with backend Mysql
DEBU[2019-05-07T09:23:42+02:00] Acl check with backend Mysql
DEBU[2019-05-07T09:23:42+02:00] ACL Query: SELECT topic FROM acl INNER JOIN account ON account.id=acl.user WHERE (username = ?) AND (rw = ? OR rw=3)
DEBU[2019-05-07T09:23:42+02:00] Account int32: 4
DEBU[2019-05-07T09:23:42+02:00] Username: test
DEBU[2019-05-07T09:23:42+02:00] Topic: test/1/test
DEBU[2019-05-07T09:23:42+02:00] ACL Topic before strings.Replace: test/3/test
DEBU[2019-05-07T09:23:42+02:00] ACL Topic after strings.Replace: test/3/test
DEBU[2019-05-07T09:23:42+02:00] setting acl cache (granted = false) for test
DEBU[2019-05-07T09:23:42+02:00] Acl is false for user test

Output when subscribing to test/2/test (Permission set to 1, r):
DEBU[2019-05-07T09:23:59+02:00] checking acl cache for test
DEBU[2019-05-07T09:23:59+02:00] Superuser check with backend Mysql
DEBU[2019-05-07T09:23:59+02:00] Acl check with backend Mysql
DEBU[2019-05-07T09:23:59+02:00] ACL Query: SELECT topic FROM acl INNER JOIN account ON account.id=acl.user WHERE (username = ?) AND (rw = ? OR rw=3)
DEBU[2019-05-07T09:23:59+02:00] Account int32: 4
DEBU[2019-05-07T09:23:59+02:00] Username: test
DEBU[2019-05-07T09:23:59+02:00] Topic: test/2/test
DEBU[2019-05-07T09:23:59+02:00] ACL Topic before strings.Replace: test/3/test
DEBU[2019-05-07T09:23:59+02:00] ACL Topic after strings.Replace: test/3/test
DEBU[2019-05-07T09:23:59+02:00] setting acl cache (granted = false) for test
DEBU[2019-05-07T09:23:59+02:00] Acl is false for user test

I didn't see any GO code before in my life but mixing the programming languages I do know I think that
o.DB.Select(&acls, o.AclQuery, username, acc)
executed the query replacing the variables with username and acc (Permission Level?) and saved the result to acls. So I added
og.Debugf("Length of ACLs returned by SQL: %d\n", len(acls)) fmt.Printf("%v\n", acls)

And for each topic it keeps telling me
DEBU[2019-05-07T09:44:01+02:00] Length of ACLs returned by SQL: 1
[test/3/test]

Unfortunately I don't know how to print the actual executed query. I'm a bit curious though why the "acc" variable is set to "4". Where does that come from? If I understand the o.DB.Select statement correctly that should be the requested access level (1 for read, 2 for write), right? How comes that is 4 in my case?
I tested with a publish command and indeed got
DEBU[2019-05-07T10:00:29+02:00] Account int32: 2
and I also get the correct number of topics that match the ACL rule so it seems the Permission request seems to be wrong (4 instead of 1). Now the question is, where does this come from? I tested with 2 clients (MQTT.fx and MQTT.js) and for both I get the DEBU[2019-05-07T10:06:37+02:00] Account int32: 4 for subscribe requests. Any idea what is going wrong here? If I can help debug further just let me know what to do as I know very little about GO :)

Best Regards,
Nils

@iegomez
Copy link
Owner

iegomez commented May 7, 2019

Ok, as I suspected, you're indeed getting subscribe requests, which appeared in Mosquitto 1.5 and are also part of 1.6 as defined here (from the latest mosquitto_plugin.h): https://github.com/eclipse/mosquitto/blob/master/src/mosquitto_plugin.h#L25

You can read how it differs from MOSQ_ACL_READ here: https://github.com/eclipse/mosquitto/blob/master/src/mosquitto_plugin.h#L204

So all you need to do is to account for subscriptions (i.e., acc -which is indeed short for "access"- 4), either in your query or at your ACLs table.

@NoelzeN
Copy link
Author

NoelzeN commented May 7, 2019

Allright, thanks a lot for your support :)

@NoelzeN NoelzeN closed this as completed May 7, 2019
@NoelzeN
Copy link
Author

NoelzeN commented May 10, 2019

Btw, during testing further with loraserver when wondering why the server didn't get any messages I found out you need to set rw=4 so it can subscribe and rw=1 so it actually receives messages, otherwise it subscribes but doesn't get any messages

@vava24680
Copy link

Sorry for re-asking the question, but I am still confused about what is difference between read permission and subscribe permission. Can you help me to understand the difference?

Thank you very much!!!

@iegomez
Copy link
Owner

iegomez commented Aug 20, 2019

From the header:

 *  MOSQ_ACL_SUBSCRIBE when a client is asking to subscribe to a topic string.
 *                     This differs from MOSQ_ACL_READ in that it allows you to
 *                     deny access to topic strings rather than by pattern. For
 *                     example, you may use MOSQ_ACL_SUBSCRIBE to deny
 *                     subscriptions to '#', but allow all topics in
 *                     MOSQ_ACL_READ. This allows clients to subscribe to any
 *                     topic they want, but not discover what topics are in use
 *                     on the server.
 *  MOSQ_ACL_READ      when a message is about to be sent to a client (i.e. whether
 *                     it can read that topic or not).

The main difference is that subscribe is checked at first, when a client connects and tells the broker it wants to subscribe to some topic, while read is checked when an actual message is being published to that topic, which makes it particular. So in practice you could deny general subscriptions such as # by returning false from the acl check when you receive MOSQ_ACL_SUBSCRIBE, but allow any particular one by returning true on MOSQ_ACL_READ.

Does that clear it?

@vava24680
Copy link

Thank you iegomez. But how to write the acl rules in the backend like mysql or postgresql if I need to do something like this

deny general subscriptions such as # by returning false from the acl check when you receive MOSQ_ACL_SUBSCRIBE, but allow any particular one by returning true on MOSQ_ACL_READ

Thank you~

@iegomez
Copy link
Owner

iegomez commented Aug 20, 2019

These are the values for the acc:

#define MOSQ_ACL_NONE 0x00
#define MOSQ_ACL_READ 0x01
#define MOSQ_ACL_WRITE 0x02
#define MOSQ_ACL_SUBSCRIBE 0x04

So if you want to treat read and subscribe differently, just set different rules for acc 1 and 4.

@vava24680
Copy link

So i have to explicitly set acl rules with each topic by assigning proper acc value and add a acl rule which disables the wildcard subscription. Is that correct?

@iegomez
Copy link
Owner

iegomez commented Aug 20, 2019

If you want to. You don't need to treat read and subscribe differently, mosquitto just allows you to do it by writing different rules for them. Also, the # wildcard is just an example of how this mechanism could work, but it's not tied to # or any other pattern in particular.
In summary, if you are fine with clients subscribing to patterns that would match the topics they can read, then just check for 1 or 4 in your ACLs; if you don't want them subscribing to patterns but only to particular topics, set different rules for 1 and 4.

@vava24680
Copy link

Oh I understand what you stated.
If a user wants to subscribe to a topic pattern and get messages from that topic pattern, there are must be acl rules stating that this user has subscribe(4) and read(1) permission. But if the user is restricted to get messages from some topics, the subscribe acl rules and read acl rules should set separately.

@IsabelManiega
Copy link

IsabelManiega commented Apr 16, 2020

Hi!
I am checking my users in mongodb backend.
I have a problem when I put superuser: true and acc: 1 in one topic, the user not only subscribe in this topic, also in all topics and publish in all of them, the problem is the superuser. It is not a correct process for the user. is there another process?

@iegomez
Copy link
Owner

iegomez commented Apr 16, 2020

Hi @IsabelManiega. Setting superuser to true effectively grants the user with permissions to publish and subcribe to any topic, so it doesn't matter how you configure topics ACLs at that point. If you want ACLs to be taken into account, set superuser to false.

Also, this issue is unrelated.

@IsabelManiega
Copy link

The problem is that:

If I select: "superuser" = False, (a normal user),
I have 3 possible permissions: (R, W, R/W):
Write: It is working, (the user is only able to write)
ReadWrite: It is working, (the user is able to both write and read)

but..
Read: It is not working. (I use MongoDB).
When I select this option, the user is NOT able to read.

AND, If I select "superuser=True", the user will be able to Read and Write
(for Read).

Therefore,
There is no way to select "READ" for a user who should ONLY read.
and not write,

How I perform this "Read" option? (for a user able to Read but never able to write)

Thank you very much for your attention ;)

@iegomez
Copy link
Owner

iegomez commented Apr 17, 2020

@IsabelManiega I believe you're missing exactly what's discussed in this issue: allowing for subscribe. If your client is sending acc 4, which stands for subscribe, before reading then it'll fail because you don't have that ACL. Try setting read and subscribe permissions to the topic, that is 2 ACLs for the same topic, one with acc 1 and the other 4, and see if that works.

@IsabelManiega
Copy link

Thanks!!!
I have just created my user:
{
"_id" : ObjectId("5e994c75fc4f7805f97e3d4a"),
"username" : "Prueba",
"password" : "PBKDF2$sha512$100000$gkFNM/6IQhxuFXm8uRd49w==$7VvfH4KkE9lYpgCBRci4xURIAFpAJYNxq3HcmBLqMlKnbPup7PTVSmCxz0pH9XCy9ff/cf6/95Mm7wALe1tysg==",
"superuser" : false,
"acls" : [ { "topic" : "test/#", "acc" : 1.0},
{ "topic" : "test/#", "acc" : 4.0}]
}

It is working, the user is able to ONLY READ.

Thank you very much !!! ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants