Go mosquitto auth plugin


#1

Hi, guys.

Following another post, I finally developed a new auth plugin for mosquitto entirely written in Go, it just uses cgo to expose the C functions that mosquitto expects in the plugin.so. You can check it out here:

The obvious question is: why? Well, I like Go a lot, and I’m quite faster reading and writing Go code than C code. Also, I needed an extension for the mosquitto-auth-plug described in the docs, and had some troubles with json parsers in C, so finally decided to write a new plugin. The great thing about it being in Go is that it’s really easy to add a new plugin and get it working in a couple of hours. Just check the code (consider it as a first attempt, some refactoring is needed) and compare with the C backends.

I wrote it specifically to handle mqtt auth needs presented by this project, loraserver, so the existing backends aim to deal with its use cases. Of course, it may and will be further developed, but for now it allows Postgres, Files and JWT backends. The latter allows two modes: the remote mode calls an external API (for example, included in lora-app-server) to check auth and acls; the local mode makes use of a jwt secret to decode the token and check directly against a local DB (for example, the same used in lora-app-server).

Anyway, just wanted to let it out there. It’s definitely not production ready, but I’ve tested the 3 backends (both modes for jwt) with our loraserver infrastructure and they seem to be working fine. It’d be great if anyone wants to use it and test it a little to let me know about any bugs (right now it is pretty verbose, so it shouldn’t be such a pain). I’d also appreciate any feedback on the plugin and what backends you would need or want to see implemented in order to add them as soon as possible, and will gladly help anyone trying to use it.

Cheers! :slightly_smiling_face:


Mosquitto Auth Plugin
Filter messages on the network server, or before?
TLS certificates for LoRa Server components
TLS certificates for LoRa Server components
TLS certificates for LoRa Server components
#2

I haven’t tested this, but I think this is a great idea :slight_smile: Would you be able or are you planning to add some tests? I think for such a library it would be good to add some tests to avoid regressions.

Btw, you might want to remove and exclude the binary (.o and pw) files from your repo.


#3

Hi, @brocaar, thanks for the reply and suggestion.

I’ve just removed the binaries that I’d forgotten, cleaned up a bit the Readme and added a make option for getting dependencies as well. Just pushed it to the repo.

There’ll definitely gonna be some tests, I just finished getting it to work today and wanted to give it a try first, but I’ll get to that right now.

I really hope this helps and facilitates the process of securing mosquitto, specially for use in conjunction with loraserver. If you’d like to suggest any more ideas or contribute with some features, I’ll be glad to hear about them! They’ll sure be of great help. :wink:


#4

Hi, @brocaar (and anyone else who is interested), I’ve updated the plugin with several changes.

Some bits were cleaned up and refactored, the plugin name was shortened to mosquitto-go-auth and I included tests for every backend.

Also, I wrote a new Redis backend and added an optional “prefixes” option in order to define (when present) which backend should be used for a username that has the given prefix. More details about this option are included in the Readme file.

Finally, I’m working on a Mysql backend and expect to push it next week, but again, if anyone is interested in any backend additions, just let me know here or on the github issues. It’d also be great if you guys can report any bugs in the issues as well.

Here’s the url for the renamed repo:

Cheers! :slightly_smiling_face:


#5

Just added Mysql support.


#6

Hi, guys. I’ve made some improvements in the last couple of days, which include:

-HTTP backend added: now the plugin allows to check user/superuser and acls against an HTTP api with a bunch of different configurations (tls enabled/disabled, response type, data format, etc.). Details about these may be checked at the readme.

-JWT enhanced: the JWT backend has been improved with two new capabilities. First, instead of restricting the remote API to receive and respond with json, now it offers sent data format options (json encoded or form values), and response options (json, plain text or simple status code), just as the HTTP backend does. The other improvement is that JWT backend now allows to set either Mysql or Postgres (this is default) as the local DB for local mode. Also, JWT tests have been extended to cover all relevant functionality.


#7

Just added one more backend: SQLite3. As every other backend, it offers full support: user, superuser and acl checks.

:grin:


#8

Awesome! I really need to start testing it. I’ve had so many troubles with the other “mosquitto-auth-plugin”. E.g. readwrite in file ACLs simply did not work (in some cases), where one read and an other write rule would work. When Mosquitto starts before PostgreSQL, the other plugin simply causes Mosquitto to terminate (instead of retrying until PostgreSQL is up again). See also: https://github.com/jpmens/mosquitto-auth-plug/issues/269.

