基于PostgreSQL的MQTT访问控制
2018年5月25日
MQTT的轻量级发布-订阅模型是物联网系统的自然选择,但需要一些保护措施。与诸如HTTP之类的流行协议相比,文档更少,特别是对于特定的代理([Mosquitto(http://mosquitto.org)与Apache,有人吗?)。由于Mosquitto具有广泛的硬件支持,因此已经选择了Mosquitto,除了内置的访问控制列表之外,我还需要可扩展的每设备身份验证,该列表在测试中运行良好,但是需要重新启动服务以更新访问权限。已经配置了PostgreSQL安装,它是与设备相关的数据存储的自然场所。
mosquitto-auth-plug进行救援。这个Mosquitto插件将身份验证扩展到包括各种数据库。从Mosquitto 1.3.5和MySQL已有三年历史的指南中可以看出,该设置略有涉及。这篇文章建立在PostgreSQL(插件完全支持)和Mosquitto 1.5的基础上。假定已经安装了数据库,git和编译库(make,gcc,g ++)。使用Debian风格的命令。
初始安装和构建
由于必须使用到源的路径来构建插件,因此我必须先卸载Mosquitto(您可能要先从/ etc / mosquitto备份任何复杂的配置,并完全清除安装以避免重新安装时出现问题)。建造新鲜的蚊子的初始步骤包括:
sudo apt-get install libc-ares-dev libssl-dev uuid uuid-dev wget http://mosquitto.org/files/source/mosquitto-1.5.tar.gz tar xvzf mosquitto-1.5.tar.gz cd mosquitto-1.5 make mosquitto sudo make install
插件的设置保持不变:
git clone https://github.com/jpmens/mosquitto-auth-plug.git cd mosquitto-auth-plug cp config.mk.in config.mk
编辑config.mk,以说明不同的数据库和文件夹,
BACKEND_MYQSL ?= no # ... BACKEND_POSTGRES ?= yes # ... # Specify the path to the Mosquitto sources here MOSQUITTO_SRC = [path]/mosquitto-1.5 # And since I wanted to have websockets (allow browser clients), # this was the moment to set: WITH_WEBSOCKETS:=yes
([路径]取决于早期的tar)。
制作mosquitto-auth-plug包括:
make clean #recommended by the make script since configuration changed sudo apt-get install libpq-dev #The server was already installed and working, and this was supposed to install without it, but otherwise I hit a missing header error make sudo mv auth-plug.so /etc/mosquitto/ #As in the guide
配置和数据库
现在该配置Mosquitto了。我之前的mosquitto.conf具有一些持久性和日志设置,因此我将其保留在原处,或者用示例覆盖了所有内容。我添加了指南文本的修改版本,幸运的是PostgreSQL的文本更短:
auth_plugin /etc/mosquitto/auth-plug.so auth_opt_backends postgres auth_opt_dbname [dbname] %[use your own] auth_opt_user [user] auth_opt_pass [password] auth_opt_userquery SELECT pw FROM account WHERE username = $1 limit 1 auth_opt_superquery SELECT COALESCE(COUNT(*),0) FROM account WHERE username = $1 AND superuser = 1 auth_opt_aclquery SELECT topic FROM acls WHERE (username = $1) AND (rw & $2) > 0
最后三个是默认的PostgreSQL调用(原则上,我建议使用您自己的模式)。根据文档,
-
userquery:强制查询,返回给定用户的1x1结果和PBKDF2密码哈希
-
superquery:查询不受访问控制限制的超级用户,返回带有0/1的1x1条目,指示该用户是否是超级用户(有用,因为我需要一个全局用户才能从所有通道读取)
-
aclquery:查询返回具有任意行数的单列,每行包含一个MQTT主题字符串。
现在是时候测试np密码生成实用程序并设置数据库了。
./np #Enter a password twice Enter password: Re-enter same password: PBKDF2$sha256$901$gjOcwLF+xs92U5Mf$wHdnuPJTAdhTsy7bMOco+9vtO2W86K8h sudo psql -U [dbuser] \connect [database] #'You are now connected to database "[database]" as user "[dbuser]"' CREATE TABLE account ( id serial PRIMARY KEY, username varchar(20) NOT NULL, pw varchar(100) NOT NULL, superuser smallint NOT NULL DEFAULT 0, CONSTRAINT superuser CHECK (superuser = 0 OR superuser = 1) ); -- CREATE TABLE INSERT INTO account(username, pw) VALUES ('testuser', 'PBKDF2$sha256$901$gjOcwLF+xs92U5Mf$wHdnuPJTAdhTsy7bMOco+9vtO2W86K8h'); -- INSERT 0 1 SELECT * FROM account; -- Should show new user CREATE TABLE acls ( id serial PRIMARY KEY, username varchar(20) NOT NULL, topic varchar(40) NOT NULL, rw smallint NOT NULL DEFAULT 1, CONSTRAINT rw CHECK (rw >= 1 AND rw <= 4) ); -- CREATE TABLE INSERT INTO acls(username, topic, rw) VALUES ('testuser', 'testuser/#', 2); -- INSERT 0 1 SELECT * FROM acls; -- Should show new user
测验
cd /usr/local/sbin ./mosquitto -c /etc/mosquitto/mosquitto.conf
经过几次错误的启动(使用-c参数静默失败,但没有使用-c参数即可正常运行),日志使用相同的解决方案显示了Rex Xia描述的错误:
sudo ln -s /usr/local/lib/libmosquitto.so.1 /usr/lib/libmosquitto.so.1
从这里开始,Mosquitto和插件运行完美。请注意,在mosquitto.conf中配置的用户的PostgreSQL身份验证中的任何缺陷,或该用户对新创建的表的访问权限,都在这里暴露出来,从而使身份验证/授权检查提前终止。
成功看起来像
1527399177: |-- mosquitto_auth_unpwd_check(testuser) 1527399177: |-- ** checking backend postgres 1527399177: |-- GETTING USERS: testuser 1527399177: |-- getuser(testuser) AUTHENTICATED=1 by Postgres
在使用例如MQTT.fx进行订阅时(除testuser / password之外的所有凭据都无法连接),并且:
USERNAME: testuser, TOPIC: testuser/test, acc: 2 1527400680: |-- postgres: topic_matches(testuser/#, testuser/#) == 1 1527400680: |-- aclcheck(testuser, testuser/test, 2) trying to acl with postgres 1527400680: |-- aclcheck(testuser, testuser/test, 2) AUTHORIZED=1 by postgres
发布到测试用户/测试时。奇怪的是,尽管rw值3会同时授予读写权限,但是更改此值对于testuser而言还不够。由于每当我尝试订阅同一主题时都会显示以下内容,
1527401125: |-- USERNAME: testuser, TOPIC: testuser/#, acc: 4
我将权限设置为4,并且能够发布消息并使用相同的连接接收消息。尝试订阅任何不允许的主题仍然会导致连接终止。