rabbitmq / rabbitmq-auth-backend-http

HTTP-based authorisation and authentication for RabbitMQ
Other
199 stars 72 forks source link

Add vhost as request params to user_path #58

Closed hijklmno closed 7 years ago

hijklmno commented 7 years ago

I'm working on rabbit 3.6.10, Erlang R16B03, with rabbitmq_auth_backend_http version 3.6.12+2.g430e0e3 (as from rabbitmq-plugins list). Noticed a small discrepancy in the README, so just a quick text update.

pivotal-issuemaster commented 7 years ago

@hijklmno Please sign the Contributor License Agreement!

Click here to manually synchronize the status of this Pull Request.

See the FAQ for frequently asked questions.

pivotal-issuemaster commented 7 years ago

@hijklmno Thank you for signing the Contributor License Agreement!

hijklmno commented 7 years ago

I don't see where (or why) RabbitMQ would pass a vhost name to the authentication function. An arbitrary map of properties is passed to include socket information but it makes little sense to include vhost because ghost access check is a separate step.

Hi @michaelklishin, I will not be able to answer that question. I made no source code changes to the RabbitMQ server nor the plugin itself. I noticed the vhost surfacing as part of the request payload for user_path differed from the README.md. I will try my best to explain the steps to reproduce.

  1. Launch Ubuntu 14.04

  2. Launch RabbitMQ, activate rabbitmq_web_mqtt, rabbitmq_auth_backend_http community plugins.

  3. Setup RabbitMQ config /etc/rabbitmq/rabbitmq.config:

[
  {kernel, [

  ]},
  {rabbitmq_management, [
    {listener, [
                {port, 15672}
    ]}
  ]},
  {rabbit, [
    {auth_backends, [rabbit_auth_backend_http]},
    {cluster_nodes, {[], disc}},
    {cluster_partition_handling,ignore},
    {tcp_listen_options, [binary, {packet,raw},
                                  {reuseaddr,true},
                                  {backlog,128},
                                  {nodelay,true},
                                  {exit_on_close,false},
                                  {keepalive,false},
                                  {linger, {true,0}}]},

    {log_levels, [{ connection, info }]},
    {loopback_users, []},

    {default_user, <<"admin">>},
    {default_pass, <<"RabbitPass">>},
    {heartbeat, 580}

  ]}
  ,{rabbitmq_mqtt, [
    {default_user, <<"admin">>},
    {default_pass, <<"RabbitPass">>},
    {allow_anonymous, true},
    {vhost, <<"/">>},
    {exchange, <<"amq.topic">>},
    {subscription_ttl, 3600},
    {prefetch, 10},
    {ssl_listeners, []},
    {tcp_listeners, [1883]},
    {tcp_listen_options, [
      {backlog, 128},
      {nodelay, true}
    ]}
  ]},
  {rabbitmq_auth_backend_http,
   [{http_method,   post},
    {user_path,     "http://10.0.0.190:3004/auth/user"},
    {vhost_path,    "http://10.0.0.190:3004/auth/vhost"},
    {resource_path, "http://10.0.0.190:3004/auth/resource"},
    {topic_path,    "http://10.0.0.190:3004/auth/topic"}]}
].

Below is the status of rabbit:

Status of node 'rabbit@ip-10-0-1-55'
[{pid,13365},
 {running_applications,
     [{rabbitmq_web_mqtt,"RabbitMQ MQTT-over-WebSockets adapter",[]},
      {rabbitmq_management,"RabbitMQ Management Console","3.6.10"},
      {rabbitmq_web_dispatch,"RabbitMQ Web Dispatcher","3.6.10"},
      {cowboy,"Small, fast, modular HTTP server.","1.0.4"},
      {cowlib,"Support library for manipulating Web protocols.","1.0.2"},
      {rabbitmq_management_agent,"RabbitMQ Management Agent","3.6.10"},
      {rabbitmq_auth_backend_http,"RabbitMQ HTTP Authentication Backend",
          "3.6.12+2.g430e0e3"},
      {rabbitmq_mqtt,"RabbitMQ MQTT Adapter","3.6.10"},
      {rabbit,"RabbitMQ","3.6.10"},
      {os_mon,"CPO  CXC 138 46","2.2.14"},
      {amqp_client,"RabbitMQ AMQP Client","3.6.10"},
      {rabbit_common,
          "Modules shared by rabbitmq-server and rabbitmq-erlang-client",
          "3.6.10"},
      {inets,"INETS  CXC 138 49","5.9.7"},
      {ranch,"Socket acceptor pool for TCP protocols.","1.3.0"},
      {compiler,"ERTS  CXC 138 10","4.9.4"},
      {syntax_tools,"Syntax tools","1.6.12"},
      {ssl,"Erlang/OTP SSL application","5.3.2"},
      {public_key,"Public key infrastructure","0.21"},
      {asn1,"The Erlang ASN1 compiler version 2.0.4","2.0.4"},
      {crypto,"CRYPTO version 2","3.2"},
      {xmerl,"XML parser","1.3.5"},
      {mnesia,"MNESIA  CXC 138 12","4.11"},
      {sasl,"SASL  CXC 138 11","2.3.4"},
      {stdlib,"ERTS  CXC 138 10","1.19.4"},
      {kernel,"ERTS  CXC 138 10","2.16.4"}]},
 {os,{unix,linux}},
 {erlang_version,
     "Erlang R16B03 (erts-5.10.4) [source] [64-bit] [async-threads:64] [kernel-poll:true]\n"},
 {memory,
     [{total,60991752},
      {connection_readers,0},
      {connection_writers,0},
      {connection_channels,0},
      {connection_other,16560},
      {queue_procs,8656},
      {queue_slave_procs,0},
      {plugins,1414296},
      {other_proc,23198688},
      {mnesia,60640},
      {metrics,52528},
      {mgmt_db,186256},
      {msg_index,39112},
      {other_ets,2392192},
      {binary,1106560},
      {code,27159127},
      {atom,992409},
      {other_system,4414624}]},
 {alarms,[]},
 {listeners,
     [{clustering,25672,"::"},
      {amqp,5672,"::"},
      {mqtt,1883,"::"},
      {http,15672,"::"}]},
 {vm_memory_high_watermark,0.4},
 {vm_memory_limit,839062323},
 {disk_free_limit,50000000},
 {disk_free,6565482496},
 {file_descriptors,
     [{total_limit,924},{total_used,2},{sockets_limit,829},{sockets_used,0}]},
 {processes,[{limit,1048576},{used,365}]},
 {run_queue,0},
 {uptime,64187},
 {kernel,{net_ticktime,60}}]
  1. Spin up a sample NodeJS to listen to what RabbitMQ auth is sending arbitrarily at 10.0.0.190:
    
    var bodyParser = require('body-parser');