Will do some testing in the new year! Keep up the good work :+1:


#9

Thanks! I’m really glad you find it useful.

As for your issues with the other plugin, this one indeed takes care of the Mosquitto before Postgres problem by looping on initialization just as the loraserver does (actually, apart from the topics matching logic, db connection and hashing algorithms in utils.go are taken from your project). Also, I tried stopping postgres, subscribing with a postgres user, failed to subscribe, then restarted postgres (after the cache expired the reject status) and the user was able to subscribe, so it seems to respond to temporal failures.

Topics also appear to work well, at least for the automated tests and daily use, so the readwrite pattern shouldn’t be a problem either.

What I haven’t done yet is benchmarking the plugin. There’s no speed problem for my daily use, but who knows, thousands of users could be an issue. I’ll check the benchmarking tools to write up some tests and see how well it copes.

I’ll be waiting for your feedback and any bug you find to get it fixed as soon as possible.

Thanks again!


#10

Hi, today I added a couple of more things and fixed a tiny bug:

  1. Redis acls now allow to specify read, write and readwrite topics, either for specific users, as common rules or a mix of both. Before, it only allowed user acls and treated everything as readwrite.

  2. The readme has been cleaned up once again, and has now a table of contents for ease of reading.

  3. I fixed a bug for the files backend, where a “topic write test/topic” would allow read privileges too. Now anything marked read or write is strict, and readwrite or missing option give both privileges.

  4. I added some benchmarks for files and redis operations with the regular go tools, though I don’ think they are very interesting. Check the readme for those benchmarks and a little discussion.

  5. Log level support is now available (uses github.com/sirupsen/logrus).


#11

And just added MongoDB support, so now the plugin allows all these backends:

  1. Files
  2. PostgreSQL
  3. JWT (with local DB or remote API)
  4. HTTP
  5. Redis
  6. Mysql
  7. SQLite3
  8. MongoDB

Cheers!


#12

There’s one new addition: a custom backend.
This option allows to write your own custom backend/plugin that will be linked and used by mosquitto-go-auth. How to implement it is described in the readme.


#13

Hello @iegomez ,

here my first question plz.

Following Configuration step of your project I did…

For General option I modified mosquitto.conf like this:

auth_plugin $GOPATH/src/github.com/iegomez/mosquitto-go-auth/auth-plug.so

include_dir /etc/mosquitto/conf.d

Then I created mosquitto-go-auth.conf file under /etc/mosquitto/conf.d/ and I added:

auth_opt_backends files, postgres, jwt

and there I’m not sure, could you confirm me that all auth_opt_ option from Cache, Log Level, Prefixes and Files must be completed in mosquitto-go-auth.conf ?

and concerning the desired configuration on POSTGRE I should add all the options auth_opt available in the paragraph PostgreSQL?

Here my mosquitto-go-auth.conf:

#Backends
auth_opt_backends files, postgres, jwt

#Cache
auth_opt_cache true
auth_opt_cache_reset true

auth_opt_cache_host localhost
auth_opt_cache_port 6379
auth_opt_cache_password pwd
auth_opt_cache_db 3
auth_opt_auth_cache_seconds 30
auth_opt_acl_cache_seconds 30

#Log Level
auth_opt_log_level debug

#Prefixes
auth_opt_check_prefix true
auth_opt_prefixes filesprefix, pgprefix, jwtprefix

#Files
auth_opt_password_path $GOPATH/src/github.com/iegomez/mosquitto-go-auth/test-files/password_file
auth_opt_acl_path $GOPATH/src/github.com/iegomez/mosquitto-go-auth/test-files/acl_file

#PostgreSQL
auth_opt_pg_host localhost
auth_opt_pg_port 5432
auth_opt_pg_user go_auth_test
auth_opt_pg_password go_auth_test
auth_opt_pg_dbname go_auth_test
auth_opt_pg_userquery select password_hash from "user" where username = $1 and is_active = true limit 1
auth_opt_pg_superquery select count(*) from "user" where username = $1 and is_admin = true
auth_opt_pg_aclquery select distinct 'application/' || a.id || '/#' from "user" u inner join organization_user ou on ou.user_id = u.id inner join organization o on o.i$
auth_opt_pg_sslmode disable
#auth_opt_pg_sslcert
#auth_opt_pg_sslkey
#auth_opt_pg_sslrootcert

