zaqar-specs/specs/liberty/pre-signed-url.rst
Flavio Percoco d1d5cc8521 Move pre-signed URLs under liberty
Change-Id: I7e20fa968ab08f4e9257623837c1b6c66c74e8b2
2015-08-14 14:25:07 +02:00

306 lines
8.9 KiB
ReStructuredText

..
This template should be in ReSTructured text. The filename in the git
repository should match the launchpad URL, for example a URL of
https://blueprints.launchpad.net/zaqar/+spec/awesome-thing should be named
awesome-thing.rst.
Please do not delete any of the sections in this
template. If you have nothing to say for a whole section, just write: None
For help with syntax, see http://sphinx-doc.org/rest.html
To test out your formatting, see http://www.tele3.cz/jbar/rest/rest.html
======================
Pre-Signed URLs
======================
https://blueprints.launchpad.net/zaqar/+spec/pre-signed-url
There's a need to have pre-signed URLs - ala Swift's tempURL - that
would grant temporary access, to non-authenticated users, to specific
queues.
Problem description
===================
In cases where there's a need to allow "outsiders" of the system - i.e
guest-agents - to interact with Zaqar, it's useful to have a way to
grant them access so that they can do whatever is needed without
giving these "outsiders" a username and password or any other kind of
permission in the system.
This access, however, needs to be temporary, revocable and
granular. Outsiders shouldn't get access to more than 1 resource at a
time and the permissions granted should identify what kind of
operations they can execute.
Proposed change
===============
The proposed solution to the aforementioned problem is to use
pre-signed URLs. Services like Swift use a similar approach - called
temp URL there - to provide access to internal resources. The
pre-signed URL consists of a URL that contains a `hash` with the
encoded permissions.
In order to make it easier to read/write this spec, let's split it in
several parts. The first part will describe the parts composing the
URL. The second part will describe how the URL will be generated and
the third part how it'll be consumed.
Pre-Signed URL
--------------
A pre-signed URL ought to contain enough information for it to provide
enough control over the resource being shared, without compromising
security. Authorization is a must have and a huge part of this
URL. Therefore, the information present in this URL has to be
exhaustive in that perspective.
As mentioned in a previous paragraph, the URL contains a hashed piece
of information that serializes the required fields. Those fields are:
* Project: The `keystone` tenant of the entity generating the URL
* TTL: Expiration time (in seconds) for the URL
* Queue: `Zaqar`'s queue name that this URL gives access to
* HTTP Method: The `HTTP` method(s) this `URL` was created for. By
selecting the HTTP method, it's possible to give either read or
read/write access to a specific resource.
The above fields will be part of the generated hash but they'll also
be available as public information in the URL. The reason they're
encoded is to have a way to verify that the URL has not been changed
by the user whenever a request is made to `Zaqar`.
The generated hash will be an HMAC-SHA1. To generate such signature,
it is required to have a secret key. In swift, it's possible to have a
key per-account. Unfortunately, that's not possible in Zaqar +
Keystone, therefore this spec proposes adding a new configuration
option that will contain the key to use.::
[signed_url]
secret_key = some-very-strong-key
URL Generation
--------------
This spec proposes adding a new endpoint in the queue namespace that
returns the generated signature and expiration time that'll grant
access to this resource. The request and response for this operation:
Request:::
POST /v2/queues/shared_queue/share HTTP/1.1
...
{
'methods': ['GET', 'POST']
}
Response:::
HTTP/1.0 201 OK
...
{
'signature': '518b51ea133c4facadae42c328d6b77b',
'expires': 2015-05-31T19:00:17Z,
'project': '7d2f63fd4dcc47528e9b1d08f989cc00',
'url': '/v2/queues/shared_queue/messages',
'methods': ['GET', 'POST']
}
This request sets a different expiration date for the URL. Note that
the default method is `GET`.
Request:::
POST /v2/queues/shared_queue/share HTTP/1.1
...
{
'expires': 2015-06-19T19:00:00Z
}
Response:::
HTTP/1.0 201 OK
...
{
'signature': '518b51ea133c4facadae42c328d6b77b',
'expires': 2015-06-19T19:00:00Z,
'project': '7d2f63fd4dcc47528e9b1d08f989cc00',
'url': '/v2/queues/shared_queue/messages'
'methods': ['GET']
}
This request combines both parameters (methods and expires):
Request:::
POST /v2/queues/shared_queue/share HTTP/1.1
...
{
'methods': ['GET', 'POST'],
'expires': 2015-06-19T19:00:00Z
}
Response:::
HTTP/1.0 201 OK
...
{
'signature': '518b51ea133c4facadae42c328d6b77b',
'expires': 2015-06-19T19:00:00Z,
'project': '7d2f63fd4dcc47528e9b1d08f989cc00',
'url': '/v2/queues/shared_queue/messages'
'methods': ['GET', 'POST']
}
Consuming the URL
-----------------
First and foremost, it's important to mention that **NONE** of the
URL headers can/should be modified and/or omitted. As soon as one of
them is, the signature verification will fail and therefore the
request will respond 404.
Requests for pre-signed URLs will be processed by a middleware that
should be placed **before** keystone's middleware. This will allow us
to authenticate the request in advance and skip keystone's
authentication. A request using the signature generated in the
previous section would look like:
Request:::
GET /v2/queues/shared_queue/messages HTTP/1.1
Host: zaqar.example.com
User-Agent: python/2.7 killer-rabbit/1.2
Date: Wed, 28 Nov 2012 21:14:19 GMT
Accept: application/json
Accept-Encoding: gzip
URL-Signature: 518b51ea133c4facadae42c328d6b77b
URL-Expires: 2015-05-31T19:00:17Z
X-Project-Id: 7d2f63fd4dcc47528e9b1d08f989cc00
Client-ID: 30387f00-39a0-11e2-be4d-a8d15f34bae2
Note that, in the above example, headers were chosen over query
parameters. The main 2 reasons behind this choice are:
1. Consistency with other security related parameters - i.e
X-Project-Id - that are sent in HTTP headers.
2. These new parameters don't belong in the `messages` request and
won't affect `messages` navigation.
Similarly, other requests like the one below can be done.
Request:::
GET /v2/queues/shared_queue/messages?marker=1355-237242-783&limit=10 HTTP/1.1
Host: zaqar.example.com
User-Agent: python/2.7 killer-rabbit/1.2
Date: Wed, 28 Nov 2012 21:14:19 GMT
Accept: application/json
Accept-Encoding: gzip
URL-Signature: 518b51ea133c4facadae42c328d6b77b
URL-Expires: 2015-05-31T19:00:17Z
X-Project-Id: 7d2f63fd4dcc47528e9b1d08f989cc00
Client-ID: 30387f00-39a0-11e2-be4d-a8d15f34bae2
Filtering and pagination are not part of the signature and fall into
the `read` permissions that were granted on this.
Posting messages will work the same way:
Request:::
POST /v2/queues/shared_queue/messages HTTP/1.1
Host: zaqar.example.com
User-Agent: python/2.7 killer-rabbit/1.2
Date: Wed, 28 Nov 2012 21:14:19 GMT
Accept: application/json
Accept-Encoding: gzip
URL-Signature: 518b51ea133c4facadae42c328d6b77b
URL-Expires: 2015-05-31T19:00:17Z
X-Project-Id: 7d2f63fd4dcc47528e9b1d08f989cc00
Client-ID: 30387f00-39a0-11e2-be4d-a8d15f34bae2
...
Other Notes
-----------
1. In the case of pre-signed URLs, the queue cannot be created
lazily. This is to prevent cases where queues are deleted and
users still have a valid URL. This is not a big issues in cases
where there's just 1 pool. However, if there's a deployment using
more than 1 type of pool, the lazily created queue may end up in an
undesired pool and it'd be possible for an attacker to try a DoS on
that pool. Therefore, whenever a pre-signed URL is created, if a
pool doesn't exist, one will be created.
2. I'm not a fan of passing the `project-id` around but I can't think
of another way to do this and still have the ability to preserve
multi-tenancy without passing the project.
3. I don't like having the key set in the config file. In future
versions, we could think of making this information part of the
queue itself. The reason we can't do that right now is because we
don't have private fields in the metadata. It should be easy enough
to do it as an enhancement for this feature.
4. As a future enhancement, we could also use Barbican for key management.
Drawbacks
---------
Security issues may be added by this work. We ought to be extra
careful on reviews and create a vulnerability team that is ready to
address any issues that might come up.
Alternatives
------------
None
Implementation
==============
Assignee(s)
-----------
Primary assignee:
flaper87
Work Items
----------
1. Write utilities to generate the signature with proper tests
2. Add the endpoint that generates the pre-signed URL
3. Create a middleware capable of processing these URL
Dependencies
============
None
.. note::
This work is licensed under a Creative Commons Attribution 3.0
Unported License.
http://creativecommons.org/licenses/by/3.0/legalcode