UAA configuration demo

This commit is contained in:
Daniil Fedotov 2017-09-20 16:42:42 +01:00
parent 3ee9e3f218
commit cdb142e68a
7 changed files with 310 additions and 25 deletions

View File

@ -13,26 +13,101 @@ To test the plugin, you should build it yourself, together with RabbitMQ.
You can find build instructions [here](https://github.com/rabbitmq/rabbitmq-public-umbrella)
### Authorization Workflow
This plugin does not communicate with an UAA server. It decodes an access token and
authorises a user based on the token data.
The token can be any JWT token, which contains `scope` and `aud` fields.
The way the token was retrieved (such as grant type) is outside of this plufin scope.
#### Prerequisites
1. A symmetrically encrypted JWT token containing RabbitMQ scopes.
2. RabbitMQ auth_backends should include `rabbit_auth_backend_uaa`
3. The RabbitMQ scope prefix (e.g. `rabbitmq` in `rabbitmq.read:*/*`) needs to
match the `resource_server_id` configuration (empty by default).
#### Authorization
1. Client authorize with UAA, requesting an `access_token` (using any grant type)
2. Token scope should contain RabbitMQ resource scopes (e.g. `configure:%2F/foo` means "configure queue 'foo' in vhost '/'")
3. Client passes token as the username when connecting to a RabbitMQ node. The password
field is not used.
### Usage
First, enable the plugin. Then, configure access to UAA:
The plugin should be configured with UAA signing key to check token signatures.
You can get the signing key from the UAA server using
[token_key api](https://docs.cloudfoundry.org/api/uaa/version/4.6.0/index.html#token-key-s)
or [uaac](https://github.com/cloudfoundry/cf-uaac) using `uaac signing key` command.
``` erlang
[{rabbitmq_auth_backend_uaa,
[{resource_server_id, <<"your-resource-server-id"}]},
{uaa_jwt, [
{default_key, <<"key1">>},
{signing_keys, #{
<<"key1">> => {map, #{<<"kty">> => <<"oct">>, <<"k">> => <<"dG9rZW5rZXk">>}},
<<"key2">> => {pem_file, <<"/path/to/public_key.pem">>},
<<"key3">> => {json, "{\"kid\":\"key3\",\"alg\":\"HMACSHA256\",\"value\":\"tokenkey\",\"kty\":\"MAC\",\"use\":\"sig\"}"}}}]}].
Important fields are `kty`, `value`, `alg` and `kid`.
For example if UAA returns
```
uaac signing key
kty: RSA
e: AQAB
use: sig
kid: legacy-token-key
alg: RS256
value: -----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2dP+vRn+Kj+S/oGd49kq
6+CKNAduCC1raLfTH7B3qjmZYm45yDl+XmgK9CNmHXkho9qvmhdksdzDVsdeDlhK
IdcIWadhqDzdtn1hj/22iUwrhH0bd475hlKcsiZ+oy/sdgGgAzvmmTQmdMqEXqV2
B9q9KFBmo4Ahh/6+d4wM1rH9kxl0RvMAKLe+daoIHIjok8hCO4cKQQEw/ErBe4SF
2cr3wQwCfF1qVu4eAVNVfxfy/uEvG3Q7x005P3TcK+QcYgJxav3lictSi5dyWLgG
QAvkknWitpRK8KVLypEj5WKej6CF8nq30utn15FQg0JkHoqzwiCqqeen8GIPteI7
VwIDAQAB
-----END PUBLIC KEY-----
n: ANnT_r0Z_io_kv6BnePZKuvgijQHbggta2i30x-wd6o5mWJuOcg5fl5oCvQjZh15IaPar5oXZLHcw1bHXg5YSiHXCFmnYag83bZ9YY_9tolMK4R9G3eO-YZSnLImfqMv7HYBoAM75pk0JnTKhF6ldgfavShQZqOAIYf-vneMDNax_ZMZdEbzACi3vnWqCByI6JPIQju
HCkEBMPxKwXuEhdnK98EMAnxdalbuHgFTVX8X8v7hLxt0O8dNOT903CvkHGICcWr95YnLUouXcli4BkAL5JJ1oraUSvClS8qRI-Vino-ghfJ6t9LrZ9eRUINCZB6Ks8Igqqnnp_BiD7XiO1c
```
where
The configuration for a signing key should be:
* `your-resource-server-id` is a resource server ID (e.g. 'rabbitmq')
* `signing_keys` is a map of keys to sign JWT tokens (see [UAA_JWT](https://github.com/rabbitmq/uaa_jwt) library for mode info)
* `default_key` is the default value used for the `kid` (key id) header parameter.
```erlang
[ {rabbitmq_auth_backend_uaa, [{resource_server_id, <<"my_rabbit_server">>}]},
{uaa_jwt, [
{signing_keys, #{
<<"legacy-token-key">> => {map, #{<<"kty">> => <<"RSA">>,
<<"alg">> => <<"RS256">>,
<<"kid">> => <<"legacy-token-key">>,
<<"value">> => <<"-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2dP+vRn+Kj+S/oGd49kq
6+CKNAduCC1raLfTH7B3qjmZYm45yDl+XmgK9CNmHXkho9qvmhdksdzDVsdeDlhK
IdcIWadhqDzdtn1hj/22iUwrhH0bd475hlKcsiZ+oy/sdgGgAzvmmTQmdMqEXqV2
B9q9KFBmo4Ahh/6+d4wM1rH9kxl0RvMAKLe+daoIHIjok8hCO4cKQQEw/ErBe4SF
2cr3wQwCfF1qVu4eAVNVfxfy/uEvG3Q7x005P3TcK+QcYgJxav3lictSi5dyWLgG
QAvkknWitpRK8KVLypEj5WKej6CF8nq30utn15FQg0JkHoqzwiCqqeen8GIPteI7
VwIDAQAB
-----END PUBLIC KEY-----">>}}
}}
]}
].
```
If you are using a symmetric key, the configuration will be:
```erlang
[ {rabbitmq_auth_backend_uaa, [{resource_server_id, <<"my_rabbit_server">>}]},
{uaa_jwt, [
{signing_keys, #{
<<"legacy-token-key">> => {map, #{<<"kty">> => <<"MAC">>,
<<"alg">> => <<"HMACSHA256">>,
<<"kid">> => <<"legacy-token-key">>,
<<"value">> => <<"my_signing_key">>}}
}}
]}
].
```
see [UAA_JWT](https://github.com/rabbitmq/uaa_jwt) library for more info
`resource_server_id` is a prefix for scopes in UAA to avoid overlap of scopes.
It is empty by default
To learn more about UAA/OAuth 2 clients, see [UAA docs](https://github.com/cloudfoundry/uaa/blob/master/docs/UAA-APIs.rst#id73).
@ -77,19 +152,54 @@ These are the typical permissions examples:
See the [./test/wildcard_match_SUITE.erl](wildcard matching test suite) and [./test/scope_SUITE.erl](scopes test suite) for more examples.
### Authorization Workflow
Scopes should be prefixed with `resource_server_id`. For example,
if `resource_server_id` is "my_rabbit", a scope to enable read from any vhost will
be `my_rabbit.read:*/*`
#### Prerequisites
### Examples
1. A symmetrically encrypted JWT token containing RabbitMQ scopes.
2. RabbitMQ auth_backends should include `rabbit_auth_backend_uaa`
3. The RabbitMQ scope prefix (e.g. `rabbitmq` in `rabbitmq.read:*/*`) needs to
match the `resource_server_id` configuration (empty by default).
The [demo](/demo) directory contains configuration files which can be used to set up
a development UAA server and issue tokens, which can be used to access RabbitMQ
resources.
#### Authorization
To run the demo you should download [UAA](https://github.com/cloudfoundry/uaa)
1. Client authorize with UAA, requesting an `access_token` (using any grant type)
2. Token scope should contain RabbitMQ resource scopes (e.g. `configure:%2F/foo` means "configure queue 'foo' in vhost '/'")
3. Client passes token as the username when connecting to a RabbitMQ node. The password
field is not used.
You can set `CLOUD_FOUNDRY_CONFIG_PATH` to `demo/symmetric_keys` to set
signing key to be symmetric `MAC` key. The value is `rabbit_signing_key`.
`demo/symmetric_keys/rabbit.config` contains a RabbitMQ configuration file to
set up this signing key for RabbitMQ.
To run UAA, from the UAA directory:
```
CLOUD_FOUNDRY_CONFIG_PATH=<path_to_plugin>/demo/symmetric_keys ./gradlew run
```
To run RabbitMQ:
```
## To run rabbitmq-server
RABBITMQ_CONFIG_FILE=<path_to_plugin>/demo/symmetric_keys/rabbitmq rabbitmq-server
## Or to run from source from the plugin directory
make run-broker RABBITMQ_CONFIG_FILE=demo/symmetric_keys/rabbitmq
```
You should enable `rabbitmq_auth_backend_uaa` plugin in RabbitMQ.
Or to use an RSA key, you can set `CLOUD_FOUNDRY_CONFIG_PATH` to `demo/rsa_keys`.
This directory also contains `rabbit.config` file, as well as `public_key.pem`,
which will be used for signature verification.
UAA sets scopes from client scopes and user groups. The demo uses groups to set up
a set of RabbitMQ permissions scopes.
The `demo/setup.sh` script can be used to configure a demo user and groups.
The script will also create RabbitMQ resources associated with permissions.
The script uses `uaac` and `bunny` (RabbitMQ client) and requires rubygems to be installed.
When running the script, UAA server and RabbitMQ server should be running.
You should configure `UAA_HOST` (localhost:8080/uaa for local machine) and
`RABBITMQCTL` (a path to `rabbitmqctl` script) environment variables to run this script.
Please refer to `demo/setup.sh` to get more info about configuring UAA permissions.
The script will return an access tokens, which can be used to authorise
in RabbitMQ. When authorising, you should use the token as a **username**.

View File

@ -0,0 +1,16 @@
#!/usr/bin/env ruby
require 'bunny'
queues = ARGV
queues.each do |q|
split = q.split("/")
vhost = split[0]
queue_name = split[1]
conn = Bunny.new(:vhost => vhost)
conn.start
ch = conn.create_channel
ch.queue(queue_name)
conn.stop
end

View File

@ -0,0 +1,21 @@
[{rabbit,
[{auth_backends, [rabbit_auth_backend_uaa, rabbit_auth_backend_internal]}]},
{rabbitmq_auth_backend_uaa,
[{resource_server_id, <<"rabbitmq">>}]},
{uaa_jwt, [
{default_key, <<"legacy-token-key">>},
{signing_keys,
#{
<<"legacy-token-key">> =>
{pem, <<"-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2dP+vRn+Kj+S/oGd49kq
6+CKNAduCC1raLfTH7B3qjmZYm45yDl+XmgK9CNmHXkho9qvmhdksdzDVsdeDlhK
IdcIWadhqDzdtn1hj/22iUwrhH0bd475hlKcsiZ+oy/sdgGgAzvmmTQmdMqEXqV2
B9q9KFBmo4Ahh/6+d4wM1rH9kxl0RvMAKLe+daoIHIjok8hCO4cKQQEw/ErBe4SF
2cr3wQwCfF1qVu4eAVNVfxfy/uEvG3Q7x005P3TcK+QcYgJxav3lictSi5dyWLgG
QAvkknWitpRK8KVLypEj5WKej6CF8nq30utn15FQg0JkHoqzwiCqqeen8GIPteI7
VwIDAQAB
-----END PUBLIC KEY-----">>}
}
}]
}].

View File

@ -0,0 +1,41 @@
jwt:
token:
signing-key: |
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA2dP+vRn+Kj+S/oGd49kq6+CKNAduCC1raLfTH7B3qjmZYm45
yDl+XmgK9CNmHXkho9qvmhdksdzDVsdeDlhKIdcIWadhqDzdtn1hj/22iUwrhH0b
d475hlKcsiZ+oy/sdgGgAzvmmTQmdMqEXqV2B9q9KFBmo4Ahh/6+d4wM1rH9kxl0
RvMAKLe+daoIHIjok8hCO4cKQQEw/ErBe4SF2cr3wQwCfF1qVu4eAVNVfxfy/uEv
G3Q7x005P3TcK+QcYgJxav3lictSi5dyWLgGQAvkknWitpRK8KVLypEj5WKej6CF
8nq30utn15FQg0JkHoqzwiCqqeen8GIPteI7VwIDAQABAoIBAFsB5FszYepa11o3
4zSPxgv4qyUjuYf3GfoNW0rRGp3nJLtoHAIYa0CcLX9kzsQfmLtxoY46mdppxr8Z
2qUZpBdRVO7ILNfyXhthdQKI2NuyFDhtYK1p8bx6BXe095HMcvm2ohjXzPdTP4Hq
HrXAYXjUndUbClbjMJ82AnPF8pM70kBq7g733UqkdfrMuv6/d95Jiyw4cC7dGsI3
Ruz9DGhiAyCBtQ0tUB+6Kqn5DChSB+ccfMJjr6GnCVYmERxEQ5DJCTIX8am8C6KX
mAxUwHMTsEGBU6GzhcUgAwUFEK3I9RptdlRFp7F8E/P0LxmPkFdgaBNUhrdnB7Y4
01n1R1kCgYEA/huFJgwVWSBSK/XIouFuQrxZOI9JbBbdmpFT7SBGCdFg26Or9y7j
+N5HE7yuoZ9PkBh17zzosZdsJhGocRYvO0LSq8cXvKXKCwn2fTMM7uJ/oQe68sxG
cF/fC0M/8LvRESWShH920rrERu0s161RuasdOPre0aXu7ZQzkQ68O6MCgYEA23NO
DHKNblBOdFEWsvotLqV8DrIbQ4le7sSgQr56/bdn9GScZk2JU0f+pqzpiGUy9bIt
6uujvt5ar0IvpIQVdjf3dbp6Fy+Dwhd4yTR4dMdDECest7jL++/21x8Y0ywFhBIK
yEd+QxpOLXP6qaSKTGxL2rnTXRjl8/g629xQPL0CgYEAkNNOh+jLIgjxzGxA9dRV
62M91qaTyi8eDkJV+wgx4taaxZP7Jt5qwCSvjegz/5m01wOZ88hbNxx+XxQhVJK4
SKZFO/I07Sfwh2oeOi0maeBdrYGiY09ZtiJuFRU3FBV3irZHU4zyRBh+VY5HyITX
12JXPWp+JC7WhkG5QiuLzNECgYEA15OBzICLpx6Es4clAVT6JaSzJcyZM9MyyuOl
e2ubbrpJCK/9ZBIvIPzMj/e0wiSH1wzeRrSM+ud7tkcSfk6ytptsIN67KSOoD3b3
VNCStEU7ABe5eBG1cRzeI52MyYWpNYBzzyNMSacBvWz9hMD6ivCn44pAtGfNHclw
KKNYvxECgYBOamf25md9Jy6rtQsJVEJWw+8sB4lBlKEEadc5qekR7ZQ0hwj8CnTm
WOo856ynI28Sog62iw8F/do/z0B29RuGuxw+prkBkn3lg/VQXEitzqcYvota6osa
8XSfaPiTyQwWpzbFNZzzemlTsIDiF3UqwkHvWaMYPDf4Ng3cokPPxw==
-----END RSA PRIVATE KEY-----
verification-key: |
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2dP+vRn+Kj+S/oGd49kq
6+CKNAduCC1raLfTH7B3qjmZYm45yDl+XmgK9CNmHXkho9qvmhdksdzDVsdeDlhK
IdcIWadhqDzdtn1hj/22iUwrhH0bd475hlKcsiZ+oy/sdgGgAzvmmTQmdMqEXqV2
B9q9KFBmo4Ahh/6+d4wM1rH9kxl0RvMAKLe+daoIHIjok8hCO4cKQQEw/ErBe4SF
2cr3wQwCfF1qVu4eAVNVfxfy/uEvG3Q7x005P3TcK+QcYgJxav3lictSi5dyWLgG
QAvkknWitpRK8KVLypEj5WKej6CF8nq30utn15FQg0JkHoqzwiCqqeen8GIPteI7
VwIDAQAB
-----END PUBLIC KEY-----

View File

@ -0,0 +1,76 @@
#!/usr/bin/env bash
set -e
gem install bunny
gem install cf-uaac
target=${UAA_HOST:="localhost:8080/uaa"}
ctl=${RABBITMQCTL:="rabbitmqctl"}
# Target the server
uaac target $target
# Get admin client to manage UAA
uaac token client get admin -s adminsecret
# Set permission to list signing keys
uaac client update admin --authorities "clients.read clients.secret clients.write uaa.admin clients.admin scim.write scim.read uaa.resource"
# Rabbit UAA user
# The user name and password needed to retrieve access tokens,
# RabbiMQ does not have to be aware of this credentials.
uaac user add rabbit_super -p rabbit_super --email rabbit_super@example.com
uaac user add rabbit_nosuper -p rabbit_nosuper --email rabbit_nosuper@example.com
# Create permissions
# Uaa groups will become scopes, which should define RabbitMQ permissions.
uaac group add "read:*/*"
uaac group add "write:*/*"
uaac group add "configure:*/*"
uaac group add "write:uaa_vhost/*"
uaac group add "read:uaa_vhost/some*"
# Assigning groups to users.
# rabbit_super will be able to read,write and configure any resources.
uaac member add "read:*/*" rabbit_super
uaac member add "write:*/*" rabbit_super
uaac member add "configure:*/*" rabbit_super
# rabbit_nosuper will be able to read uaa_vhost resources starting with some
# and write to any uaa_vhost resources
uaac member add "write:uaa_vhost/*" rabbit_nosuper
uaac member add "read:uaa_vhost/some*" rabbit_nosuper
# Configure RabbiqMQ
# Add uaa_vhost and other vhost to check permissions
$ctl add_vhost uaa_vhost
$ctl add_vhost other_vhost
# Set guest user permissions to create queues.
$ctl set_permissions -p uaa_vhost guest '.*' '.*' '.*'
$ctl set_permissions -p other_vhost guest '.*' '.*' '.*'
# Create queues
ruby declare_queues.rb uaa_vhost/some_queue uaa_vhost/other_queue other_vhost/some_queue other_vhost/other_queue
# Get access tokens
uaac token owner get cf rabbit_super -s "" -p rabbit_super
uaac token owner get cf rabbit_nosuper -s "" -p rabbit_nosuper
echo "Auth info for rabbit_super user
This user will have full access to all rabbitmq vhosts.
Use access_token as a RabbitMQ username"
echo
uaac context rabbit_super
echo
echo "Auth info for rabbit_nosuper user
This user will have read access to uaa_vhost resources,
which name start with some and write access to all uaa_vhost resources.
Use access_token as a RabbitMQ username"
echo
uaac context rabbit_nosuper
echo

View File

@ -0,0 +1,17 @@
[{rabbit,
[{auth_backends, [rabbit_auth_backend_uaa, rabbit_auth_backend_internal]}]},
{rabbitmq_auth_backend_uaa,
[{resource_server_id, <<"rabbitmq">>}]},
{uaa_jwt, [
{default_key, <<"legacy-token-key">>},
{signing_keys,
#{
<<"legacy-token-key">> =>
{map,
#{<<"kty">> => <<"MAC">>,
<<"value">> => <<"rabbit_signing_key">>,
<<"alg">> => <<"HMACSHA256">>}
}
}
}]
}].

View File

@ -0,0 +1,4 @@
jwt:
token:
signing-key: rabbit_signing_key
verification-key: rabbit_signing_key