thank you for your help.


#14

Hi, @julien.

For cache, you may choose or not to use Redis as a cache for checks, wich make them a lot faster. If you don’t want to use a cache, just set:
auth_opt_cache false

Log’s default level is info, so that’ll be the log level when the option is not present, but you can modify it with whatever level (e.g., warning) you want using:
auth_opt_log_level warning

Prefixes allow to set which backend should authenticate and authorize a user. For example, these settings would make a user with username myprefix_someuser be authenticated/authorized by the postgres backend:

auth_opt_backends files, postgres, jwt
auth_opt_check_prefix true
auth_opt_prefixes someprefix, myprefix, otherprefix

That said, you don’t need to use them (I rarely do), and you may disable them with auth_opt_check_prefix true.

If you included the files backend, then these are mandatory:

auth_opt_password_path /path/to/password_file
auth_opt_acl_path /path/to/acl_file

You may generate passwords for your users with the pw utility included in the plugin, which gets compiled when running make and made available at the plugin’s directory. So, if you are at $GOPATH/src/github.com/iegomez/mosquitto-go-auth/, you may generate a password with:

./pw -p plain_text_password

Then you need to write down users and their passwords at the passwords files mentioned earlier, e.g.:

test1:PBKDF2$sha512$100000$2WQHK5rjNN+oOT+TZAsWAw==$TDf4Y6J+9BdnjucFQ0ZUWlTwzncTjOOeE00W4Qm8lfPQyPCZACCjgfdK353jdGFwJjAf6vPAYaba9+z4GWK7Gg==
test2:PBKDF2$sha512$100000$o513B9FfaKTL6xalU+UUwA==$mAUtjVg1aHkDpudOnLKUQs8ddGtKKyu+xi07tftd5umPKQKnJeXf1X7RpoL/Gj/ZRdpuBu5GWZ+NZ2rYyAsi1g==

Finally, necessary options for Postgres depend on your requirements. You may check which are mandatory at the README, but here’s an example configuration from one of my deployments:

auth_opt_pg_host localhost
auth_opt_pg_port 5432
auth_opt_pg_dbname my-db-name
auth_opt_pg_user my-db-user
auth_opt_pg_password my-password
auth_opt_pg_userquery select password_hash from "user" where username = $1 and is_active = true limit 1
auth_opt_pg_superquery select count(*) from "user" where username = $1 and is_admin = true
auth_opt_pg_aclquery select distinct 'application/' || a.id || '/#' from "user" u inner join organization_user ou on ou.user_id = u.id inner join organization o on o.id = ou.organization_id inner join application a on a.organization_id = o.id where u.username = $1 and $2 = $2

As a note, ssl_mode is disable by default.

Important: That said, your conf file looks fine except for the files paths, which need to be absolute (mosquitto reads $GOPATH as a string, not as an env var, and it’ll fail).


#15

thank you for the reply,
I changed $GOPATH env var by absolute path in mosquitto-go-auth.conf.

What is your advise plz first I have to check the plugin with your example “Testing postgre” or can I test it directly with my lora-app-server’s postgre database and therefore change the conf of mosquitto-go-auth.conf?

For the second option I must create user tables in lora-app-server’s postgre db?
Then and If I understood correctly, I must use your pw utility and fill the password file with the pw utility output so that create password for lora-gateway-bridge, loraserver and lora-app-server
And to finish I must modified acls file in order to limit some login to such or such topic

thank you


#16

Unless you made some modifications to the plugin, there´s no need to test it, though it doesn’t hurt and it should show any prior issues (wrong Postgres DSN, faulty mosquitto installation, etc.) if the tests fail. But it’s ready to use, so again, it’s not mandatory to run the tests.

The lora-app-server user´s table is enough for authentication and authorization, you don’t need to create your own. You only need to provide the relevant queries, which are the same as mentioned in loraserver’s doc and the example conf I posted in my earlier reply. Basically, userquery checks that the user exists in your lora-app-server, superquery checks if it’s a general admin and therefore has full permissions on any topic, and aclquery checks that the user is member of the organization and application of the according device.

That’s correct, the pw binary will generate the PBKDF2 passwords for static auth which are needed for loraserver, lora-app-server and lora-gateway-bridge users (the ones you fill out in the .toml config files of each). And ACLs should follow the official docs, so they should look like this:

user loraserver_gw
topic write gateway/+/stats
topic write gateway/+/rx
topic read gateway/+/tx