var app = express();

app.use(bodyParser.urlencoded({ extended: true }));

app.post('/auth/user', function(req, res) { console.log('user_path', req.body);

res.send('allow');

});

app.post('/auth/vhost', function(req, res) { console.log('vhost', req.body);

    res.send('allow');

});

app.post('/auth/resource', function(req, res) { console.log('resource', req.body);

    res.send('allow');

});

app.post('/auth/topic', function(req, res) { console.log('topic', req.body);

    res.send('allow');

});

app.listen(3004, function() { console.log('App is running on http://localhost:3004'); });


4. Subscribe via `[mosquitto_sub](https://mosquitto.org/man/mosquitto_sub-1.html)` with command: `mosquitto_sub -t 'testing/123' -h 10.0.1.55 -u '123' -P 'asdfPass'`

Response body I get for `user_path`:
`{ username: '123', password: 'asdfPass', vhost: '/' }`

All other response body matches README (except topic; which didn't surface from this MQTT sub). This is the output from NodeJS:
```user_path { username: '123', password: 'asdfPass', vhost: '/' }
vhost { username: '123', vhost: '/', ip: '::FFFF:10.0.0.166' }
resource { username: '123',
  vhost: '/',
  resource: 'queue',
  name: 'mqtt-subscription-mosqsub|20500-ip-10-0-0qos1',
  permission: 'configure' }
resource { username: '123',
  vhost: '/',
  resource: 'topic',
  name: 'testing/123',
  permission: 'read' }
resource { username: '123',
  vhost: '/',
  resource: 'queue',
  name: 'mqtt-subscription-mosqsub|20500-ip-10-0-0qos0',
  permission: 'configure' }
resource { username: '123',
  vhost: '/',
  resource: 'queue',
  name: 'mqtt-subscription-mosqsub|20500-ip-10-0-0qos0',
  permission: 'read' }
resource { username: '123',
  vhost: '/',
  resource: 'queue',
  name: 'mqtt-subscription-mosqsub|20500-ip-10-0-0qos0',
  permission: 'write' }
resource { username: '123',
  vhost: '/',
  resource: 'exchange',
  name: 'amq.topic',
  permission: 'read' }
  1. Publish via [mosquitto_pub](https://mosquitto.org/man/mosquitto_pub-1.html) with command: mosquitto_pub -t 'testing/pub' -h 10.0.1.55 -u '123' -P 'asdfPass' -m 'hey'

Response body I get for user_path: { username: '123', password: 'asdfPass', vhost: '/' }

This is the output from NodeJS. Again, all other response body matches README (except topic; which didn't surface from this MQTT sub).

vhost { username: '123', vhost: '/', ip: '::FFFF:10.0.0.166' }
resource { username: '123',
  vhost: '/',
  resource: 'queue',
  name: 'mqtt-subscription-mosqpub|20504-ip-10-0-0qos1',
  permission: 'configure' }
resource { username: '123',
  vhost: '/',
  resource: 'topic',
  name: 'testing/pub',
  permission: 'write' }
resource { username: '123',
  vhost: '/',
  resource: 'exchange',
  name: 'amq.topic',
  permission: 'write' }

Hope this helps.

michaelklishin commented 7 years ago

@hijklmno thank you for the detailed explanation and spending your time on improving the docs.

So this is with an MQTT connection. This won't be true for other protocols and documenting vhost as an argument for the authentication function can be misleading; authentication and vhost access authorization are two separate steps, by design.

MQTT does not really have the idea of vhosts/multi-tenancy at the protocol level, so RabbitMQ MQTT plugin adopted a convention plus the configurable default vhost. This is where such internal information kind of leaks through to the backend. AMQP 0-9-1, AMQP 1.0, STOMP should not have this problem.