[rabbitmq-discuss] Creating an auth plugin (Kerberos)

Simon Lundström simlu at su.se
Tue Nov 13 14:35:05 GMT 2012


On Tue, 2012-11-13 at 11:07:34 +0000, Simon MacMullen wrote:
> There are two extension points within RabbitMQ here.
> 
> The EXTERNAL plugin is an example of how to implement a SASL
> mechanism (rabbit_auth_mechanism behaviour), i.e. control the
> exchange of information on the wire within AMQP, leading to authN.
> By default "the exchange of information ion the wire" means "send
> username and password", but it doesn't have to.
> 
> The rabbit_auth_backend behaviour then does two thing:
> 
> * Determines how to do authN for a user when we've already decided
> how to do the wire level stuff (e.g. look up in internal DB, make an
> LDAP query).
> 
> * Determines how to do authZ once we have a #user{} record.
> 
> - in other words it's a "user database".
> 
> Note that all the existing rabbit_auth_mechanism implementations
> call into rabbit_access_control:check_user_pass_login/2 or similar -
> that's the entry point to go and find out which rabbit_auth_backends
> are configured and go and query them for authentication.
> 
> I suspect to do Kerberos you will need both behaviours - from a
> quick google I *think* you need to control the wire level protocol
> with rabbit_auth_mechanism, and then create a #user{} based on that.

Now I understand it much better. For Kerberos authentication I don't
need to use SASL EXTERNAL but if we were to implement Kerberos GSSAPI
authentication we would have to use SASL EXTERNAL. It's easy to confuse
Kerberos auth and Kerberos GSSAPI auth (I've done it many times and it
always comes up in discussions on Kerberos authentication).

If we would implement GSSAPI support for the RabbitMQ authentication
plugin then all client libraries would have to support it too. Even
though we'd love to use GSSAPI, it's just too much work (for us, right
now atleast).

Might be something that VMware and or some other organisation is
interested in implementing though? We'll happily add it to "our" plugin!

> >1.2, If I use `-behaviour(rabbit_auth_backend).` I must implement
> >check_vhost_access and check_resource_access. What is the appropriate
> >way to handle this?
> >* Don't use `-behaviour(rabbit_auth_backend).`?
> >* Implement check_vhost_access and check_resource_access but just have
> >them call rabbit_access_control:check_vhost_access?
> >Later we are planning on implementing an authZ *only* plugin for
> >RabbitMQ, will this break if we want to have a plugin which only
> >implements check_vhost_access and check_resource_access?
> 
> Well, how do you want it to work? At the moment rabbit_auth_backend
> ties together authN and authZ, because if a backend is the authority
> that says that a user exists, it is likely to the be the only thing
> which can sensibly decide what that user is allowed to do.
> 
> You *could* decouple these by having one rabbit_auth_backend which
> implements check_user_login/2 and returns a #user{} with its
> auth_backend field set to a different rabbit_auth_backend that would
> then do authZ. We could add more support for that in RabbitMQ, but
> I'm not sure how your use case works.

Ah, that is exactly what I want. Aaah, so that's what the auth_backend
parameter in #user{} was!
So if I make what auth_backend rabbitmq-auth-backend-kerberos should use
as an option in the config I should be pretty much good to go.

Also, I'm intersted in the order/priority of auth_backends in
rabbitmq.config that I asked about in the reply to Emiles reply.
Because if I can just fall back on other configured backends then making
the auth_backend in #user{} configurable might not even be necessary?

As I would like it to work is that one could have multiple backends
configured in auth_backend in rabbitmq.config and they could handle both
or one of authN and authZ.

> >2, I have started to create my code and the idea is to use open_port to open an
> >external binary to do the actual Kerberos "talk". As a start I've made my to
> >check the exit status of /bin/true. See
> ><https://github.com/simmel/rabbitmq-auth-backend-kerberos>. The code compiles,
> >heh, and RabbitMQ starts and "accepts" the connection but some how it fails
> >anyway.
> 
> I ran your code. Check the SASL log (confusingly that's nothing to
> do with the SASL protocol, it's Erlang's "System Application Support
> Libraries".

Aaah! I thought it was SASL and even then I didn't look there this time
(because the last issue I had when developing the plugin it didn't log
anything there).

> In RabbitMQ terms it's the "log of things which
> crashed". You should see:
> 
> =CRASH REPORT==== 13-Nov-2012::11:04:03 ===
>   crasher:
>     initial call: rabbit_reader:init/4
>     pid: <0.334.0>
>     registered_name: []
>     exception exit: {unexpected_message,{'EXIT',#Port<0.25776>,normal}}
>       in function  rabbit_reader:handle_other/3
> (src/rabbit_reader.erl, line 363)
>       in call from rabbit_reader:start_connection/7
> (src/rabbit_reader.erl, line 238)
> 
> i.e. you got a message saying the port had closed. Your plugin
> (executing in the reader process) did not handle this message, and
> the reader process is designed to crash when it receives a message
> it didn't expect.
> 
> So make sure to receive that message in your plugin...

In my receive, here
<https://github.com/simmel/rabbitmq-auth-backend-kerberos/blob/213a831a61e1524cb187273b7d78ff5bc415a678/src/rabbit_auth_backend_kerberos.erl#L42-55>,
I tried to add:

    {'EXIT', Port, PosixCode} ->
      rabbit_log:error("EXIT: ~p~n", [PosixCode]),
      false;

however I still get the same error.
I even tried to add:
    {A, B, C} ->
      rabbit_log:error("EXIT: ~p : ~p : ~p~n", [A,B,C]),
      false;

just to see so I could "catch" every case but I still got the same error.

Since the crash report mentions a supervisor I suspected it had
something to do with "my" sup in
<https://github.com/simmel/rabbitmq-auth-backend-kerberos/blob/213a831a61e1524cb187273b7d78ff5bc415a678/src/rabbit_auth_backend_kerberos_app.erl>
so I tried to add this to the start function:
  receive
    {'EXIT', Port, PosixCode} ->
      rabbit_log:error("EXIT in sup: ~p~n", [PosixCode]),
      false
  end.

but RabbitMQ wouldn't even start and didn't log anything anywhere then = D

As you've probably guessed by Erlang coding so far has been trial and
error based and I have no idea what I'm doing really = )

Where should I handle the message?

Thanks!
- Simon


More information about the rabbitmq-discuss mailing list