user loraserver_ns
topic read gateway/+/stats
topic write gateway/+/tx
topic read gateway/+/rx

user loraserver_as
topic write application/+/device/+/rx
topic write application/+/device/+/join
topic write application/+/device/+/ack
topic write application/+/device/+/error
topic read application/+/device/+/tx

#17

Thank you for the reply,

Again some questions please.

Regarding three fields present in mosquitto-go-auth.conf:

auth_opt_pg_dbname appserver
auth_opt_pg_user appserver
auth_opt_pg_password appserver

I must replace it with my lora-app-server postgre database configuration ==> dbname, user & password?

Then I configured a mosquitto user loraroot, it could be considered as superuser?

Under /etc/mosquitto/conf.d I kept my previous local.conf file:

allow_anonymous false
password_file /etc/mosquitto/pwd

#cafile /etc/mosquitto/certs/ca.crt
#certfile /etc/mosquitto/certs/hostname.crt
#keyfile /etc/mosquitto/certs/hostname.key

I must remove it, if yes what about allow_anonymous field ??? ==> Dumb I must add allow_anonymous to false in mosquitto-go-auth.conf

To be sure auth-plugin must be replace in mosquitto.conf or add in mosquitto-go-auth.conf or both?
In other words “General option” must be implemented in mosquitto.conf or mosquitto-go-auth.conf or both?

And last question, I have to replace the mosquitto password set in each .toml with the PBKDF2 passwords or it’s not necessary? ==> double dumb I must create hash password with my current password of each mqtt user?

./pw -p my_current_mqtt_user_plaint-text_password

NB: I havn’t .toml config file for the lora-gateway-bridge service because this is directly hosted in my mutlitech gateway.

Here several logs.

At the launch of the service mosquitto:

root@Debian02:~# mosquitto -c /etc/mosquitto/mosquitto.conf &
[1] 920
root@Debian02:~# 1531082169: mosquitto version 1.4.10 (build date 2018-05-05 22:35:52+0100) starting
1531082169: Config loaded from /etc/mosquitto/mosquitto.conf.
INFO[2018-07-08T21:36:10+01:00] Got 3 users from passwords file.

INFO[2018-07-08T21:36:10+01:00] created aclrecord {gateway/+/stats 2} for user loragw

INFO[2018-07-08T21:36:10+01:00] created aclrecord {gateway/+/rx 2} for user loragw

INFO[2018-07-08T21:36:10+01:00] created aclrecord {gateway/+/tx 1} for user loragw

INFO[2018-07-08T21:36:10+01:00] created aclrecord {gateway/+/stats 1} for user loraserver

INFO[2018-07-08T21:36:10+01:00] created aclrecord {gateway/+/tx 2} for user loraserver

INFO[2018-07-08T21:36:10+01:00] created aclrecord {gateway/+/rx 1} for user loraserver

INFO[2018-07-08T21:36:10+01:00] created aclrecord {application/+/node/+/rx 2} for user loraappserver

INFO[2018-07-08T21:36:10+01:00] created aclrecord {application/+/node/+/join 2} for user loraappserver

INFO[2018-07-08T21:36:10+01:00] created aclrecord {application/+/node/+/ack 2} for user loraappserver

INFO[2018-07-08T21:36:10+01:00] created aclrecord {application/+/node/+/error 2} for user loraappserver

INFO[2018-07-08T21:36:10+01:00] created aclrecord {application/+/node/+/tx 1} for user loraappserver

INFO[2018-07-08T21:36:10+01:00] Got 11 lines from acl file.

