mirror of
https://github.com/hashicorp/vault.git
synced 2025-11-16 08:11:20 +01:00
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
8cabb11b48
commit
b2441efd37
@ -2,7 +2,6 @@ package rabbitmq
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@ -73,18 +72,10 @@ func (b *backend) Client(ctx context.Context, s logical.Storage) (*rabbithole.Cl
|
|||||||
b.lock.RUnlock()
|
b.lock.RUnlock()
|
||||||
|
|
||||||
// Otherwise, attempt to make connection
|
// Otherwise, attempt to make connection
|
||||||
entry, err := s.Get(ctx, "config/connection")
|
connConfig, err := readConfig(ctx, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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()
|
b.lock.Lock()
|
||||||
defer b.lock.Unlock()
|
defer b.lock.Unlock()
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/hashicorp/vault/helper/testhelpers/docker"
|
"github.com/hashicorp/vault/helper/testhelpers/docker"
|
||||||
logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical"
|
logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical"
|
||||||
"github.com/hashicorp/vault/sdk/helper/jsonutil"
|
"github.com/hashicorp/vault/sdk/helper/jsonutil"
|
||||||
|
"github.com/hashicorp/vault/sdk/helper/random"
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
rabbithole "github.com/michaelklishin/rabbit-hole"
|
rabbithole "github.com/michaelklishin/rabbit-hole"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
@ -27,6 +28,8 @@ const (
|
|||||||
testTags = "administrator"
|
testTags = "administrator"
|
||||||
testVHosts = `{"/": {"configure": ".*", "write": ".*", "read": ".*"}}`
|
testVHosts = `{"/": {"configure": ".*", "write": ".*", "read": ".*"}}`
|
||||||
testVHostTopics = `{"/": {"amq.topic": {"write": ".*", "read": ".*"}}}`
|
testVHostTopics = `{"/": {"amq.topic": {"write": ".*", "read": ".*"}}}`
|
||||||
|
|
||||||
|
roleName = "web"
|
||||||
)
|
)
|
||||||
|
|
||||||
func prepareRabbitMQTestContainer(t *testing.T) (func(), string, int) {
|
func prepareRabbitMQTestContainer(t *testing.T) (func(), string, int) {
|
||||||
@ -89,9 +92,9 @@ func TestBackend_basic(t *testing.T) {
|
|||||||
PreCheck: testAccPreCheckFunc(t, uri),
|
PreCheck: testAccPreCheckFunc(t, uri),
|
||||||
LogicalBackend: b,
|
LogicalBackend: b,
|
||||||
Steps: []logicaltest.TestStep{
|
Steps: []logicaltest.TestStep{
|
||||||
testAccStepConfig(t, uri),
|
testAccStepConfig(t, uri, ""),
|
||||||
testAccStepRole(t),
|
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),
|
PreCheck: testAccPreCheckFunc(t, uri),
|
||||||
LogicalBackend: b,
|
LogicalBackend: b,
|
||||||
Steps: []logicaltest.TestStep{
|
Steps: []logicaltest.TestStep{
|
||||||
testAccStepConfig(t, uri),
|
testAccStepConfig(t, uri, ""),
|
||||||
{
|
{
|
||||||
Operation: logical.CreateOperation,
|
Operation: logical.CreateOperation,
|
||||||
Path: "roles/web",
|
Path: fmt.Sprintf("roles/%s", roleName),
|
||||||
Data: map[string]interface{}{
|
Data: map[string]interface{}{
|
||||||
"tags": testTags,
|
"tags": testTags,
|
||||||
"vhosts": `{"invalid":{"write": ".*", "read": ".*"}}`,
|
"vhosts": `{"invalid":{"write": ".*", "read": ".*"}}`,
|
||||||
@ -123,7 +126,7 @@ func TestBackend_returnsErrs(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Operation: logical.ReadOperation,
|
Operation: logical.ReadOperation,
|
||||||
Path: "creds/web",
|
Path: fmt.Sprintf("creds/%s", roleName),
|
||||||
ErrorOk: true,
|
ErrorOk: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -144,11 +147,35 @@ func TestBackend_roleCrud(t *testing.T) {
|
|||||||
PreCheck: testAccPreCheckFunc(t, uri),
|
PreCheck: testAccPreCheckFunc(t, uri),
|
||||||
LogicalBackend: b,
|
LogicalBackend: b,
|
||||||
Steps: []logicaltest.TestStep{
|
Steps: []logicaltest.TestStep{
|
||||||
testAccStepConfig(t, uri),
|
testAccStepConfig(t, uri, ""),
|
||||||
testAccStepRole(t),
|
testAccStepRole(t),
|
||||||
testAccStepReadRole(t, "web", testTags, testVHosts, testVHostTopics),
|
testAccStepReadRole(t, roleName, testTags, testVHosts, testVHostTopics),
|
||||||
testAccStepDeleteRole(t, "web"),
|
testAccStepDeleteRole(t, roleName),
|
||||||
testAccStepReadRole(t, "web", "", "", ""),
|
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)
|
username := os.Getenv(envRabbitMQUsername)
|
||||||
if len(username) == 0 {
|
if len(username) == 0 {
|
||||||
username = "guest"
|
username = "guest"
|
||||||
@ -175,9 +202,10 @@ func testAccStepConfig(t *testing.T, uri string) logicaltest.TestStep {
|
|||||||
Operation: logical.UpdateOperation,
|
Operation: logical.UpdateOperation,
|
||||||
Path: "config/connection",
|
Path: "config/connection",
|
||||||
Data: map[string]interface{}{
|
Data: map[string]interface{}{
|
||||||
"connection_uri": uri,
|
"connection_uri": uri,
|
||||||
"username": username,
|
"username": username,
|
||||||
"password": password,
|
"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 {
|
func testAccStepRole(t *testing.T) logicaltest.TestStep {
|
||||||
return logicaltest.TestStep{
|
return logicaltest.TestStep{
|
||||||
Operation: logical.UpdateOperation,
|
Operation: logical.UpdateOperation,
|
||||||
Path: "roles/web",
|
Path: fmt.Sprintf("roles/%s", roleName),
|
||||||
Data: map[string]interface{}{
|
Data: map[string]interface{}{
|
||||||
"tags": testTags,
|
"tags": testTags,
|
||||||
"vhosts": testVHosts,
|
"vhosts": testVHosts,
|
||||||
|
|||||||
14
builtin/logical/rabbitmq/passwords.go
Normal file
14
builtin/logical/rabbitmq/passwords.go
Normal file
@ -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"
|
rabbithole "github.com/michaelklishin/rabbit-hole"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
storageKey = "config/connection"
|
||||||
|
)
|
||||||
|
|
||||||
func pathConfigConnection(b *backend) *framework.Path {
|
func pathConfigConnection(b *backend) *framework.Path {
|
||||||
return &framework.Path{
|
return &framework.Path{
|
||||||
Pattern: "config/connection",
|
Pattern: "config/connection",
|
||||||
@ -30,6 +34,10 @@ func pathConfigConnection(b *backend) *framework.Path {
|
|||||||
Default: true,
|
Default: true,
|
||||||
Description: `If set, connection_uri is verified by actually connecting to the RabbitMQ management API`,
|
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{
|
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
|
return logical.ErrorResponse("missing password"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
passwordPolicy := data.Get("password_policy").(string)
|
||||||
|
|
||||||
// Don't check the connection_url if verification is disabled
|
// Don't check the connection_url if verification is disabled
|
||||||
verifyConnection := data.Get("verify_connection").(bool)
|
verifyConnection := data.Get("verify_connection").(bool)
|
||||||
if verifyConnection {
|
if verifyConnection {
|
||||||
@ -73,15 +83,14 @@ func (b *backend) pathConnectionUpdate(ctx context.Context, req *logical.Request
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Store it
|
// Store it
|
||||||
entry, err := logical.StorageEntryJSON("config/connection", connectionConfig{
|
config := connectionConfig{
|
||||||
URI: uri,
|
URI: uri,
|
||||||
Username: username,
|
Username: username,
|
||||||
Password: password,
|
Password: password,
|
||||||
})
|
PasswordPolicy: passwordPolicy,
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
if err := req.Storage.Put(ctx, entry); err != nil {
|
err := writeConfig(ctx, req.Storage, config)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,6 +100,33 @@ func (b *backend) pathConnectionUpdate(ctx context.Context, req *logical.Request
|
|||||||
return nil, nil
|
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
|
// connectionConfig contains the information required to make a connection to a RabbitMQ node
|
||||||
type connectionConfig struct {
|
type connectionConfig struct {
|
||||||
// URI of the RabbitMQ server
|
// URI of the RabbitMQ server
|
||||||
@ -101,6 +137,9 @@ type connectionConfig struct {
|
|||||||
|
|
||||||
// Password for the Username
|
// Password for the Username
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
|
|
||||||
|
// PasswordPolicy for generating passwords for dynamic credentials
|
||||||
|
PasswordPolicy string `json:"password_policy"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const pathConfigConnectionHelpSyn = `
|
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)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,7 +37,7 @@ var (
|
|||||||
AlphaNumericFullSymbolRuneset = []rune(AlphaNumericFullSymbolCharset)
|
AlphaNumericFullSymbolRuneset = []rune(AlphaNumericFullSymbolCharset)
|
||||||
|
|
||||||
// DefaultStringGenerator has reasonable default rules for generating strings
|
// DefaultStringGenerator has reasonable default rules for generating strings
|
||||||
DefaultStringGenerator = StringGenerator{
|
DefaultStringGenerator = &StringGenerator{
|
||||||
Length: 20,
|
Length: 20,
|
||||||
Rules: []Rule{
|
Rules: []Rule{
|
||||||
CharsetRule{
|
CharsetRule{
|
||||||
|
|||||||
@ -106,7 +106,7 @@ func TestStringGenerator_Generate_errors(t *testing.T) {
|
|||||||
type testCase struct {
|
type testCase struct {
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
generator *StringGenerator
|
generator *StringGenerator
|
||||||
rng io.Reader
|
rng io.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := map[string]testCase{
|
tests := map[string]testCase{
|
||||||
@ -121,7 +121,7 @@ func TestStringGenerator_Generate_errors(t *testing.T) {
|
|||||||
},
|
},
|
||||||
charset: AlphaNumericShortSymbolRuneset,
|
charset: AlphaNumericShortSymbolRuneset,
|
||||||
},
|
},
|
||||||
rng: rand.Reader,
|
rng: rand.Reader,
|
||||||
},
|
},
|
||||||
"impossible rules": {
|
"impossible rules": {
|
||||||
timeout: 10 * time.Millisecond, // Keep this short so the test doesn't take too long
|
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,
|
charset: AlphaNumericShortSymbolRuneset,
|
||||||
},
|
},
|
||||||
rng: rand.Reader,
|
rng: rand.Reader,
|
||||||
},
|
},
|
||||||
"bad RNG reader": {
|
"bad RNG reader": {
|
||||||
timeout: 10 * time.Millisecond, // Keep this short so the test doesn't take too long
|
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{},
|
Rules: []Rule{},
|
||||||
charset: AlphaNumericShortSymbolRuneset,
|
charset: AlphaNumericShortSymbolRuneset,
|
||||||
},
|
},
|
||||||
rng: badReader{},
|
rng: badReader{},
|
||||||
},
|
},
|
||||||
"0 length": {
|
"0 length": {
|
||||||
timeout: 10 * time.Millisecond,
|
timeout: 10 * time.Millisecond,
|
||||||
@ -157,7 +157,7 @@ func TestStringGenerator_Generate_errors(t *testing.T) {
|
|||||||
},
|
},
|
||||||
charset: []rune("abcde"),
|
charset: []rune("abcde"),
|
||||||
},
|
},
|
||||||
rng: rand.Reader,
|
rng: rand.Reader,
|
||||||
},
|
},
|
||||||
"-1 length": {
|
"-1 length": {
|
||||||
timeout: 10 * time.Millisecond,
|
timeout: 10 * time.Millisecond,
|
||||||
@ -171,7 +171,7 @@ func TestStringGenerator_Generate_errors(t *testing.T) {
|
|||||||
},
|
},
|
||||||
charset: []rune("abcde"),
|
charset: []rune("abcde"),
|
||||||
},
|
},
|
||||||
rng: rand.Reader,
|
rng: rand.Reader,
|
||||||
},
|
},
|
||||||
"no charset": {
|
"no charset": {
|
||||||
timeout: 10 * time.Millisecond,
|
timeout: 10 * time.Millisecond,
|
||||||
@ -179,7 +179,7 @@ func TestStringGenerator_Generate_errors(t *testing.T) {
|
|||||||
Length: 20,
|
Length: 20,
|
||||||
Rules: []Rule{},
|
Rules: []Rule{},
|
||||||
},
|
},
|
||||||
rng: rand.Reader,
|
rng: rand.Reader,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,8 +333,8 @@ func TestRandomRunes_errors(t *testing.T) {
|
|||||||
"šŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſ℀℁ℂ℃℄℅℆ℇ℈℉ℊℋℌℍℎℏℐℑℒℓ℔ℕ№℗℘ℙℚℛℜℝ℞℟℠" +
|
"šŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſ℀℁ℂ℃℄℅℆ℇ℈℉ℊℋℌℍℎℏℐℑℒℓ℔ℕ№℗℘ℙℚℛℜℝ℞℟℠" +
|
||||||
"Σ",
|
"Σ",
|
||||||
),
|
),
|
||||||
length:20,
|
length: 20,
|
||||||
rng: rand.Reader,
|
rng: rand.Reader,
|
||||||
},
|
},
|
||||||
"length is zero": {
|
"length is zero": {
|
||||||
charset: []rune("abcde"),
|
charset: []rune("abcde"),
|
||||||
@ -372,22 +372,24 @@ func BenchmarkStringGenerator_Generate(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
generator StringGenerator
|
generator *StringGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
benches := map[string]testCase{
|
benches := map[string]testCase{
|
||||||
"no rules": {
|
"no restrictions": {
|
||||||
generator: StringGenerator{
|
generator: &StringGenerator{
|
||||||
charset: AlphaNumericFullSymbolRuneset,
|
Rules: []Rule{
|
||||||
Rules: []Rule{},
|
CharsetRule{
|
||||||
|
Charset: AlphaNumericFullSymbolRuneset,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"default generator": {
|
"default generator": {
|
||||||
generator: DefaultStringGenerator,
|
generator: DefaultStringGenerator,
|
||||||
},
|
},
|
||||||
"large symbol set": {
|
"large symbol set": {
|
||||||
generator: StringGenerator{
|
generator: &StringGenerator{
|
||||||
charset: AlphaNumericFullSymbolRuneset,
|
|
||||||
Rules: []Rule{
|
Rules: []Rule{
|
||||||
CharsetRule{
|
CharsetRule{
|
||||||
Charset: LowercaseRuneset,
|
Charset: LowercaseRuneset,
|
||||||
@ -409,13 +411,14 @@ func BenchmarkStringGenerator_Generate(b *testing.B) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"max symbol set": {
|
"max symbol set": {
|
||||||
generator: StringGenerator{
|
generator: &StringGenerator{
|
||||||
charset: []rune(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" +
|
|
||||||
"`abcdefghijklmnopqrstuvwxyz{|}~ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠ" +
|
|
||||||
"ġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠ" +
|
|
||||||
"šŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſ℀℁ℂ℃℄℅℆ℇ℈℉ℊℋℌℍℎℏℐℑℒℓ℔ℕ№℗℘ℙℚℛℜℝ℞℟℠",
|
|
||||||
),
|
|
||||||
Rules: []Rule{
|
Rules: []Rule{
|
||||||
|
CharsetRule{
|
||||||
|
Charset: []rune(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" +
|
||||||
|
"`abcdefghijklmnopqrstuvwxyz{|}~ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠ" +
|
||||||
|
"ġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠ" +
|
||||||
|
"šŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſ℀℁ℂ℃℄℅℆ℇ℈℉ℊℋℌℍℎℏℐℑℒℓ℔ℕ№℗℘ℙℚℛℜℝ℞℟℠"),
|
||||||
|
},
|
||||||
CharsetRule{
|
CharsetRule{
|
||||||
Charset: LowercaseRuneset,
|
Charset: LowercaseRuneset,
|
||||||
MinChars: 1,
|
MinChars: 1,
|
||||||
@ -432,9 +435,11 @@ func BenchmarkStringGenerator_Generate(b *testing.B) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"restrictive charset rules": {
|
"restrictive charset rules": {
|
||||||
generator: StringGenerator{
|
generator: &StringGenerator{
|
||||||
charset: AlphaNumericShortSymbolRuneset,
|
|
||||||
Rules: []Rule{
|
Rules: []Rule{
|
||||||
|
CharsetRule{
|
||||||
|
Charset: AlphaNumericShortSymbolRuneset,
|
||||||
|
},
|
||||||
CharsetRule{
|
CharsetRule{
|
||||||
Charset: []rune("A"),
|
Charset: []rune("A"),
|
||||||
MinChars: 1,
|
MinChars: 1,
|
||||||
@ -551,7 +556,7 @@ func (badReader) Read([]byte) (int, error) {
|
|||||||
|
|
||||||
func TestValidate(t *testing.T) {
|
func TestValidate(t *testing.T) {
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
generator StringGenerator
|
generator *StringGenerator
|
||||||
expectErr bool
|
expectErr bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,33 +566,33 @@ func TestValidate(t *testing.T) {
|
|||||||
expectErr: false,
|
expectErr: false,
|
||||||
},
|
},
|
||||||
"length is 0": {
|
"length is 0": {
|
||||||
generator: StringGenerator{
|
generator: &StringGenerator{
|
||||||
Length: 0,
|
Length: 0,
|
||||||
},
|
},
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
},
|
},
|
||||||
"length is negative": {
|
"length is negative": {
|
||||||
generator: StringGenerator{
|
generator: &StringGenerator{
|
||||||
Length: -2,
|
Length: -2,
|
||||||
},
|
},
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
},
|
},
|
||||||
"nil charset, no rules": {
|
"nil charset, no rules": {
|
||||||
generator: StringGenerator{
|
generator: &StringGenerator{
|
||||||
Length: 5,
|
Length: 5,
|
||||||
charset: nil,
|
charset: nil,
|
||||||
},
|
},
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
},
|
},
|
||||||
"zero length charset, no rules": {
|
"zero length charset, no rules": {
|
||||||
generator: StringGenerator{
|
generator: &StringGenerator{
|
||||||
Length: 5,
|
Length: 5,
|
||||||
charset: []rune{},
|
charset: []rune{},
|
||||||
},
|
},
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
},
|
},
|
||||||
"rules require password longer than length": {
|
"rules require password longer than length": {
|
||||||
generator: StringGenerator{
|
generator: &StringGenerator{
|
||||||
Length: 5,
|
Length: 5,
|
||||||
charset: []rune("abcde"),
|
charset: []rune("abcde"),
|
||||||
Rules: []Rule{
|
Rules: []Rule{
|
||||||
@ -600,7 +605,7 @@ func TestValidate(t *testing.T) {
|
|||||||
expectErr: true,
|
expectErr: true,
|
||||||
},
|
},
|
||||||
"charset has non-printable characters": {
|
"charset has non-printable characters": {
|
||||||
generator: StringGenerator{
|
generator: &StringGenerator{
|
||||||
Length: 0,
|
Length: 0,
|
||||||
charset: []rune{
|
charset: []rune{
|
||||||
'a',
|
'a',
|
||||||
|
|||||||
@ -194,3 +194,16 @@ func (d StaticSystemView) GeneratePasswordFromPolicy(ctx context.Context, policy
|
|||||||
}
|
}
|
||||||
return policy.Generate(ctx, nil)
|
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) {
|
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()
|
defer cancel()
|
||||||
|
|
||||||
policyEntry := storageEntry(t, "testpolicy",
|
policyEntry := storageEntry(t, "testpolicy",
|
||||||
|
|||||||
2
vendor/github.com/hashicorp/vault/sdk/helper/random/string_generator.go
generated
vendored
2
vendor/github.com/hashicorp/vault/sdk/helper/random/string_generator.go
generated
vendored
@ -37,7 +37,7 @@ var (
|
|||||||
AlphaNumericFullSymbolRuneset = []rune(AlphaNumericFullSymbolCharset)
|
AlphaNumericFullSymbolRuneset = []rune(AlphaNumericFullSymbolCharset)
|
||||||
|
|
||||||
// DefaultStringGenerator has reasonable default rules for generating strings
|
// DefaultStringGenerator has reasonable default rules for generating strings
|
||||||
DefaultStringGenerator = StringGenerator{
|
DefaultStringGenerator = &StringGenerator{
|
||||||
Length: 20,
|
Length: 20,
|
||||||
Rules: []Rule{
|
Rules: []Rule{
|
||||||
CharsetRule{
|
CharsetRule{
|
||||||
|
|||||||
13
vendor/github.com/hashicorp/vault/sdk/logical/system_view.go
generated
vendored
13
vendor/github.com/hashicorp/vault/sdk/logical/system_view.go
generated
vendored
@ -194,3 +194,16 @@ func (d StaticSystemView) GeneratePasswordFromPolicy(ctx context.Context, policy
|
|||||||
}
|
}
|
||||||
return policy.Generate(ctx, nil)
|
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
|
### Parameters
|
||||||
|
|
||||||
- `connection_uri` `(string: <required>)` – Specifies the RabbitMQ connection
|
- `connection_uri` `(string: <required>)` – Specifies the RabbitMQ connection URI.
|
||||||
URI.
|
|
||||||
|
|
||||||
- `username` `(string: <required>)` – Specifies the RabbitMQ management
|
- `username` `(string: <required>)` – Specifies the RabbitMQ management administrator username.
|
||||||
administrator username.
|
|
||||||
|
|
||||||
- `password` `(string: <required>)` – Specifies the RabbitMQ management
|
- `password` `(string: <required>)` – Specifies the RabbitMQ management administrator password.
|
||||||
administrator password.
|
|
||||||
|
|
||||||
- `verify_connection` `(bool: true)` – Specifies whether to verify connection
|
- `verify_connection` `(bool: true)` – Specifies whether to verify connection URI, username, and password.
|
||||||
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
|
### Sample Payload
|
||||||
|
|
||||||
@ -44,12 +43,16 @@ RabbitMQ.
|
|||||||
{
|
{
|
||||||
"connection_uri": "https://...",
|
"connection_uri": "https://...",
|
||||||
"username": "user",
|
"username": "user",
|
||||||
"password": "password"
|
"password": "password",
|
||||||
|
"password_policy": "rabbitmq_policy"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Sample Request
|
### Sample Request
|
||||||
|
|
||||||
|
<Tabs>
|
||||||
|
<Tab heading="cURL">
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ curl \
|
$ curl \
|
||||||
--header "X-Vault-Token: ..." \
|
--header "X-Vault-Token: ..." \
|
||||||
@ -57,6 +60,18 @@ $ curl \
|
|||||||
--data @payload.json \
|
--data @payload.json \
|
||||||
http://127.0.0.1:8200/v1/rabbitmq/config/connection
|
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
|
## Configure Lease
|
||||||
|
|
||||||
@ -83,6 +98,9 @@ This endpoint configures the lease settings for generated credentials.
|
|||||||
|
|
||||||
### Sample Request
|
### Sample Request
|
||||||
|
|
||||||
|
<Tabs>
|
||||||
|
<Tab heading="cURL">
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ curl \
|
$ curl \
|
||||||
--header "X-Vault-Token: ..." \
|
--header "X-Vault-Token: ..." \
|
||||||
@ -90,6 +108,16 @@ $ curl \
|
|||||||
--data @payload.json \
|
--data @payload.json \
|
||||||
http://127.0.0.1:8200/v1/rabbitmq/config/lease
|
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
|
## Create Role
|
||||||
|
|
||||||
@ -124,6 +152,9 @@ This endpoint creates or updates the role definition.
|
|||||||
|
|
||||||
### Sample Request
|
### Sample Request
|
||||||
|
|
||||||
|
<Tabs>
|
||||||
|
<Tab heading="cURL">
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ curl \
|
$ curl \
|
||||||
--header "X-Vault-Token: ..." \
|
--header "X-Vault-Token: ..." \
|
||||||
@ -131,6 +162,17 @@ $ curl \
|
|||||||
--data @payload.json \
|
--data @payload.json \
|
||||||
http://127.0.0.1:8200/v1/rabbitmq/roles/my-role
|
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
|
## Read Role
|
||||||
|
|
||||||
@ -147,11 +189,22 @@ This endpoint queries the role definition.
|
|||||||
|
|
||||||
### Sample Request
|
### Sample Request
|
||||||
|
|
||||||
|
<Tabs>
|
||||||
|
<Tab heading="cURL">
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ curl \
|
$ curl \
|
||||||
--header "X-Vault-Token: ..." \
|
--header "X-Vault-Token: ..." \
|
||||||
http://127.0.0.1:8200/v1/rabbitmq/roles/my-role
|
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
|
### Sample Response
|
||||||
|
|
||||||
@ -180,12 +233,23 @@ This endpoint deletes the role definition.
|
|||||||
|
|
||||||
### Sample Request
|
### Sample Request
|
||||||
|
|
||||||
|
<Tabs>
|
||||||
|
<Tab heading="cURL">
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ curl \
|
$ curl \
|
||||||
--header "X-Vault-Token: ..." \
|
--header "X-Vault-Token: ..." \
|
||||||
--request DELETE \
|
--request DELETE \
|
||||||
http://127.0.0.1:8200/v1/rabbitmq/roles/my-role
|
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
|
## Generate Credentials
|
||||||
|
|
||||||
@ -203,11 +267,22 @@ role.
|
|||||||
|
|
||||||
### Sample Request
|
### Sample Request
|
||||||
|
|
||||||
|
<Tabs>
|
||||||
|
<Tab heading="cURL">
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ curl \
|
$ curl \
|
||||||
--header "X-Vault-Token: ..." \
|
--header "X-Vault-Token: ..." \
|
||||||
http://127.0.0.1:8200/v1/rabbitmq/creds/my-role
|
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
|
### Sample Response
|
||||||
|
|
||||||
|
|||||||
@ -81,11 +81,11 @@ the proper permission, it can generate credentials.
|
|||||||
$ vault read rabbitmq/creds/my-role
|
$ vault read rabbitmq/creds/my-role
|
||||||
Key Value
|
Key Value
|
||||||
--- -----
|
--- -----
|
||||||
lease_id rabbitmq/creds/my-role/37d70d04-f24d-760a-e06e-b9b21087f0f4
|
lease_id rabbitmq/creds/my-role/I39Hu8XXOombof4wiK5bKMn9
|
||||||
lease_duration 768h
|
lease_duration 768h
|
||||||
lease_renewable true
|
lease_renewable true
|
||||||
password a98af72b-b6c9-b4b1-fe37-c73a572befed
|
password 3yNDBikgQvrkx2VA2zhq5IdSM7IWk1RyMYJr
|
||||||
username token-590f1fe2-1094-a4d6-01a7-9d4ff756a085
|
username root-39669250-3894-8032-c420-3d58483ebfc4
|
||||||
```
|
```
|
||||||
|
|
||||||
Using ACLs, it is possible to restrict using the rabbitmq secrets engine
|
Using ACLs, it is possible to restrict using the rabbitmq secrets engine
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user