Capabilities For Messaging
This article introduces the potential usage of capabilities as an access control mechanism in messaging middleware. It describes the motivation behind a proof-of-concept implementation that demonstrates the feasibility of a capability based approach to securing access in the RabbitMQ message broker. Whilst it was decided not to further develop this paradigm in Rabbit, the experiences gained from it may be valuable for anybody contemplating using capabilities to implement access control. Though not strictly necessary, a basic understanding of the AMQP execution model can be advantageous to appreciate the implications of this article.
Introduction
The RabbitMQ community has frequently requested the core engineering team to deliver an access control mechanism that allows untrusted users to connect to a broker and send or receive messages in a fashion that a server administrator can control. Classically, this kind of functionality falls into realm of access control lists, or ACLs for short. The only problem the remained was to categorize the types of operations that an administrator would typically want to control.
This appeared to be a relatively straight forward requirement for which something quite simple could be developed. However, in a pioneering spirit, the engineering team asked itself whether using traditional ACLs was the best (or only) solution to the problem. In order to answer this question, they searched for an alternative approach to solving the same problem.
After researching the topic, capabilities were identified as a potential alternative to ACLs. The pros and contras of both approaches were deliberated as well as implementing proof-of-concepts and in the end, the engineering team decided to go for ACLs after all. This was due to the changes to client side libraries that would have to take place in the case of capabilities. The ACL approach is not afflicted by this. Despite capabilities being ruled out for RabbitMQ, it remains a simple and powerful concept and the experiences gained from this exercise deserve some kind of documentation.
Disclaimer
This article does not attempt to deliver a generalized treatment of capabilities as a security concept. It merely discusses an application of the paradigm from a practical perspective, so the usage of some terminology may be specific to the use case at hand.
Capabilities
A capability is an unforgeable reference (or key) that allows access to an arbitrary entity. This follows the principle of least authority whereby a resource is accessed using the smallest possible set of privileges.
Imagine you want to prevent access to the front door of a house. Using a ACL, you would first identify the person wishing to enter the door and then verify that this person is actually authorized to open the door.
Using a capability, that person would be given a key to unlock the door. The mere possession of the key enables that person access to the house. In this form of access control, it is irrelevant who wants to enter, as long as they have the right key.
A capabililty allows access control to be tied to a specific door, and that door only. To acheive this with an ACL, I would have to make sure that the person granted access to the door in question was not also granted access to something else, by virtue of belonging to an access group. The upshot is that the capability permits an arbritrarily granular access restriction mechanism that does not require different types of privileges to be categorized a priori.
Decentralizing Access Control In RabbitMQ
The goal of this article is not to explain in full or discuss the relative merits of ACLs contra capabilities - there is enough academic literature covering this topic. Moreover, the goal of this article is to illustrate the benefits that a capability approach may have brought to a messaging broker.
Capabilities follow the philosophy that possession is nine tenths of the law. If I create something, therefore I own it. For example, if I create a queue in RabbitMQ, the server can generate a cryptographically secure name for my queue that nobody could guess. As long as I keep this name secret, nobody can feasibly put stuff into my queue or take stuff from it.
If I wanted to grant somebody access to this queue, then all I have to do is to give them a copy of the name. Once they have this name, they can do whatever they want with the queue, because they then possess a reference to the underlying entity that they want to access. If they can see it, they can access it. If they can't see it, they can't access it.
The upshot of this is that I can delegate authority to whoever I like without consulting the administrator. This decentralization potential brings huge gains - one no longer has to administer access to the message broker - whoever creates an entity controls the access to it. From a practical perspective, this
- frees a server administrator from having to care about access control at all;
- allows controllable access to unknown entities, e.g. people outside of a particular organization;
Delegating And Revoking Privileges
The second main advantage of capabilities is the ability to create delegates that encapsulate the access to the capability to access the actual underlying resource.
If I give somebody a key to a door, then they can open it. I can't take the key back, because doing so would be pointless if they had made a copy of it in the meantime. If I put a second door with an open passageway in front of the first, behind which a concierge of my appointment has the key to the first, I can give that person a key to the second door and they can access the first door using the key of the second. If I decide at some later stage that I no longer like that person, I can just remove the concierge with the key to the first door. Although I cannot take back the key to the second door, by removing the concierge, I render the second door useless and hence prevent access to the first.
This scheme can be extended recursively. If the person I want to authorize wants to authorize a third person in the same way, then that's their prerogative. Whether the second person creates their own concierge or just hands out the capability to the second door, by removing my concierge I prevent access to both of them. In this fashion the system is always recursively rooted so that you can safely give out as many capabilities as you want, provided that the root capability is keep secret.
The benefit of this in a multi-user and multi-participant messaging fabric is that a central instance is longer required to broaden or narrow the scope of access of any entity you would like to protect.
The User Decides How Access Is Granted
In contrast to an ACL based system, where the intentions of a user have to be taxonomized in order for the system to compute access, a capability based system leaves this up to whoever possesses the capability to access a resource.
In the RabbitMQ scenario, I may create an exchange where I want to give certain people read access (to bind queues to the exchange) and certain people write access (in order to publish messages). Using an ACL model, the server implementor would need to identify these two usage patterns a priori. Using a capability model, whoever creates the exchange can create separate delegates to allow read access and write access. In the concierge analogy above, this is akin to employing two concierges - one to let people in and one to let people out.
This approach allows the user to define their own access semantics, so not only do they control who has access to what, they can also determine what access to a particular resource actually means to them.
How This All Works
In order to be able to hand out access and take it back at some later stage, a resource is typically protected by a chain of two delegates. These two delegates form the fundamental facets of the delegation and revocation mechanism:
- The forwarding facet is a delegate whose capability is given to the person that you want to delegate something to;
- The revoking facet is the delegate that the forwarding facet points to and in turn can eliminate the capability of the forwarding facet.
The forwarding facet is given out to the delegatee, whilst the revoking facet is retained by the delegator. The delegatee can access the underlying resource as long as the delegator has not exercised the revoking facet. This way the delegator does not need to change the locks of the real entity they are trying to protect.
Furthermore, since all access control is hierarchically delegatable, the whole hierarchy has to be rooted by somebody. This is analogous to the super user of an operating system.
The Proof Of Concept
The proof of concept code can be downloaded from the RabbitMQ Mercurial repository, either using the hg command line tool or downloading a tarball. You will need a recent version of Erlang on your system to run this. These instructions are based on checking out the source from hg:
$ hg clone http://hg.rabbitmq.com/rabbitmq-codegen
$ hg clone http://hg.rabbitmq.com/rabbitmq-server
$ cd rabbitmq-server
$ hg up -C bug20149
$ make
After this has successfully been built, start an Erlang shell and run the main test:
$ erl -pa ebin
Erlang (BEAM) emulator version 5.6.5 [source] [smp:2] [async-threads:0]
Eshell V5.6.5 (abort with ^G)
1> rabbit_capability:test().
ok
2>
This runs two different tests:
- exchange_declare_test;
- bogus_intent_test
which will be explained separately.
These tests use a simulation of the core channel API in the RabbitMQ broker. Whilst this not the exact same API as in the core broker, the observant reader will recognize the similarity. The point is that the code serves as a proof of concept, that may have been fully integrated in the server codebase, had ACLs not been preferred over capabilities.
Restrictions and Assumptions
The astute reader of the code will notice that the command set of AMQP has been marginally tweaked to allow for a more straight forward implementation of capabilities. The rationale behind this is twofold:
- The current AMQP specification is 0.9.1 and will be revised before the protocol goes 1.0 - this gives an opportunity to extend the command set so that it can carry the necessary capabilities between client and server;
- A client library would have to be modified in any case in order to provide key distribution and delegate creation.
Hence it was decided to modify the execution model slightly in order to more easily prove this concept. It is possible impose this restriction and avoid changes to the execution model, but doing so provides more clarity about the usage of capabilities.
Exchange Declare Test
This is a test case to for creating and revoking forwarding capabilites, which follows the following steps:
There is a root capability to create exchanges;
Root creates a delegate to this functionality and gives the forwarding facet to Alice;
Alice now has the capability C to a delegate that can execute the AMQP exchange.declare command. To declare an exchange, Alice does the following:
- Sends an exchange.declare command as she would in a world without capabilities with the exception that she adds the capability C as an argument to the command;
- The channel detects the presence of the capability argument, resolves the delegate function and executes it with the exchange.declare command from Alice in situ;
- The result is returned to Alice which contains the capability to the exchange she just created;
If Alice wants to delegate the ability to create exchanges to Bob, she can either:
- Create a delegate that forwards to the delegate for which Alice has the capability C;
- Just give Bob the capability C;
By revoking any of the capabilities higher up the hierarchy, Bob's ability to create exchanges can be revoked at any time.
Bogus Intent Test
This is a test case for creating and forwarding capabilities on the same exchange entity. This demonstrates how different delegates encapsulate different intents in a way that is specified by the owner of the underlying entity. In this example, Alice creates an exchange and she wants to allow only Bob to read from it and only Carol to write to it:
There is a root capability to create exchanges and bindings as well as to publish messages;
Root creates a delegate to these functionalities and gives the forwarding facets to Alice;
Alice creates an exchange that she would like to protect;
Alice creates a delegate to allow Bob to bind queues to her exchange and a delegate to allow Carol to publish messages to her exchange;
After this has been verified, Bob and Carol try to be sneaky with the delegates they have been given. Each one of them tries to misuse the capability to perform a different action to the delegate they possess, i.e. Bob tries to send a message whilst Carol tries to bind a queue to the exchange - they both find out that their respective capabilities have been bound by intent.
Possible Extensions
The user defined semantics of a delegate chain stem from the fact that the action of creating a delegate is effectively shipping executable code from the user to the server. The body of a delegate is an arbitrary command that the delegator specifies. The delegatee can invoke this command, but the contents of the command have been baked into the delegate and hence are opaque to the delegatee. To keep the proof-of-concept simple, the command set has been restricted to the AMQP command set. The process of creating a delegate verifies that the commands that will ultimately be invoked on the server are actually valid AMQP commands. This restriction could be relaxed to allow for a richer command set to be provided to the user. This command set could range from just adding some simple logic constructs to the instruction set of a Turing complete language (for example the language that the server is implemented in).
Observations
The proof-of-concept code is small and concise - most of the code is test code or code designed to set up the root capabilities to system. The actual capability checking code is minimal. Whilst it does not use the real API directly, the proof-of-concept API is so similar to the real API that a conversion would be trivial. Because of the changes to the client, it was decided that ACLs were a more appropriate choice for an access control mechanism.
Hopefully this article has been helpful for anybody contemplating using capabilities to implement access restrictions in a system of their own.
References (1)
-
The developers of the popular open source AMQP broker named

Reader Comments (3)
This is very cool, Ben. It's inspired me to start thinking about how capabilities and URLs might line up in a ReSTful scenario... the possibilities are quite exciting, esp when combined with something like RestMS (http://wiki.amqp.org/spec:7).
nice post , its a great source of information , we can avail messaging concierge for best options .
Capabilities and REST have already been combined quite naturally over http/https in the Waterken server.