INFO[2018-07-08T21:36:10+01:00] Backend registered: Files
DEBU[2018-07-08T21:36:10+01:00] Initializing postgres backend with options:
DEBU[2018-07-08T21:36:10+01:00] pg_host: localhost
DEBU[2018-07-08T21:36:10+01:00] pg_password: ##########
DEBU[2018-07-08T21:36:10+01:00] pg_sslmode: disable
DEBU[2018-07-08T21:36:10+01:00] pg_aclquery: select distinct 'application/' || a.id || '/#' from "user" u inner join organization_user ou on ou.user_id = u.id inner join organization o on o.id = ou.organization_id inner join application a on a.organization_id =$
DEBU[2018-07-08T21:36:10+01:00] pg_user: loraappserver
DEBU[2018-07-08T21:36:10+01:00] pg_superquery: select count(*) from "user" where username = $1 and is_admin = true
DEBU[2018-07-08T21:36:10+01:00] pg_port: 5432
DEBU[2018-07-08T21:36:10+01:00] pg_dbname: loraappserver
DEBU[2018-07-08T21:36:10+01:00] pg_userquery: select password_hash from "user" where username = $1 and is_active = true limit 1
DEBU[2018-07-08T21:36:10+01:00] Postgres user query is: select password_hash from "user" where username = $1 and is_active = true limit 1
DEBU[2018-07-08T21:36:10+01:00] Postgres superuser query is: select count(*) from "user" where username = $1 and is_admin = true
DEBU[2018-07-08T21:36:10+01:00] Postgres acl query is: select distinct 'application/' || a.id || '/#' from "user" u inner join organization_user ou on ou.user_id = u.id inner join organization o on o.id = ou.organization_id inner join application a on a.organization_id =$
INFO[2018-07-08T21:36:10+01:00] Backend registered: Postgres
DEBU[2018-07-08T21:36:10+01:00] Initializing postgres backend with options:
DEBU[2018-07-08T21:36:10+01:00] pg_aclquery: select distinct 'application/' || a.id || '/#' from "user" u inner join organization_user ou on ou.user_id = u.id inner join organization o on o.id = ou.organization_id inner join application a on a.organization_id =$
DEBU[2018-07-08T21:36:10+01:00] pg_port: 5432
DEBU[2018-07-08T21:36:10+01:00] pg_user: loraappserver
DEBU[2018-07-08T21:36:10+01:00] pg_superquery: select count(*) from "user" where username = $1 and is_admin = true
DEBU[2018-07-08T21:36:10+01:00] pg_dbname: loraappserver
DEBU[2018-07-08T21:36:10+01:00] pg_userquery: select password_hash from "user" where username = $1 and is_active = true limit 1
DEBU[2018-07-08T21:36:10+01:00] pg_sslmode: disable
DEBU[2018-07-08T21:36:10+01:00] pg_host: localhost
DEBU[2018-07-08T21:36:10+01:00] pg_password: ############
DEBU[2018-07-08T21:36:10+01:00] Postgres user query is: select password_hash from "user" where username = $1 and is_active = true limit 1
DEBU[2018-07-08T21:36:10+01:00] Postgres superuser query is: select count(*) from "user" where username = $1 and is_admin = true
DEBU[2018-07-08T21:36:10+01:00] Postgres acl query is: select distinct 'application/' || a.id || '/#' from "user" u inner join organization_user ou on ou.user_id = u.id inner join organization o on o.id = ou.organization_id inner join application a on a.organization_id =$
INFO[2018-07-08T21:36:10+01:00] Backend registered: JWT
INFO[2018-07-08T21:36:10+01:00] Cache activated
ERRO[2018-07-08T21:36:10+01:00] couldn't start Redis, defaulting to no cache. error: ERR Client sent AUTH, but no password is set
INFO[2018-07-08T21:36:10+01:00] Prefixes enabled for backends  with prefixes filesprefix, pgprefix, jwtprefix.
1531082170: Opening websockets listen socket on port 9001.
1531082170: Opening ipv4 listen socket on port 1883.
1531082170: Opening ipv6 listen socket on port 1883.
1531082171: New connection from ::1 on port 1883.
1531082171: New connection from ::1 on port 1883.
DEBU[2018-07-08T21:36:11+01:00] checking user loraserver with backend Files
DEBU[2018-07-08T21:36:12+01:00] user loraserver authenticated with backend Files
1531082172: New client connected from ::1 as 359a901f-5f3f-487e-bf50-d400a284f695 (c1, k30, u'loraserver').
DEBU[2018-07-08T21:36:12+01:00] checking user loraappserver with backend Files
DEBU[2018-07-08T21:36:13+01:00] user loraappserver authenticated with backend Files
1531082173: New client connected from ::1 as 527ae1a1-2979-4ac2-8d98-31dd4cfe5f5c (c1, k30, u'loraappserver').

From the regular log file I only changed db_user,db_name and db_password.
So you can see an error:
ERRO[2018-07-08T21:36:10+01:00] couldn't start Redis, defaulting to no cache. error: ERR Client sent AUTH, but no password is set ???

Then if I use my client paho MQTT which is localhost listener (port:9001) I retrieved this error:

