From 1b7523abd618597526e8b7bdca96d0fac73e4782 Mon Sep 17 00:00:00 2001 From: Michael Golowka <72365+pcman312@users.noreply.github.com> Date: Thu, 11 Jun 2020 16:08:20 -0600 Subject: [PATCH] Integrate password policies into RabbitMQ secret engine (#9143) * Add password policies to RabbitMQ & update docs * Also updates some parts of the password policies to aid/fix testing --- builtin/logical/rabbitmq/backend.go | 11 +-- builtin/logical/rabbitmq/backend_test.go | 56 ++++++++--- builtin/logical/rabbitmq/passwords.go | 14 +++ .../rabbitmq/path_config_connection.go | 55 +++++++++-- builtin/logical/rabbitmq/path_role_create.go | 7 +- sdk/helper/random/string_generator.go | 2 +- sdk/helper/random/string_generator_test.go | 67 ++++++------- sdk/logical/system_view.go | 13 +++ vault/logical_system_test.go | 2 +- .../sdk/helper/random/string_generator.go | 2 +- .../vault/sdk/logical/system_view.go | 13 +++ .../pages/api-docs/secret/rabbitmq/index.mdx | 93 +++++++++++++++++-- website/pages/docs/secrets/rabbitmq/index.mdx | 6 +- 13 files changed, 262 insertions(+), 79 deletions(-) create mode 100644 builtin/logical/rabbitmq/passwords.go diff --git a/builtin/logical/rabbitmq/backend.go b/builtin/logical/rabbitmq/backend.go index 95c99919e562..b813d128f5f6 100644 --- a/builtin/logical/rabbitmq/backend.go +++ b/builtin/logical/rabbitmq/backend.go @@ -2,7 +2,6 @@ package rabbitmq import ( "context" - "fmt" "strings" "sync" @@ -73,18 +72,10 @@ func (b *backend) Client(ctx context.Context, s logical.Storage) (*rabbithole.Cl b.lock.RUnlock() // Otherwise, attempt to make connection - entry, err := s.Get(ctx, "config/connection") + connConfig, err := readConfig(ctx, s) if err != nil { return nil, err } - if entry == nil { - return nil, fmt.Errorf("configure the client connection with config/connection first") - } - - var connConfig connectionConfig - if err := entry.DecodeJSON(&connConfig); err != nil { - return nil, err - } b.lock.Lock() defer b.lock.Unlock() diff --git a/builtin/logical/rabbitmq/backend_test.go b/builtin/logical/rabbitmq/backend_test.go index 552f5ca704b6..2b45945d2dd8 100644 --- a/builtin/logical/rabbitmq/backend_test.go +++ b/builtin/logical/rabbitmq/backend_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/vault/helper/testhelpers/docker" logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical" "github.com/hashicorp/vault/sdk/helper/jsonutil" + "github.com/hashicorp/vault/sdk/helper/random" "github.com/hashicorp/vault/sdk/logical" rabbithole "github.com/michaelklishin/rabbit-hole" "github.com/mitchellh/mapstructure" @@ -27,6 +28,8 @@ const ( testTags = "administrator" testVHosts = `{"/": {"configure": ".*", "write": ".*", "read": ".*"}}` testVHostTopics = `{"/": {"amq.topic": {"write": ".*", "read": ".*"}}}` + + roleName = "web" ) func prepareRabbitMQTestContainer(t *testing.T) (func(), string, int) { @@ -89,9 +92,9 @@ func TestBackend_basic(t *testing.T) { PreCheck: testAccPreCheckFunc(t, uri), LogicalBackend: b, Steps: []logicaltest.TestStep{ - testAccStepConfig(t, uri), + testAccStepConfig(t, uri, ""), testAccStepRole(t), - testAccStepReadCreds(t, b, uri, "web"), + testAccStepReadCreds(t, b, uri, roleName), }, }) @@ -111,10 +114,10 @@ func TestBackend_returnsErrs(t *testing.T) { PreCheck: testAccPreCheckFunc(t, uri), LogicalBackend: b, Steps: []logicaltest.TestStep{ - testAccStepConfig(t, uri), + testAccStepConfig(t, uri, ""), { Operation: logical.CreateOperation, - Path: "roles/web", + Path: fmt.Sprintf("roles/%s", roleName), Data: map[string]interface{}{ "tags": testTags, "vhosts": `{"invalid":{"write": ".*", "read": ".*"}}`, @@ -123,7 +126,7 @@ func TestBackend_returnsErrs(t *testing.T) { }, { Operation: logical.ReadOperation, - Path: "creds/web", + Path: fmt.Sprintf("creds/%s", roleName), ErrorOk: true, }, }, @@ -144,11 +147,35 @@ func TestBackend_roleCrud(t *testing.T) { PreCheck: testAccPreCheckFunc(t, uri), LogicalBackend: b, Steps: []logicaltest.TestStep{ - testAccStepConfig(t, uri), + testAccStepConfig(t, uri, ""), + testAccStepRole(t), + testAccStepReadRole(t, roleName, testTags, testVHosts, testVHostTopics), + testAccStepDeleteRole(t, roleName), + testAccStepReadRole(t, roleName, "", "", ""), + }, + }) +} + +func TestBackend_roleWithPasswordPolicy(t *testing.T) { + if os.Getenv(logicaltest.TestEnvVar) == "" { + t.Skip(fmt.Sprintf("Acceptance tests skipped unless env '%s' set", logicaltest.TestEnvVar)) + return + } + + backendConfig := logical.TestBackendConfig() + backendConfig.System.(*logical.StaticSystemView).SetPasswordPolicy("testpolicy", random.DefaultStringGenerator) + b, _ := Factory(context.Background(), backendConfig) + + cleanup, uri, _ := prepareRabbitMQTestContainer(t) + defer cleanup() + + logicaltest.Test(t, logicaltest.TestCase{ + PreCheck: testAccPreCheckFunc(t, uri), + LogicalBackend: b, + Steps: []logicaltest.TestStep{ + testAccStepConfig(t, uri, "testpolicy"), testAccStepRole(t), - testAccStepReadRole(t, "web", testTags, testVHosts, testVHostTopics), - testAccStepDeleteRole(t, "web"), - testAccStepReadRole(t, "web", "", "", ""), + testAccStepReadCreds(t, b, uri, roleName), }, }) } @@ -161,7 +188,7 @@ func testAccPreCheckFunc(t *testing.T, uri string) func() { } } -func testAccStepConfig(t *testing.T, uri string) logicaltest.TestStep { +func testAccStepConfig(t *testing.T, uri string, passwordPolicy string) logicaltest.TestStep { username := os.Getenv(envRabbitMQUsername) if len(username) == 0 { username = "guest" @@ -175,9 +202,10 @@ func testAccStepConfig(t *testing.T, uri string) logicaltest.TestStep { Operation: logical.UpdateOperation, Path: "config/connection", Data: map[string]interface{}{ - "connection_uri": uri, - "username": username, - "password": password, + "connection_uri": uri, + "username": username, + "password": password, + "password_policy": passwordPolicy, }, } } @@ -185,7 +213,7 @@ func testAccStepConfig(t *testing.T, uri string) logicaltest.TestStep { func testAccStepRole(t *testing.T) logicaltest.TestStep { return logicaltest.TestStep{ Operation: logical.UpdateOperation, - Path: "roles/web", + Path: fmt.Sprintf("roles/%s", roleName), Data: map[string]interface{}{ "tags": testTags, "vhosts": testVHosts, diff --git a/builtin/logical/rabbitmq/passwords.go b/builtin/logical/rabbitmq/passwords.go new file mode 100644 index 000000000000..cb660bc5c29b --- /dev/null +++ b/builtin/logical/rabbitmq/passwords.go @@ -0,0 +1,14 @@ +package rabbitmq + +import ( + "context" + + "github.com/hashicorp/vault/sdk/helper/base62" +) + +func (b *backend) generatePassword(ctx context.Context, policyName string) (password string, err error) { + if policyName != "" { + return b.System().GeneratePasswordFromPolicy(ctx, policyName) + } + return base62.Random(36) +} diff --git a/builtin/logical/rabbitmq/path_config_connection.go b/builtin/logical/rabbitmq/path_config_connection.go index 11536aad0537..cd41b73c8a4f 100644 --- a/builtin/logical/rabbitmq/path_config_connection.go +++ b/builtin/logical/rabbitmq/path_config_connection.go @@ -9,6 +9,10 @@ import ( rabbithole "github.com/michaelklishin/rabbit-hole" ) +const ( + storageKey = "config/connection" +) + func pathConfigConnection(b *backend) *framework.Path { return &framework.Path{ Pattern: "config/connection", @@ -30,6 +34,10 @@ func pathConfigConnection(b *backend) *framework.Path { Default: true, Description: `If set, connection_uri is verified by actually connecting to the RabbitMQ management API`, }, + "password_policy": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Name of the password policy to use to generate passwords for dynamic credentials.", + }, }, Callbacks: map[logical.Operation]framework.OperationFunc{ @@ -57,6 +65,8 @@ func (b *backend) pathConnectionUpdate(ctx context.Context, req *logical.Request return logical.ErrorResponse("missing password"), nil } + passwordPolicy := data.Get("password_policy").(string) + // Don't check the connection_url if verification is disabled verifyConnection := data.Get("verify_connection").(bool) if verifyConnection { @@ -73,15 +83,14 @@ func (b *backend) pathConnectionUpdate(ctx context.Context, req *logical.Request } // Store it - entry, err := logical.StorageEntryJSON("config/connection", connectionConfig{ - URI: uri, - Username: username, - Password: password, - }) - if err != nil { - return nil, err + config := connectionConfig{ + URI: uri, + Username: username, + Password: password, + PasswordPolicy: passwordPolicy, } - if err := req.Storage.Put(ctx, entry); err != nil { + err := writeConfig(ctx, req.Storage, config) + if err != nil { return nil, err } @@ -91,6 +100,33 @@ func (b *backend) pathConnectionUpdate(ctx context.Context, req *logical.Request return nil, nil } +func readConfig(ctx context.Context, storage logical.Storage) (connectionConfig, error) { + entry, err := storage.Get(ctx, storageKey) + if err != nil { + return connectionConfig{}, err + } + if entry == nil { + return connectionConfig{}, nil + } + + var connConfig connectionConfig + if err := entry.DecodeJSON(&connConfig); err != nil { + return connectionConfig{}, err + } + return connConfig, nil +} + +func writeConfig(ctx context.Context, storage logical.Storage, config connectionConfig) error { + entry, err := logical.StorageEntryJSON(storageKey, config) + if err != nil { + return err + } + if err := storage.Put(ctx, entry); err != nil { + return err + } + return nil +} + // connectionConfig contains the information required to make a connection to a RabbitMQ node type connectionConfig struct { // URI of the RabbitMQ server @@ -101,6 +137,9 @@ type connectionConfig struct { // Password for the Username Password string `json:"password"` + + // PasswordPolicy for generating passwords for dynamic credentials + PasswordPolicy string `json:"password_policy"` } const pathConfigConnectionHelpSyn = ` diff --git a/builtin/logical/rabbitmq/path_role_create.go b/builtin/logical/rabbitmq/path_role_create.go index 63c09f6d6d4e..77f30d2dc6d8 100644 --- a/builtin/logical/rabbitmq/path_role_create.go +++ b/builtin/logical/rabbitmq/path_role_create.go @@ -53,7 +53,12 @@ func (b *backend) pathCredsRead(ctx context.Context, req *logical.Request, d *fr } username := fmt.Sprintf("%s-%s", req.DisplayName, uuidVal) - password, err := uuid.GenerateUUID() + config, err := readConfig(ctx, req.Storage) + if err != nil { + return nil, fmt.Errorf("unable to read configuration: %w", err) + } + + password, err := b.generatePassword(ctx, config.PasswordPolicy) if err != nil { return nil, err } diff --git a/sdk/helper/random/string_generator.go b/sdk/helper/random/string_generator.go index 761577455a22..621930eb66f2 100644 --- a/sdk/helper/random/string_generator.go +++ b/sdk/helper/random/string_generator.go @@ -37,7 +37,7 @@ var ( AlphaNumericFullSymbolRuneset = []rune(AlphaNumericFullSymbolCharset) // DefaultStringGenerator has reasonable default rules for generating strings - DefaultStringGenerator = StringGenerator{ + DefaultStringGenerator = &StringGenerator{ Length: 20, Rules: []Rule{ CharsetRule{ diff --git a/sdk/helper/random/string_generator_test.go b/sdk/helper/random/string_generator_test.go index 55b252a4f9eb..af4e7da14962 100644 --- a/sdk/helper/random/string_generator_test.go +++ b/sdk/helper/random/string_generator_test.go @@ -106,7 +106,7 @@ func TestStringGenerator_Generate_errors(t *testing.T) { type testCase struct { timeout time.Duration generator *StringGenerator - rng io.Reader + rng io.Reader } tests := map[string]testCase{ @@ -121,7 +121,7 @@ func TestStringGenerator_Generate_errors(t *testing.T) { }, charset: AlphaNumericShortSymbolRuneset, }, - rng: rand.Reader, + rng: rand.Reader, }, "impossible rules": { timeout: 10 * time.Millisecond, // Keep this short so the test doesn't take too long @@ -134,7 +134,7 @@ func TestStringGenerator_Generate_errors(t *testing.T) { }, charset: AlphaNumericShortSymbolRuneset, }, - rng: rand.Reader, + rng: rand.Reader, }, "bad RNG reader": { timeout: 10 * time.Millisecond, // Keep this short so the test doesn't take too long @@ -143,7 +143,7 @@ func TestStringGenerator_Generate_errors(t *testing.T) { Rules: []Rule{}, charset: AlphaNumericShortSymbolRuneset, }, - rng: badReader{}, + rng: badReader{}, }, "0 length": { timeout: 10 * time.Millisecond, @@ -157,7 +157,7 @@ func TestStringGenerator_Generate_errors(t *testing.T) { }, charset: []rune("abcde"), }, - rng: rand.Reader, + rng: rand.Reader, }, "-1 length": { timeout: 10 * time.Millisecond, @@ -171,7 +171,7 @@ func TestStringGenerator_Generate_errors(t *testing.T) { }, charset: []rune("abcde"), }, - rng: rand.Reader, + rng: rand.Reader, }, "no charset": { timeout: 10 * time.Millisecond, @@ -179,7 +179,7 @@ func TestStringGenerator_Generate_errors(t *testing.T) { Length: 20, Rules: []Rule{}, }, - rng: rand.Reader, + rng: rand.Reader, }, } @@ -333,8 +333,8 @@ func TestRandomRunes_errors(t *testing.T) { "šŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſ℀℁ℂ℃℄℅℆ℇ℈℉ℊℋℌℍℎℏℐℑℒℓ℔ℕ№℗℘ℙℚℛℜℝ℞℟℠" + "Σ", ), - length:20, - rng: rand.Reader, + length: 20, + rng: rand.Reader, }, "length is zero": { charset: []rune("abcde"), @@ -372,22 +372,24 @@ func BenchmarkStringGenerator_Generate(b *testing.B) { } type testCase struct { - generator StringGenerator + generator *StringGenerator } benches := map[string]testCase{ - "no rules": { - generator: StringGenerator{ - charset: AlphaNumericFullSymbolRuneset, - Rules: []Rule{}, + "no restrictions": { + generator: &StringGenerator{ + Rules: []Rule{ + CharsetRule{ + Charset: AlphaNumericFullSymbolRuneset, + }, + }, }, }, "default generator": { generator: DefaultStringGenerator, }, "large symbol set": { - generator: StringGenerator{ - charset: AlphaNumericFullSymbolRuneset, + generator: &StringGenerator{ Rules: []Rule{ CharsetRule{ Charset: LowercaseRuneset, @@ -409,13 +411,14 @@ func BenchmarkStringGenerator_Generate(b *testing.B) { }, }, "max symbol set": { - generator: StringGenerator{ - charset: []rune(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + - "`abcdefghijklmnopqrstuvwxyz{|}~ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠ" + - "ġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠ" + - "šŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſ℀℁ℂ℃℄℅℆ℇ℈℉ℊℋℌℍℎℏℐℑℒℓ℔ℕ№℗℘ℙℚℛℜℝ℞℟℠", - ), + generator: &StringGenerator{ Rules: []Rule{ + CharsetRule{ + Charset: []rune(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + + "`abcdefghijklmnopqrstuvwxyz{|}~ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠ" + + "ġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠ" + + "šŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſ℀℁ℂ℃℄℅℆ℇ℈℉ℊℋℌℍℎℏℐℑℒℓ℔ℕ№℗℘ℙℚℛℜℝ℞℟℠"), + }, CharsetRule{ Charset: LowercaseRuneset, MinChars: 1, @@ -432,9 +435,11 @@ func BenchmarkStringGenerator_Generate(b *testing.B) { }, }, "restrictive charset rules": { - generator: StringGenerator{ - charset: AlphaNumericShortSymbolRuneset, + generator: &StringGenerator{ Rules: []Rule{ + CharsetRule{ + Charset: AlphaNumericShortSymbolRuneset, + }, CharsetRule{ Charset: []rune("A"), MinChars: 1, @@ -551,7 +556,7 @@ func (badReader) Read([]byte) (int, error) { func TestValidate(t *testing.T) { type testCase struct { - generator StringGenerator + generator *StringGenerator expectErr bool } @@ -561,33 +566,33 @@ func TestValidate(t *testing.T) { expectErr: false, }, "length is 0": { - generator: StringGenerator{ + generator: &StringGenerator{ Length: 0, }, expectErr: true, }, "length is negative": { - generator: StringGenerator{ + generator: &StringGenerator{ Length: -2, }, expectErr: true, }, "nil charset, no rules": { - generator: StringGenerator{ + generator: &StringGenerator{ Length: 5, charset: nil, }, expectErr: true, }, "zero length charset, no rules": { - generator: StringGenerator{ + generator: &StringGenerator{ Length: 5, charset: []rune{}, }, expectErr: true, }, "rules require password longer than length": { - generator: StringGenerator{ + generator: &StringGenerator{ Length: 5, charset: []rune("abcde"), Rules: []Rule{ @@ -600,7 +605,7 @@ func TestValidate(t *testing.T) { expectErr: true, }, "charset has non-printable characters": { - generator: StringGenerator{ + generator: &StringGenerator{ Length: 0, charset: []rune{ 'a', diff --git a/sdk/logical/system_view.go b/sdk/logical/system_view.go index 41f82f36c329..b68e1bbc29b8 100644 --- a/sdk/logical/system_view.go +++ b/sdk/logical/system_view.go @@ -194,3 +194,16 @@ func (d StaticSystemView) GeneratePasswordFromPolicy(ctx context.Context, policy } return policy.Generate(ctx, nil) } + +func (d *StaticSystemView) SetPasswordPolicy(name string, policy PasswordPolicy) { + if d.PasswordPolicies == nil { + d.PasswordPolicies = map[string]PasswordPolicy{} + } + d.PasswordPolicies[name] = policy +} + +func (d *StaticSystemView) DeletePasswordPolicy(name string) (existed bool) { + _, existed = d.PasswordPolicies[name] + delete(d.PasswordPolicies, name) + return existed +} diff --git a/vault/logical_system_test.go b/vault/logical_system_test.go index 7f13ba1f68b9..1ff14558e75d 100644 --- a/vault/logical_system_test.go +++ b/vault/logical_system_test.go @@ -3188,7 +3188,7 @@ func TestHandlePoliciesPasswordGenerate(t *testing.T) { }) t.Run("success", func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() policyEntry := storageEntry(t, "testpolicy", diff --git a/vendor/github.com/hashicorp/vault/sdk/helper/random/string_generator.go b/vendor/github.com/hashicorp/vault/sdk/helper/random/string_generator.go index 761577455a22..621930eb66f2 100644 --- a/vendor/github.com/hashicorp/vault/sdk/helper/random/string_generator.go +++ b/vendor/github.com/hashicorp/vault/sdk/helper/random/string_generator.go @@ -37,7 +37,7 @@ var ( AlphaNumericFullSymbolRuneset = []rune(AlphaNumericFullSymbolCharset) // DefaultStringGenerator has reasonable default rules for generating strings - DefaultStringGenerator = StringGenerator{ + DefaultStringGenerator = &StringGenerator{ Length: 20, Rules: []Rule{ CharsetRule{ diff --git a/vendor/github.com/hashicorp/vault/sdk/logical/system_view.go b/vendor/github.com/hashicorp/vault/sdk/logical/system_view.go index 41f82f36c329..b68e1bbc29b8 100644 --- a/vendor/github.com/hashicorp/vault/sdk/logical/system_view.go +++ b/vendor/github.com/hashicorp/vault/sdk/logical/system_view.go @@ -194,3 +194,16 @@ func (d StaticSystemView) GeneratePasswordFromPolicy(ctx context.Context, policy } return policy.Generate(ctx, nil) } + +func (d *StaticSystemView) SetPasswordPolicy(name string, policy PasswordPolicy) { + if d.PasswordPolicies == nil { + d.PasswordPolicies = map[string]PasswordPolicy{} + } + d.PasswordPolicies[name] = policy +} + +func (d *StaticSystemView) DeletePasswordPolicy(name string) (existed bool) { + _, existed = d.PasswordPolicies[name] + delete(d.PasswordPolicies, name) + return existed +} diff --git a/website/pages/api-docs/secret/rabbitmq/index.mdx b/website/pages/api-docs/secret/rabbitmq/index.mdx index d754c666ee1d..c9ab5cc0579f 100644 --- a/website/pages/api-docs/secret/rabbitmq/index.mdx +++ b/website/pages/api-docs/secret/rabbitmq/index.mdx @@ -26,17 +26,16 @@ RabbitMQ. ### Parameters -- `connection_uri` `(string: )` – Specifies the RabbitMQ connection - URI. +- `connection_uri` `(string: )` – Specifies the RabbitMQ connection URI. -- `username` `(string: )` – Specifies the RabbitMQ management - administrator username. +- `username` `(string: )` – Specifies the RabbitMQ management administrator username. -- `password` `(string: )` – Specifies the RabbitMQ management - administrator password. +- `password` `(string: )` – Specifies the RabbitMQ management administrator password. -- `verify_connection` `(bool: true)` – Specifies whether to verify connection - URI, username, and password. +- `verify_connection` `(bool: true)` – Specifies whether to verify connection URI, username, and password. + +- `password_policy` `(string: "")` - Specifies a [password policy](/docs/concepts/password-policies) to + use when creating dynamic credentials. Defaults to generating an alphanumeric password if not set. ### Sample Payload @@ -44,12 +43,16 @@ RabbitMQ. { "connection_uri": "https://...", "username": "user", - "password": "password" + "password": "password", + "password_policy": "rabbitmq_policy" } ``` ### Sample Request + + + ```shell-session $ curl \ --header "X-Vault-Token: ..." \ @@ -57,6 +60,18 @@ $ curl \ --data @payload.json \ http://127.0.0.1:8200/v1/rabbitmq/config/connection ``` + + + +```shell-session +$ vault write rabbitmq/config/connection \ + connection_uri="http://localhost:8080" \ + username="user" \ + password="password" \ + password_policy="rabbitmq_policy" +``` + + ## Configure Lease @@ -83,6 +98,9 @@ This endpoint configures the lease settings for generated credentials. ### Sample Request + + + ```shell-session $ curl \ --header "X-Vault-Token: ..." \ @@ -90,6 +108,16 @@ $ curl \ --data @payload.json \ http://127.0.0.1:8200/v1/rabbitmq/config/lease ``` + + + +```shell-session +$ vault write rabbitmq/config/lease \ + ttl=1800 \ + max_ttl=3600 +``` + + ## Create Role @@ -124,6 +152,9 @@ This endpoint creates or updates the role definition. ### Sample Request + + + ```shell-session $ curl \ --header "X-Vault-Token: ..." \ @@ -131,6 +162,17 @@ $ curl \ --data @payload.json \ http://127.0.0.1:8200/v1/rabbitmq/roles/my-role ``` + + + +```shell-session +$ vault write rabbitmq/roles/my-role \ + tags="tag1,tag2" \ + vhosts="..." \ + vhost_topics="..." +``` + + ## Read Role @@ -147,11 +189,22 @@ This endpoint queries the role definition. ### Sample Request + + + ```shell-session $ curl \ --header "X-Vault-Token: ..." \ http://127.0.0.1:8200/v1/rabbitmq/roles/my-role ``` + + + +```shell-session +$ vault read rabbitmq/roles/my-role +``` + + ### Sample Response @@ -180,12 +233,23 @@ This endpoint deletes the role definition. ### Sample Request + + + ```shell-session $ curl \ --header "X-Vault-Token: ..." \ --request DELETE \ http://127.0.0.1:8200/v1/rabbitmq/roles/my-role ``` + + + +```shell-session +vault delete rabbitmq/roles/my-role +``` + + ## Generate Credentials @@ -203,11 +267,22 @@ role. ### Sample Request + + + ```shell-session $ curl \ --header "X-Vault-Token: ..." \ http://127.0.0.1:8200/v1/rabbitmq/creds/my-role ``` + + + +```shell-session +$ vault read rabbitmq/creds/my-role +``` + + ### Sample Response diff --git a/website/pages/docs/secrets/rabbitmq/index.mdx b/website/pages/docs/secrets/rabbitmq/index.mdx index 007c9ec7d008..f8c0b0fd8458 100644 --- a/website/pages/docs/secrets/rabbitmq/index.mdx +++ b/website/pages/docs/secrets/rabbitmq/index.mdx @@ -81,11 +81,11 @@ the proper permission, it can generate credentials. $ vault read rabbitmq/creds/my-role Key Value --- ----- - lease_id rabbitmq/creds/my-role/37d70d04-f24d-760a-e06e-b9b21087f0f4 + lease_id rabbitmq/creds/my-role/I39Hu8XXOombof4wiK5bKMn9 lease_duration 768h lease_renewable true - password a98af72b-b6c9-b4b1-fe37-c73a572befed - username token-590f1fe2-1094-a4d6-01a7-9d4ff756a085 + password 3yNDBikgQvrkx2VA2zhq5IdSM7IWk1RyMYJr + username root-39669250-3894-8032-c420-3d58483ebfc4 ``` Using ACLs, it is possible to restrict using the rabbitmq secrets engine