使用webhooks为VerneMQ实现ACL

时间:2017-08-22 08:58:35

标签: caching mqtt webhooks

我试图通过为VerneMQ实现我自己的webhook来获得ACL行为。我正在使用expressapicache节点包。我希望代码对非JavaScript程序员也有意义。

在我的vernemq.conf我设置了我的钩子,它们被正确调用:

$ vmq-admin webhooks show
+-----------------+------------------------------+-------------+
|      hook       |           endpoint           |base64payload|
+-----------------+------------------------------+-------------+
|auth_on_subscribe|http://127.0.0.1:3000/vmq/sub |    true     |
|auth_on_register |http://127.0.0.1:3000/vmq/auth|    true     |
| auth_on_publish |http://127.0.0.1:3000/vmq/pub |    true     |
+-----------------+------------------------------+-------------+

此外,我禁用了所有其他插件并禁用了匿名登录。

我在express(简化)中的webhooks实现:

const express = require('express');
const apicache = require('apicache');
const bodyparser = require('body-parser');

// short cache times for demonstration
const authcache = apicache.middleware('15 seconds');
const pubcache = apicache.middleware('5 seconds');
const subcache = apicache.middleware('10 seconds');

const app = express();

const jsonparser = bodyparser.json();

app.use((req, res, next) => {
    console.log(`${req.connection.remoteAddress}:${req.connection.remotePort} ${req.method} ${req.path}`);
    return next();
});

app.post('/vmq/auth', authcache, (req, res) => {
    return res.status(200).json({result: 'ok'});
});

app.post('/vmq/pub', pubcache, jsonparser, (req, res) => {
    // this gets ignored most of the time because of caching
    if (req.body.topic === 'only/allowed/topic') {
        return res.status(200).json({result: 'ok'});
    }
    return res.status(401).end();
});

app.post('/vmq/sub', subcache, (req, res) => {
    return res.status(200).json({result: 'ok'});
});

app.use((req, res, next) => {
    return res.status(404).end();
});

app.use((err, res, req, next) => {
    console.error(err);
    return res.status(500).end();
});

const server = app.listen(3000, 'localhost', () => {
    const address = server.address();
    return console.log(`listening on ${address.address}:${address.port} ...`);
});

使用mqtt.js我写了一个客户端(简化):

const mqtt = require('mqtt');

const client = mqtt.connect('mqtt://localhost');

client.on('connect', () => {
    setInterval(() => {
        client.publish('only/allowed/topic', 'working');
        client.publish('some/disallowed/topic', 'working too :(');
    }, 500);
    return client.subscribe('some/disallowed/topic');
});

client.on('message', (topic, message) => {
   return console.log(`${topic}:${message}`); 
});

客户端成功进行身份验证,然后发布到only/allowed/topic,这是允许的,并由VerneMQ成功缓存。但是,由于现在已成功调用/vmq/pub,因此发布到some/disallowed/topic也有效。如果我改变发布顺序,两者都会失败。

我原本希望VerneMQ将缓存的结果映射到调用中的所有参数,当然除了有效负载,而不仅仅是客户端连接。然而事实并非如此。 使用缓存时通过webhook实现ACL的可行方法是什么?不使用缓存是不可能的,因为这会导致我的性能下降,并且文档会建议缓存。

此外,有1500多名代表的人会这么好并创建标签vernemq吗? :)

1 个答案:

答案 0 :(得分:1)

我误解了apicache如何运作以及它实际上做了什么。我需要做的只是为缓存设置适当的标题,如VerneMQ的文档中所述。显然apicache存储实际结果,并在指定的时间范围内发出请求,无论客户端实际请求的是什么。

这是现在的工作代码:

const express = require('express');
const bodyparser = require('body-parser');

const app = express();

// short cache times for demonstration (in seconds)
const authcachetime = 15;
const pubcachetime = 5;
const subcachetime = 10;

const jsonparser = bodyparser.json();

app.use((req, res, next) => {
    console.log(`${req.connection.remoteAddress}:${req.connection.remotePort} ${req.method} ${req.path}`);
    return next();
});

app.post('/vmq/auth', (req, res) => {
    res.set('cache-control', `max-age=${authcachetime}`);
    return res.status(200).json({result: 'ok'});
});

app.post('/vmq/pub', jsonparser, (req, res) => {
    res.set('cache-control', `max-age=${pubcachetime}`);
    if (req.body.topic === 'only/allowed/topic') {
        return res.status(200).json({result: 'ok'});
    }
    return res.status(401).end();
});

app.post('/vmq/sub', (req, res) => {
    res.set('cache-control', `max-age=${subcachetime}`);
    return res.status(200).json({result: 'ok'});
});

app.use((req, res, next) => {
    return res.status(404).end();
});

app.use((err, res, req, next) => {
    console.error(err);
    return res.status(500).end();
});

const server = app.listen(3000, 'localhost', () => {
    const address = server.address();
    return console.log(`listening on ${address.address}:${address.port} ...`);
});

正如预期的那样,客户端在尝试发布到非法主题时会收到错误。