1531082288: Socket error on client <unknown>, disconnecting.
1531082288: Socket error on client <unknown>, disconnecting.
1531082288: Socket error on client <unknown>, disconnecting.

I have to store this client under mosquitto-go-auth?

To finish after starting the service after a few minutes I get back this kind of log:

1531082587: New connection from 192.168.0.26 on port 1883.
DEBU[2018-07-08T21:43:07+01:00] checking user loragw with backend Files
DEBU[2018-07-08T21:43:08+01:00] user loragw authenticated with backend Files
1531082588: New client connected from 192.168.0.26 as febfd1b0-dccb-4b51-b908-45db9761c21b (c1, k30, u'loragw').


auth-plugin.c: starting acl check at auth-plugin.c
clientid: febfd1b0-dccb-4b51-b908-45db9761c21b
username: loragw
topic: gateway/008000000000ace0/stats
access: 2


DEBU[2018-07-08T21:43:19+01:00] Superuser check with backend Files
DEBU[2018-07-08T21:43:19+01:00] Superuser check with backend Postgres
DEBU[2018-07-08T21:43:19+01:00] Checking Postgres for superuser with username loragw
DEBU[2018-07-08T21:43:19+01:00] sql query to be executed                      query="select count(*) from \"user\" where username = $1 and is_admin = true" username=loragw
DEBU[2018-07-08T21:43:19+01:00] Superuser check with backend JWT
DEBU[2018-07-08T21:43:19+01:00] jwt parse error: token contains an invalid number of segments

DEBU[2018-07-08T21:43:19+01:00] jwt get superuser error: token contains an invalid number of segments

DEBU[2018-07-08T21:43:19+01:00] Acl check with backend Files
INFO[2018-07-08T21:43:19+01:00] Files acl check with user loragw, topic: gateway/008000000000ace0/stats, clientid: febfd1b0-dccb-4b51-b908-45db9761c21b and acc: 2

INFO[2018-07-08T21:43:19+01:00] Files acl check passed.
DEBU[2018-07-08T21:43:19+01:00] user loragw acl authenticated with backend Files
DEBU[2018-07-08T21:43:19+01:00] Acl is %!s(bool=true) for user loragw


auth-plugin.c: starting acl check at auth-plugin.c
clientid: 359a901f-5f3f-487e-bf50-d400a284f695
username: loraserver
topic: gateway/008000000000ace0/stats
access: 1


DEBU[2018-07-08T21:43:19+01:00] Superuser check with backend Files
DEBU[2018-07-08T21:43:19+01:00] Superuser check with backend Postgres
DEBU[2018-07-08T21:43:19+01:00] Checking Postgres for superuser with username loraserver
DEBU[2018-07-08T21:43:19+01:00] sql query to be executed                      query="select count(*) from \"user\" where username = $1 and is_admin = true" username=loraserver
DEBU[2018-07-08T21:43:19+01:00] Superuser check with backend JWT
DEBU[2018-07-08T21:43:19+01:00] jwt parse error: token contains an invalid number of segments

DEBU[2018-07-08T21:43:19+01:00] jwt get superuser error: token contains an invalid number of segments

DEBU[2018-07-08T21:43:19+01:00] Acl check with backend Files
INFO[2018-07-08T21:43:19+01:00] Files acl check with user loraserver, topic: gateway/008000000000ace0/stats, clientid: 359a901f-5f3f-487e-bf50-d400a284f695 and acc: 1

INFO[2018-07-08T21:43:19+01:00] Files acl check passed.
DEBU[2018-07-08T21:43:19+01:00] user loraserver acl authenticated with backend Files
DEBU[2018-07-08T21:43:19+01:00] Acl is %!s(bool=true) for user loraserver

thanks for your help


#18

That’s correct.

I’n not sure what you mean by that. To make it clear, a user will be superuser if any backend answers OK for the superuser query.

There’s one exception for this: the Files backend doesn’t check for superusers, only authenticates users with passwords and authorizes them with acls. That’s because it aims to implement mosquitto’s default static checks that allow exactly that.

Going back to our case of implementing this for loraserver, if you follow the official docs and implement the suggested queries, superuser’s will be those that appear at the user table in lora-app-server’s db, and have is_active and is_admin fields marked as true.

There’s no official way of arranging your conf files, you could very well drop everything at /etc/mosquitto/mosquitto.conf, or even in a custom location (which you need to tell the mosquitto service or pass the -c argument to tell the conf path when running it manually). That said, people tend to separate mosquitto’s general options (ports, log locations, etc.) at mosquitto.conf, and then create anything else at /etc/mosquitto/conf.d/some-name.conf, in this case the plugin’s configuration params.

