Gestione custom permessi su RabbitMQ con auth_backend_http

RabbitMQ ha la possibilità, mediante un plugin già presente all'installazione, di affiancare al suo meccanismo di autorizzazione utenti un ulteriore meccanismo di controllo degli accessi basato su un server http qualunque.

In parole povere, attivando questo meccanismo, ad ogni richiesta ricevuta RabbitMQ farà in autonomia una richiesta REST a un server, passando le credenziali dell'utente e il tipo di richiesta. Il server esterno risponderà con "allow" 0 "allow", per consentire o meno l'accesso. Il server può essere realizzato con qualunque tecnologia, e nei casi più semplici può essere sufficiente un semplice applicativo di 15 righe in NodeJS.

Le tipologie di permesso che possono essere gestite esternamente sono:

  • user: chiamata al momento della login con nome utente e password.
  • vhost: chiamata al momento in cui si chiede di accedere a un virtual host. Vengono passati il nome utente e il vhost richiesto.
  • resource_path: chiamata al momento in cui si chiede di accedere a una risorsa (exchange, coda, ...). Vengono passati, tra le altre cose, il nome utente, il tipo di risorsa e il suo nome.
  • topic_path: chiamata quando si cerca di leggere o scrivere un messaggio con uno specifico topic.

Ulteriori dettagli sulla pagina del plugin.

Per attivare il plugin "rabbitmq_auth_backend_http" è necessario inserirlo nell'elenco dei plugin disponibili (enables_plugins nella cartella dell'installazione)...

[rabbitmq_management, rabbitmq_mqtt, rabbitmq_web_mqtt, rabbitmq_auth_backend_http, rabbitmq_auth_backend_cache].
Esempio di enabled_plugins

...o attivarlo con l'apposito comando:

rabbitmq-plugins enable rabbitmq_auth_backend_http

Bisogna poi modificare il file di configurazione di RabbitMQ (rabbitmq.conf) per abilitarne l'utilizzo da parte di RabbitMQ. Si specifica la catena di gestori di autorizzazione, e l'indirizzo degli endpoint REST da interrogare.

Nota: nell'esempio sotto viene presentato un caso tipico in cui l'utente in fase di richiesta viene gestito dal sistema degli utenti interni di RabbitMQ e, se non presente nella lista, viene verificato con il server REST. Inoltre, è stato utilizzato il plugin "rabbitmq_auth_backend_cache" che fa da cache al server REST, per ridurne gli accessi.

auth_backends.1 = internal
auth_backends.2 = cache
auth_cache.cached_backend = http

auth_http.http_method   = get
auth_http.user_path     = http://mioserver:1234/user
auth_http.vhost_path    = http://mioserver:1234/vhost
auth_http.resource_path = http://mioserver:1234/resource
auth_http.topic_path    = http://mioserver:1234/topic
rabbitmq.conf

Fatte le modifiche è sufficiente riavviare RabbitMQ e il nuovo sistema sarà operativo. A questo punto non resta che predisporre un server di autenticazione. Lascio sotto un piccolo esempio in NodeJS:

const express = require('express')
const app = express()
const port = 1234

app.get('/user', (req, res) => {
  if (req.query.username === 'mario') return 'allow';
  return 'deny';
})
app.get('/vhost', (req, res) => {
  return 'allow';
})
app.get('/resource', (req, res) => {
  return 'allow';
})
app.get('/topic', (req, res) => {
  return 'allow';
})

app.listen(port, () => {
  console.log(`Example RabbitMQ auth server listening at http://localhost:${port}`)
})