diff --git a/builtin/logical/rabbitmq/backend_test.go b/builtin/logical/rabbitmq/backend_test.go index bbaf2abd9..8adddb3ed 100644 --- a/builtin/logical/rabbitmq/backend_test.go +++ b/builtin/logical/rabbitmq/backend_test.go @@ -58,7 +58,7 @@ func testAccPreCheck(t *testing.T) { func testAccStepConfig(t *testing.T) logicaltest.TestStep { return logicaltest.TestStep{ - Operation: logical.WriteOperation, + Operation: logical.UpdateOperation, Path: "config/connection", Data: map[string]interface{}{ "uri": os.Getenv("RABBITMQ_MG_URI"), @@ -70,7 +70,7 @@ func testAccStepConfig(t *testing.T) logicaltest.TestStep { func testAccStepRole(t *testing.T) logicaltest.TestStep { return logicaltest.TestStep{ - Operation: logical.WriteOperation, + Operation: logical.UpdateOperation, Path: "roles/web", Data: map[string]interface{}{ "tags": "administrator", diff --git a/builtin/logical/rabbitmq/path_config_connection.go b/builtin/logical/rabbitmq/path_config_connection.go index bf8e087cb..19bc8edc7 100644 --- a/builtin/logical/rabbitmq/path_config_connection.go +++ b/builtin/logical/rabbitmq/path_config_connection.go @@ -27,7 +27,7 @@ func pathConfigConnection(b *backend) *framework.Path { }, Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.WriteOperation: b.pathConnectionWrite, + logical.UpdateOperation: b.pathConnectionWrite, }, HelpSynopsis: pathConfigConnectionHelpSyn, diff --git a/builtin/logical/rabbitmq/path_config_lease.go b/builtin/logical/rabbitmq/path_config_lease.go index ec3bb22f0..328430e13 100644 --- a/builtin/logical/rabbitmq/path_config_lease.go +++ b/builtin/logical/rabbitmq/path_config_lease.go @@ -25,7 +25,7 @@ func pathConfigLease(b *backend) *framework.Path { Callbacks: map[logical.Operation]framework.OperationFunc{ logical.ReadOperation: b.pathLeaseRead, - logical.WriteOperation: b.pathLeaseWrite, + logical.UpdateOperation: b.pathLeaseWrite, }, HelpSynopsis: pathConfigLeaseHelpSyn, diff --git a/builtin/logical/rabbitmq/path_role_create.go b/builtin/logical/rabbitmq/path_role_create.go index 23e2deddb..57f52181b 100644 --- a/builtin/logical/rabbitmq/path_role_create.go +++ b/builtin/logical/rabbitmq/path_role_create.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "github.com/hashicorp/uuid" + "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" "github.com/michaelklishin/rabbit-hole" @@ -51,16 +51,18 @@ func (b *backend) pathRoleCreateRead( lease = &configLease{Lease: 1 * time.Hour} } - displayName := req.DisplayName - if len(displayName) > 26 { - displayName = displayName[:26] + userUUID, err := uuid.GenerateUUID() + if err != nil { + return nil, err } - username := fmt.Sprintf("%s-%s", displayName, uuid.GenerateUUID()) - // Generate the username, password and expiration. Limit user to 63 characters + username := fmt.Sprintf("%s-%s", displayName, userUUID) if len(username) > 63 { username = username[:63] } - password := uuid.GenerateUUID() + password, err := uuid.GenerateUUID() + if err != nil { + return nil, err + } // Get our connection client, err := b.Client(req.Storage) diff --git a/builtin/logical/rabbitmq/path_roles.go b/builtin/logical/rabbitmq/path_roles.go index ce3b65a33..fc829dff2 100644 --- a/builtin/logical/rabbitmq/path_roles.go +++ b/builtin/logical/rabbitmq/path_roles.go @@ -24,13 +24,13 @@ func pathRoles(b *backend) *framework.Path { "vhosts": &framework.FieldSchema{ Type: framework.TypeString, - Description: "A map of virtualhosts to permissions.", + Description: "A map of virtual hosts to permissions.", }, }, Callbacks: map[logical.Operation]framework.OperationFunc{ logical.ReadOperation: b.pathRoleRead, - logical.WriteOperation: b.pathRoleCreate, + logical.UpdateOperation: b.pathRoleCreate, logical.DeleteOperation: b.pathRoleDelete, }, diff --git a/builtin/logical/rabbitmq/secret_creds.go b/builtin/logical/rabbitmq/secret_creds.go index 151ef6978..94aa41f0c 100644 --- a/builtin/logical/rabbitmq/secret_creds.go +++ b/builtin/logical/rabbitmq/secret_creds.go @@ -26,9 +26,6 @@ func secretCreds(b *backend) *framework.Secret { }, }, - DefaultDuration: 1 * time.Hour, - DefaultGracePeriod: 10 * time.Minute, - Renew: b.secretCredsRenew, Revoke: b.secretCredsRevoke, } @@ -45,7 +42,7 @@ func (b *backend) secretCredsRenew( lease = &configLease{Lease: 1 * time.Hour} } - f := framework.LeaseExtend(lease.Lease, lease.LeaseMax, false) + f := framework.LeaseExtend(lease.Lease, lease.LeaseMax, b.System()) resp, err := f(req, d) if err != nil { return nil, err diff --git a/website/source/docs/secrets/rabbitmq/index.html.md b/website/source/docs/secrets/rabbitmq/index.html.md new file mode 100644 index 000000000..ccf70bee3 --- /dev/null +++ b/website/source/docs/secrets/rabbitmq/index.html.md @@ -0,0 +1,326 @@ +--- +layout: "docs" +page_title: "Secret Backend: RabbitMQ" +sidebar_current: "docs-secrets-rabbitmq" +description: |- + The RabbitMQ secret backend for Vault generates user credentials to access RabbitMQ. +--- + +# RabbitMQ Secret Backend + +Name: `rabbitmq` + +The RabbitMQ secret backend for Vault generates user credentials +dynamically based on configured permissions and virtual hosts. This means that +services that need to access a virtual host no longer need to hardcode credentials: +they can request them from Vault, and use Vault's leasing mechanism to more easily roll users. + +Additionally, it introduces a new ability: with every service accessing +the messaging queue with unique credentials, it makes auditing much easier when +questionable data access is discovered: you can track it down to the specific +instance of a service based on the RabbitMQ username. + +// TODO: Fix this +Vault makes use both of its own internal revocation system as well as the +deleting RabbitMQ users when creating RabbitMQ users to ensure that users +become invalid within a reasonable time of the lease expiring. + +This page will show a quick start for this backend. For detailed documentation +on every path, use `vault path-help` after mounting the backend. + +## Quick Start + +The first step to using the PostgreSQL backend is to mount it. +Unlike the `generic` backend, the `rabbitmq` backend is not mounted by default. + +```text +$ vault mount rabbitmq +Successfully mounted 'rabbitmq' at 'rabbitmq'! +``` + +Next, Vault must be configured to connect to the RabbitMQ. This is done by +writing the RabbitMQ management URI, RabbitMQ management administrator user, and +the user's password. + +```text +$ vault write rabbitmq/config/connection \ + uri="http://localhost:15672" \ + username="admin" \ + password="password" +``` + +In this case, we've configured Vault with the URI "http://localhost:15672", user "admin", +and password "password" connecting to a local RabbitMQ management instance. It is important +that the Vault user have the administrator privilege to manager users. + +Optionally, we can configure the lease settings for credentials generated +by Vault. This is done by writing to the `config/lease` key: + +``` +$ vault write postgresql/config/lease lease=1h lease_max=24h +Success! Data written to: postgresql/config/lease +``` + +This restricts each credential to being valid or leased for 1 hour +at a time, with a maximum use period of 24 hours. This forces an +application to renew their credentials at least hourly, and to recycle +them once per day. + +The next step is to configure a role. A role is a logical name that maps +to tags and virtual host permissions used to generated those credentials. For example, +lets create a "readwrite" virtual host role: + +```text +$ vault write rabbitmq/roles/readwrite \ + vhosts='{"/":{"write": ".*", "read": ".*"}}' +Success! Data written to: rabbitmq/roles/readonly +``` + +By writing to the `roles/readwrite` path we are defining the `readwrite` role. +This role will be created by evaluating the given `vhosts` and `tags` statements. +By default, no tags and no virtual hosts are assigned to a role. You can read more +about RabbitMQ management tags [here](https://www.rabbitmq.com/management.html#permissions). +Configure, write, and read permissions are granted per virtual host. + +To generate a new set of credentials, we simply read from that role: +Vault is now configured to create and manage credentials for RabbitMQ! + +```text +$ vault read rabbitmq/creds/readwrite +lease_id rabbitmq/creds/readwrite/2740df96-d1c2-7140-c406-77a137fa3ecf +lease_duration 3600 +lease_renewable true +password e1b6c159-ca63-4c6a-3886-6639eae06c30 +username root-4b95bf47-281d-dcb5-8a60-9594f8056092 +``` + +By reading from the `creds/readwrite` path, Vault has generated a new +set of credentials using the `readwrite` role configuration. Here we +see the dynamically generated username and password, along with a one +hour lease. + +Using ACLs, it is possible to restrict using the rabbitmq backend such +that trusted operators can manage the role definitions, and both +users and applications are restricted in the credentials they are +allowed to read. + +If you get stuck at any time, simply run `vault path-help rabbitmq` or with a +subpath for interactive help output. + +## API + +### /rabbitmq/config/connection +#### POST + +
+
Description
+
+ Configures the connection string used to communicate with RabbitMQ. + This is a root protected endpoint. +
+ +
Method
+
POST
+ +
URL
+
`/RabbitMQ/config/connection`
+ +
Parameters
+
+ +
+ +
Returns
+
+ A `204` response code. +
+
+ +### /postgresql/config/lease +#### POST + +
+
Description
+
+ Configures the lease settings for generated credentials. + If not configured, leases default to 1 hour. This is a root + protected endpoint. +
+ +
Method
+
POST
+ +
URL
+
`/rabbitmq/config/lease`
+ +
Parameters
+
+ +
+ +
Returns
+
+ A `204` response code. +
+
+ +### /rabbitmq/roles/ +#### POST + +
+
Description
+
+ Creates or updates the role definition. +
+ +
Method
+
POST
+ +
URL
+
`/rabbitmq/roles/`
+ +
Parameters
+
+ +
+ +
Returns
+
+ A `204` response code. +
+
+ +#### GET + +
+
Description
+
+ Queries the role definition. +
+ +
Method
+
GET
+ +
URL
+
`/rabbitmq/roles/`
+ +
Parameters
+
+ None +
+ +
Returns
+
+ + ```javascript + { + "data": { + "tags": "", + "vhost": "{\"/\": {\"configure\:".*", \"write\:".*", \"read\": \".*\"}}" + } + } + ``` + +
+
+ + +#### DELETE + +
+
Description
+
+ Deletes the role definition. +
+ +
Method
+
DELETE
+ +
URL
+
`/rabbitmq/roles/`
+ +
Parameters
+
+ None +
+ +
Returns
+
+ A `204` response code. +
+
+ +### /rabbitmq/creds/ +#### GET + +
+
Description
+
+ Generates a new set of dynamic credentials based on the named role. +
+ +
Method
+
GET
+ +
URL
+
`/postgresql/creds/`
+ +
Parameters
+
+ None +
+ +
Returns
+
+ + ```javascript + { + "data": { + "username": "root-4b95bf47-281d-dcb5-8a60-9594f8056092", + "password": "e1b6c159-ca63-4c6a-3886-6639eae06c30" + } + } + ``` + +
+