As for allow_anonymous, if you are doing auth, you probably want that set to false, else any anonymnous user will have access.

No, it’s not necessary, the .toml files expect the plain text password. When running, the plugin will hash those and compare to the stored hash password at Postgres. So yesy, you need to generate hashed password from your existing users at the .toml files and then add those to you password files for mosquitto.

As for the lora-gateway-bridge, we too host it at our gateways (one Multitech, the others a RPI 3 + Rak831), but that doesn’t mean it can’t be configured. In fact, you need to configure it with mosquitto credentials, or else it won’t be able to subscribe or publish to any topic if allow_anonymous is set to false.

That error means you are providing credentials when connecting to Redis but your Redis instance isn’t configured to expect them. You may leave auth_opt_cache_password out of the file to avoid passing a password, or reconfigure your Redis instance (I think the options is called requirepass, but please look for Redis documentation for that) and rerun it.

There are different Paho clients (python, Go, javascript, etc.), so I’m not sure and that could mean a lot of things. There’s an example call in the README using the javascript client from the lora-app-server’s UI, but take in consideration that it uses a websocket connection (so mosquitto must be compiled with websockets support and configured to serve and listen to websockets), and when connecting from a different host, it needs the broker to have tls enabled or won’t be able to connect.

Those “error” messages are perfectly fine. For example, for the user loragw you’ll get jwt get superuser error: token contains an invalid number of segments because indeed the password doesn’t look like a JWT token, which makes perfect sense. So Postrges will fail on authenticating that user, but the Files backend
will do it fine as you can tell from the message user loraserver acl authenticated with backend Files.

Well that’s a long answer :smile:. Hope it helps!


#19

First of all thank you for your time and your reply, this is really usefull for me :wink:

@iegomez sorry for my broken english :slight_smile:

So after commenting auth_opt_cache_password there’s no error, goodish

Regarding my client paho MQTT it’s javascript client and it worked well before mosquitto-auth-go plugin implementation, (I installed mosquitto with websocket support, I set mosquitto.conf as well, I’m connecting from localhost to listen port 9001 )

I am interested by this example but I can’t find it

Otherwise and after read your last comment about the log it seems that the plugin works well !

Thank you so much.


#20

Hi, no problem, I’m glad to help.

The example I mention is located at the JWT section, at the params mode subsection. Anyway, the important thing is that for credentials you should pass the JWT token as userName and any string as the password (it won’t be used, instead the token will be used to check the user’s auth). Also, it’s desirable to create some random clientid for the connection, as mosquitto won’t allow 2 or more connections to have the same clientid.

The mentioned code look like this. It’s a bit outdated, kind of messy and has some custom topics (sorry for all that), but it should give you an idea:

initMqttClient(applicationID, mode, devEUI) {
    const hostname = window && window.location && window.location.hostname;
    let wsbroker = hostname;  //mqtt websocket enabled broker
    let wsport = 1884; // port for above
    let date = new Date();
    let clientid = this.getRand() + "_" + date.getTime();
    console.log("Trying to connect to mqtt with hostname: " + hostname + " and clientid " + clientid);
    let mqttClient = new window.Paho.MQTT.Client(wsbroker, wsport,
        clientid);

    mqttClient.onConnectionLost = function (responseObject) {
      console.log("connection lost: " + responseObject.errorMessage);
    };
    mqttClient.onMessageArrived = function (message) {
      console.log(message.destinationName, ' -- ', message.payloadString);
    };

    let that = this;

    let sslOption = true;
    if(hostname == "localhost") {
      sslOption = false;
    }

    let options = {
      timeout: 3,
      userName: this.getToken(),
      password: "any",
      useSSL: sslOption,
      keepAliveInterval: 3600,
      reconnect: true,
      onSuccess: function () {
        console.log("mqtt connected");
        // Connection succeeded; subscribe to our topic, you can add multile lines of these

        let topic = 'application/' + applicationID + '/device/' + devEUI + '/data';
        console.log("Subscribing to topic " + topic);
        mqttClient.subscribe(topic, {qos: 0});
  
      },
      onFailure: function (message) {
        console.log("Connection failed: " + message.errorMessage);
      }
    };

    mqttClient.connect(options);
    return mqttClient;
  }