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
This commit is contained in:
parent
9cc77b94a8
commit
a89f09802d
|
@ -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()
|
||||
|
|
|
@ -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, "web", testTags, testVHosts, testVHostTopics),
|
||||
testAccStepDeleteRole(t, "web"),
|
||||
testAccStepReadRole(t, "web", "", "", ""),
|
||||
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),
|
||||
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,
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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 = `
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -26,17 +26,16 @@ RabbitMQ.
|
|||
|
||||
### Parameters
|
||||
|
||||
- `connection_uri` `(string: <required>)` – Specifies the RabbitMQ connection
|
||||
URI.
|
||||
- `connection_uri` `(string: <required>)` – Specifies the RabbitMQ connection URI.
|
||||
|
||||
- `username` `(string: <required>)` – Specifies the RabbitMQ management
|
||||
administrator username.
|
||||
- `username` `(string: <required>)` – Specifies the RabbitMQ management administrator username.
|
||||
|
||||
- `password` `(string: <required>)` – Specifies the RabbitMQ management
|
||||
administrator password.
|
||||
- `password` `(string: <required>)` – 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
|
||||
|
||||
<Tabs>
|
||||
<Tab heading="cURL">
|
||||
|
||||
```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
|
||||
```
|
||||
</Tab>
|
||||
<Tab heading="CLI">
|
||||
|
||||
```shell-session
|
||||
$ vault write rabbitmq/config/connection \
|
||||
connection_uri="http://localhost:8080" \
|
||||
username="user" \
|
||||
password="password" \
|
||||
password_policy="rabbitmq_policy"
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Configure Lease
|
||||
|
||||
|
@ -83,6 +98,9 @@ This endpoint configures the lease settings for generated credentials.
|
|||
|
||||
### Sample Request
|
||||
|
||||
<Tabs>
|
||||
<Tab heading="cURL">
|
||||
|
||||
```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
|
||||
```
|
||||
</Tab>
|
||||
<Tab heading="CLI">
|
||||
|
||||
```shell-session
|
||||
$ vault write rabbitmq/config/lease \
|
||||
ttl=1800 \
|
||||
max_ttl=3600
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Create Role
|
||||
|
||||
|
@ -124,6 +152,9 @@ This endpoint creates or updates the role definition.
|
|||
|
||||
### Sample Request
|
||||
|
||||
<Tabs>
|
||||
<Tab heading="cURL">
|
||||
|
||||
```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
|
||||
```
|
||||
</Tab>
|
||||
<Tab heading="CLI">
|
||||
|
||||
```shell-session
|
||||
$ vault write rabbitmq/roles/my-role \
|
||||
tags="tag1,tag2" \
|
||||
vhosts="..." \
|
||||
vhost_topics="..."
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Read Role
|
||||
|
||||
|
@ -147,11 +189,22 @@ This endpoint queries the role definition.
|
|||
|
||||
### Sample Request
|
||||
|
||||
<Tabs>
|
||||
<Tab heading="cURL">
|
||||
|
||||
```shell-session
|
||||
$ curl \
|
||||
--header "X-Vault-Token: ..." \
|
||||
http://127.0.0.1:8200/v1/rabbitmq/roles/my-role
|
||||
```
|
||||
</Tab>
|
||||
<Tab heading="CLI">
|
||||
|
||||
```shell-session
|
||||
$ vault read rabbitmq/roles/my-role
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Sample Response
|
||||
|
||||
|
@ -180,12 +233,23 @@ This endpoint deletes the role definition.
|
|||
|
||||
### Sample Request
|
||||
|
||||
<Tabs>
|
||||
<Tab heading="cURL">
|
||||
|
||||
```shell-session
|
||||
$ curl \
|
||||
--header "X-Vault-Token: ..." \
|
||||
--request DELETE \
|
||||
http://127.0.0.1:8200/v1/rabbitmq/roles/my-role
|
||||
```
|
||||
</Tab>
|
||||
<Tab heading="CLI">
|
||||
|
||||
```shell-session
|
||||
vault delete rabbitmq/roles/my-role
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Generate Credentials
|
||||
|
||||
|
@ -203,11 +267,22 @@ role.
|
|||
|
||||
### Sample Request
|
||||
|
||||
<Tabs>
|
||||
<Tab heading="cURL">
|
||||
|
||||
```shell-session
|
||||
$ curl \
|
||||
--header "X-Vault-Token: ..." \
|
||||
http://127.0.0.1:8200/v1/rabbitmq/creds/my-role
|
||||
```
|
||||
</Tab>
|
||||
<Tab heading="CLI">
|
||||
|
||||
```shell-session
|
||||
$ vault read rabbitmq/creds/my-role
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Sample Response
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue