diff --git a/go.mod b/go.mod index a418c691f..9b315839c 100644 --- a/go.mod +++ b/go.mod @@ -83,7 +83,7 @@ require ( github.com/hashicorp/vault-plugin-auth-oci v0.5.4 github.com/hashicorp/vault-plugin-database-elasticsearch v0.5.4 github.com/hashicorp/vault-plugin-database-mongodbatlas v0.1.0-beta1.0.20200521152755-9cf156a44f9c - github.com/hashicorp/vault-plugin-secrets-ad v0.6.4-beta1.0.20200518124111-3dceeb3ce90e + github.com/hashicorp/vault-plugin-secrets-ad v0.6.6 github.com/hashicorp/vault-plugin-secrets-alicloud v0.5.5 github.com/hashicorp/vault-plugin-secrets-azure v0.5.6 github.com/hashicorp/vault-plugin-secrets-gcp v0.6.2 @@ -92,7 +92,7 @@ require ( github.com/hashicorp/vault-plugin-secrets-mongodbatlas v0.1.2 github.com/hashicorp/vault-plugin-secrets-openldap v0.1.3 github.com/hashicorp/vault/api v1.0.5-0.20200519221902-385fac77e20f - github.com/hashicorp/vault/sdk v0.1.14-0.20200519221838-e0cfd64bc267 + github.com/hashicorp/vault/sdk v0.1.14-0.20200527182800-ad90e0b39d2f github.com/influxdata/influxdb v0.0.0-20190411212539-d24b7ba8c4c4 github.com/jcmturner/gokrb5/v8 v8.0.0 github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f diff --git a/go.sum b/go.sum index a77b4318b..497135683 100644 --- a/go.sum +++ b/go.sum @@ -471,8 +471,6 @@ github.com/hashicorp/nomad/api v0.0.0-20191220223628-edc62acd919d/go.mod h1:WKCL github.com/hashicorp/raft v1.0.1/go.mod h1:DVSAWItjLjTOkVbSpWQ0j0kUADIvDaCtBxIcbNAQLkI= github.com/hashicorp/raft v1.1.2-0.20191002163536-9c6bd3e3eb17 h1:p+2EISNdFCnD9R+B4xCiqSn429MCFtvM41aHJDJ6qW4= github.com/hashicorp/raft v1.1.2-0.20191002163536-9c6bd3e3eb17/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= -github.com/hashicorp/raft v1.1.2 h1:oxEL5DDeurYxLd3UbcY/hccgSPhLLpiBZ1YxtWEq59c= -github.com/hashicorp/raft v1.1.2/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= github.com/hashicorp/raft v1.1.3-0.20200501224250-c95aa91e604e h1:hMRRBhY9cayPJzEgNGNAl74TJ0rwY3Csbr43ogjKh1I= github.com/hashicorp/raft v1.1.3-0.20200501224250-c95aa91e604e/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk= @@ -504,8 +502,8 @@ github.com/hashicorp/vault-plugin-database-elasticsearch v0.5.4 h1:YE4qndazWmYGp github.com/hashicorp/vault-plugin-database-elasticsearch v0.5.4/go.mod h1:QjGrrxcRXv/4XkEZAlM0VMZEa3uxKAICFqDj27FP/48= github.com/hashicorp/vault-plugin-database-mongodbatlas v0.1.0-beta1.0.20200521152755-9cf156a44f9c h1:9pXwe7sEVhZ5C3U6egIrKaZBb5lD0FvLIjISEvpbQQA= github.com/hashicorp/vault-plugin-database-mongodbatlas v0.1.0-beta1.0.20200521152755-9cf156a44f9c/go.mod h1:HTXNzFr/SAVtJOs7jz0XxZ69jlKtaceEwp37l86UAQ0= -github.com/hashicorp/vault-plugin-secrets-ad v0.6.4-beta1.0.20200518124111-3dceeb3ce90e h1:0GK1BNBfglD2sydZ4XXMjJElhY8bC2TDdc0vk1Q9zbA= -github.com/hashicorp/vault-plugin-secrets-ad v0.6.4-beta1.0.20200518124111-3dceeb3ce90e/go.mod h1:SCsKcChP8yrtOHXOeTD7oRk0oflj3IxA9y9zTOGtQ8s= +github.com/hashicorp/vault-plugin-secrets-ad v0.6.6 h1:GskxrCCL2flrBtnAeOsBV+whCaqnnM/+t/h1IyqukNo= +github.com/hashicorp/vault-plugin-secrets-ad v0.6.6/go.mod h1:L5L6NoJFxRvgxhuA2sWhloc3sbgmE7KxhNcoRxcaH9U= github.com/hashicorp/vault-plugin-secrets-alicloud v0.5.5 h1:BOOtSls+BQ1EtPmpE9LoqZztsEZ1fRWVSkHWtRIrCB4= github.com/hashicorp/vault-plugin-secrets-alicloud v0.5.5/go.mod h1:gAoReoUpBHaBwkxQqTK7FY8nQC0MuaZHLiW5WOSny5g= github.com/hashicorp/vault-plugin-secrets-azure v0.5.6 h1:4PgQ5rCT29wW5PMyebEhPkEYuR5s+SnInuZz3x2cP50= diff --git a/vendor/github.com/hashicorp/raft/.golangci-lint.yml b/vendor/github.com/hashicorp/raft/.golangci-lint.yml new file mode 100644 index 000000000..a021e196e --- /dev/null +++ b/vendor/github.com/hashicorp/raft/.golangci-lint.yml @@ -0,0 +1,49 @@ +run: + deadline: 5m + +linters-settings: + govet: + check-shadowing: true + golint: + min-confidence: 0 + +linters: + disable-all: true + enable: + - gofmt + #- golint + - govet + #- varcheck + #- typecheck + #- gosimple + +issues: + exclude-use-default: false + exclude: + # ignore the false positive erros resulting from not including a comment above every `package` keyword + - should have a package comment, unless it's in another file for this package (golint) + # golint: Annoying issue about not having a comment. The rare codebase has such comments + # - (comment on exported (method|function|type|const)|should have( a package)? comment|comment should be of the form) + # errcheck: Almost all programs ignore errors on these functions and in most cases it's ok + - Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked + + # golint: False positive when tests are defined in package 'test' + - func name will be used as test\.Test.* by other packages, and that stutters; consider calling this + + # staticcheck: Developers tend to write in C-style with an + # explicit 'break' in a 'switch', so it's ok to ignore + - ineffective break statement. Did you mean to break out of the outer loop + # gosec: Too many false-positives on 'unsafe' usage + - Use of unsafe calls should be audited + + # gosec: Too many false-positives for parametrized shell calls + - Subprocess launch(ed with variable|ing should be audited) + + # gosec: Duplicated errcheck checks + - G104 + + # gosec: Too many issues in popular repos + - (Expect directory permissions to be 0750 or less|Expect file permissions to be 0600 or less) + + # gosec: False positive is triggered by 'src, err := ioutil.ReadFile(filename)' + - Potential file inclusion via variable diff --git a/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/backend.go b/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/backend.go index 6174e159f..f1ddbb3cc 100644 --- a/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/backend.go +++ b/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/backend.go @@ -14,21 +14,22 @@ import ( ) func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) { - backend := newBackend(util.NewSecretsClient(conf.Logger)) + backend := newBackend(util.NewSecretsClient(conf.Logger), conf.System) if err := backend.Setup(ctx, conf); err != nil { return nil, err } return backend, nil } -func newBackend(client secretsClient) *backend { +func newBackend(client secretsClient, passwordGenerator passwordGenerator) *backend { adBackend := &backend{ client: client, roleCache: cache.New(roleCacheExpiration, roleCacheCleanup), credCache: cache.New(credCacheExpiration, credCacheCleanup), rotateRootLock: new(int32), checkOutHandler: &checkOutHandler{ - client: client, + client: client, + passwordGenerator: passwordGenerator, }, checkOutLocks: locksutil.CreateLocks(), } diff --git a/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/checkout_handler.go b/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/checkout_handler.go index 5b4c729aa..61d021446 100644 --- a/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/checkout_handler.go +++ b/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/checkout_handler.go @@ -3,7 +3,7 @@ package plugin import ( "context" "errors" - "github.com/hashicorp/vault-plugin-secrets-ad/plugin/util" + "github.com/hashicorp/vault/sdk/logical" ) @@ -32,7 +32,8 @@ type CheckOut struct { // checkOutHandler manages checkouts. It's not thread-safe and expects the caller to handle locking because // locking may span multiple calls. type checkOutHandler struct { - client secretsClient + client secretsClient + passwordGenerator passwordGenerator } // CheckOut attempts to check out a service account. If the account is unavailable, it returns @@ -98,7 +99,7 @@ func (h *checkOutHandler) CheckIn(ctx context.Context, storage logical.Storage, if engineConf == nil { return errors.New("the config is currently unset") } - newPassword, err := util.GeneratePassword(engineConf.PasswordConf.Formatter, engineConf.PasswordConf.Length) + newPassword, err := GeneratePassword(ctx, engineConf.PasswordConf, h.passwordGenerator) if err != nil { return err } diff --git a/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/config.go b/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/config.go new file mode 100644 index 000000000..90de64e91 --- /dev/null +++ b/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/config.go @@ -0,0 +1,76 @@ +package plugin + +import ( + "fmt" + "strings" + + "github.com/hashicorp/vault-plugin-secrets-ad/plugin/client" +) + +type configuration struct { + PasswordConf passwordConf + ADConf *client.ADConf + LastRotationTolerance int +} + +type passwordConf struct { + TTL int `json:"ttl"` + MaxTTL int `json:"max_ttl"` + + // Mutually exclusive with Length and Formatter + PolicyName string `json:"password_policy"` + + // Length of the password to generate. Mutually exclusive with PolicyName. + // Deprecated + Length int `json:"length"` + + // Formatter describes how to format a password. This allows for prefixes and suffixes on the password. + // Mutually exclusive with PolicyName. + // Deprecated + Formatter string `json:"formatter"` +} + +func (c passwordConf) Map() map[string]interface{} { + return map[string]interface{}{ + "ttl": c.TTL, + "max_ttl": c.MaxTTL, + "length": c.Length, + "formatter": c.Formatter, + "policy_name": c.PolicyName, + } +} + +// validate returns an error if the configuration is invalid/unable to process for whatever reason. +func (c passwordConf) validate() error { + if c.PolicyName != "" && + (c.Length != 0 || c.Formatter != "") { + return fmt.Errorf("cannot set password_policy and either length or formatter") + } + + // Don't validate the length and formatter fields if a policy is set + if c.PolicyName != "" { + return nil + } + + // Check for if there's no formatter. + if c.Formatter == "" { + if c.Length < len(passwordComplexityPrefix)+minimumLengthOfComplexString { + return fmt.Errorf("it's not possible to generate a _secure_ password of length %d, please boost length to %d, though Vault recommends higher", + c.Length, minimumLengthOfComplexString+len(passwordComplexityPrefix)) + } + return nil + } + + // Check for if there is a formatter. + if lengthOfPassword(c.Formatter, c.Length) < minimumLengthOfComplexString { + return fmt.Errorf("since the desired length is %d, it isn't possible to generate a sufficiently complex password - please increase desired length or remove characters from the formatter", c.Length) + } + numPwdFields := strings.Count(c.Formatter, pwdFieldTmpl) + if numPwdFields == 0 { + return fmt.Errorf("%s must contain password replacement field of %s", c.Formatter, pwdFieldTmpl) + } + if numPwdFields > 1 { + return fmt.Errorf("%s must contain ONE password replacement field of %s", c.Formatter, pwdFieldTmpl) + } + return nil +} diff --git a/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/engineconf.go b/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/engineconf.go deleted file mode 100644 index b2f4837cc..000000000 --- a/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/engineconf.go +++ /dev/null @@ -1,11 +0,0 @@ -package plugin - -import ( - "github.com/hashicorp/vault-plugin-secrets-ad/plugin/client" -) - -type configuration struct { - PasswordConf *passwordConf - ADConf *client.ADConf - LastRotationTolerance int -} diff --git a/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/passwordconf.go b/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/passwordconf.go deleted file mode 100644 index b43d4e12f..000000000 --- a/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/passwordconf.go +++ /dev/null @@ -1,17 +0,0 @@ -package plugin - -type passwordConf struct { - TTL int `json:"ttl"` - MaxTTL int `json:"max_ttl"` - Length int `json:"length"` - Formatter string `json:"formatter"` -} - -func (c *passwordConf) Map() map[string]interface{} { - return map[string]interface{}{ - "ttl": c.TTL, - "max_ttl": c.MaxTTL, - "length": c.Length, - "formatter": c.Formatter, - } -} diff --git a/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/passwords.go b/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/passwords.go new file mode 100644 index 000000000..c349f7229 --- /dev/null +++ b/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/passwords.go @@ -0,0 +1,58 @@ +package plugin + +import ( + "context" + "strings" + + "github.com/hashicorp/vault/sdk/helper/base62" +) + +var ( + // Per https://en.wikipedia.org/wiki/Password_strength#Guidelines_for_strong_passwords + minimumLengthOfComplexString = 8 + + passwordComplexityPrefix = "?@09AZ" + pwdFieldTmpl = "{{PASSWORD}}" +) + +type passwordGenerator interface { + GeneratePasswordFromPolicy(ctx context.Context, policyName string) (password string, err error) +} + +// GeneratePassword from the password configuration. This will either generate based on a password policy +// or from the provided formatter. The formatter/length options are deprecated. +func GeneratePassword(ctx context.Context, passConf passwordConf, generator passwordGenerator) (password string, err error) { + err = passConf.validate() + if err != nil { + return "", err + } + + if passConf.PolicyName != "" { + return generator.GeneratePasswordFromPolicy(ctx, passConf.PolicyName) + } + return generateDeprecatedPassword(passConf.Formatter, passConf.Length) +} + +func generateDeprecatedPassword(formatter string, totalLength int) (string, error) { + // Has formatter + if formatter != "" { + passLen := lengthOfPassword(formatter, totalLength) + pwd, err := base62.Random(passLen) + if err != nil { + return "", err + } + return strings.Replace(formatter, pwdFieldTmpl, pwd, 1), nil + } + + // Doesn't have formatter + pwd, err := base62.Random(totalLength - len(passwordComplexityPrefix)) + if err != nil { + return "", err + } + return passwordComplexityPrefix + pwd, nil +} + +func lengthOfPassword(formatter string, totalLength int) int { + lengthOfText := len(formatter) - len(pwdFieldTmpl) + return totalLength - lengthOfText +} diff --git a/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_config.go b/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_config.go index a5345a890..ed8698ba7 100644 --- a/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_config.go +++ b/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_config.go @@ -3,10 +3,10 @@ package plugin import ( "context" "errors" + "fmt" "time" "github.com/hashicorp/vault-plugin-secrets-ad/plugin/client" - "github.com/hashicorp/vault-plugin-secrets-ad/plugin/util" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/helper/ldaputil" "github.com/hashicorp/vault/sdk/logical" @@ -32,13 +32,24 @@ func readConfig(ctx context.Context, storage logical.Storage) (*configuration, e if entry == nil { return nil, nil } - config := &configuration{&passwordConf{}, &client.ADConf{}, 0} + config := &configuration{} if err := entry.DecodeJSON(config); err != nil { return nil, err } return config, nil } +func writeConfig(ctx context.Context, storage logical.Storage, config *configuration) (err error) { + entry, err := logical.StorageEntryJSON(configStorageKey, config) + if err != nil { + return fmt.Errorf("unable to marshal config to JSON: %w", err) + } + if err := storage.Put(ctx, entry); err != nil { + return fmt.Errorf("unable to store config: %w", err) + } + return nil +} + func (b *backend) pathConfig() *framework.Path { return &framework.Path{ Pattern: configPath, @@ -63,20 +74,28 @@ func (b *backend) configFields() map[string]*framework.FieldSchema { Type: framework.TypeDurationSecond, Description: "In seconds, the maximum password time-to-live.", } - fields["length"] = &framework.FieldSchema{ - Type: framework.TypeInt, - Default: defaultPasswordLength, - Description: "The desired length of passwords that Vault generates.", - } - fields["formatter"] = &framework.FieldSchema{ - Type: framework.TypeString, - Description: `Text to insert the password into, ex. "customPrefix{{PASSWORD}}customSuffix".`, - } fields["last_rotation_tolerance"] = &framework.FieldSchema{ Type: framework.TypeDurationSecond, Description: "The number of seconds after a Vault rotation where, if Active Directory shows a later rotation, it should be considered out-of-band.", Default: 5, } + fields["password_policy"] = &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Name of the password policy to use to generate passwords.", + } + + // Deprecated fields + fields["length"] = &framework.FieldSchema{ + Type: framework.TypeInt, + Default: defaultPasswordLength, + Description: "The desired length of passwords that Vault generates.", + Deprecated: true, + } + fields["formatter"] = &framework.FieldSchema{ + Type: framework.TypeString, + Description: `Text to insert the password into, ex. "customPrefix{{PASSWORD}}customSuffix".`, + Deprecated: true, + } return fields } @@ -93,9 +112,11 @@ func (b *backend) configUpdateOperation(ctx context.Context, req *logical.Reques // Build the password conf. ttl := fieldData.Get("ttl").(int) maxTTL := fieldData.Get("max_ttl").(int) + lastRotationTolerance := fieldData.Get("last_rotation_tolerance").(int) + length := fieldData.Get("length").(int) formatter := fieldData.Get("formatter").(string) - lastRotationTolerance := fieldData.Get("last_rotation_tolerance").(int) + passwordPolicy := fieldData.Get("password_policy").(string) if pre111Val, ok := fieldData.GetOk("use_pre111_group_cn_behavior"); ok { activeDirectoryConf.UsePre111GroupCNBehavior = new(bool) @@ -120,23 +141,28 @@ func (b *backend) configUpdateOperation(ctx context.Context, req *logical.Reques if maxTTL < 1 { return nil, errors.New("max_ttl must be positive") } - if err := util.ValidatePwdSettings(formatter, length); err != nil { - return nil, err - } - passwordConf := &passwordConf{ - TTL: ttl, - MaxTTL: maxTTL, - Length: length, - Formatter: formatter, + passwordConf := passwordConf{ + TTL: ttl, + MaxTTL: maxTTL, + Length: length, + Formatter: formatter, + PolicyName: passwordPolicy, } - - config := &configuration{passwordConf, &client.ADConf{ConfigEntry: activeDirectoryConf}, lastRotationTolerance} - entry, err := logical.StorageEntryJSON(configStorageKey, config) + err = passwordConf.validate() if err != nil { return nil, err } - if err := req.Storage.Put(ctx, entry); err != nil { + + config := configuration{ + PasswordConf: passwordConf, + ADConf: &client.ADConf{ + ConfigEntry: activeDirectoryConf, + }, + LastRotationTolerance: lastRotationTolerance, + } + err = writeConfig(ctx, req.Storage, &config) + if err != nil { return nil, err } diff --git a/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_creds.go b/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_creds.go index c2a192b9a..9e88ba342 100644 --- a/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_creds.go +++ b/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_creds.go @@ -7,7 +7,6 @@ import ( "time" "github.com/go-errors/errors" - "github.com/hashicorp/vault-plugin-secrets-ad/plugin/util" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/logical" ) @@ -150,7 +149,7 @@ func (b *backend) credReadOperation(ctx context.Context, req *logical.Request, f } func (b *backend) generateAndReturnCreds(ctx context.Context, engineConf *configuration, storage logical.Storage, roleName string, role *backendRole, previousCred map[string]interface{}) (*logical.Response, error) { - newPassword, err := util.GeneratePassword(engineConf.PasswordConf.Formatter, engineConf.PasswordConf.Length) + newPassword, err := GeneratePassword(ctx, engineConf.PasswordConf, b.System()) if err != nil { return nil, err } diff --git a/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_roles.go b/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_roles.go index 2f92f0bf1..1c4b864e0 100644 --- a/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_roles.go +++ b/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_roles.go @@ -221,7 +221,7 @@ func getServiceAccountName(fieldData *framework.FieldData) (string, error) { return serviceAccountName, nil } -func getValidatedTTL(passwordConf *passwordConf, fieldData *framework.FieldData) (int, error) { +func getValidatedTTL(passwordConf passwordConf, fieldData *framework.FieldData) (int, error) { ttl := fieldData.Get("ttl").(int) if ttl == 0 { ttl = passwordConf.TTL diff --git a/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_rotate_root_creds.go b/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_rotate_root_creds.go index 402a0aa79..d911eef9f 100644 --- a/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_rotate_root_creds.go +++ b/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_rotate_root_creds.go @@ -8,7 +8,6 @@ import ( "sync/atomic" "time" - "github.com/hashicorp/vault-plugin-secrets-ad/plugin/util" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/logical" ) @@ -38,7 +37,7 @@ func (b *backend) pathRotateCredentialsUpdate(ctx context.Context, req *logical. return nil, errors.New("the config is currently unset") } - newPassword, err := util.GeneratePassword(engineConf.PasswordConf.Formatter, engineConf.PasswordConf.Length) + newPassword, err := GeneratePassword(ctx, engineConf.PasswordConf, b.System()) if err != nil { return nil, err } @@ -58,7 +57,7 @@ func (b *backend) pathRotateCredentialsUpdate(ctx context.Context, req *logical. engineConf.ADConf.BindPassword = newPassword // Update the password locally. - if pwdStoringErr := storePassword(ctx, req, engineConf); pwdStoringErr != nil { + if pwdStoringErr := writeConfig(ctx, req.Storage, engineConf); pwdStoringErr != nil { // We were unable to store the new password locally. We can't continue in this state because we won't be able // to roll any passwords, including our own to get back into a state of working. So, we need to roll back to // the last password we successfully got into storage. @@ -93,17 +92,6 @@ func (b *backend) rollBackPassword(ctx context.Context, engineConf *configuratio return err } -func storePassword(ctx context.Context, req *logical.Request, engineConf *configuration) error { - entry, err := logical.StorageEntryJSON(configStorageKey, engineConf) - if err != nil { - return err - } - if err := req.Storage.Put(ctx, entry); err != nil { - return err - } - return nil -} - const pathRotateCredentialsUpdateHelpSyn = ` Request to rotate the root credentials. ` diff --git a/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/util/passwords.go b/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/util/passwords.go deleted file mode 100644 index a0c7cb013..000000000 --- a/vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/util/passwords.go +++ /dev/null @@ -1,77 +0,0 @@ -package util - -import ( - "encoding/base64" - "fmt" - "strings" - - "github.com/hashicorp/go-uuid" -) - -var ( - // Per https://en.wikipedia.org/wiki/Password_strength#Guidelines_for_strong_passwords - minimumLengthOfComplexString = 8 - - PasswordComplexityPrefix = "?@09AZ" - PwdFieldTmpl = "{{PASSWORD}}" -) - -func GeneratePassword(formatter string, totalLength int) (string, error) { - if err := ValidatePwdSettings(formatter, totalLength); err != nil { - return "", err - } - pwd, err := generatePassword(totalLength) - if err != nil { - return "", err - } - if formatter == "" { - pwd = PasswordComplexityPrefix + pwd - return pwd[:totalLength], nil - } - return strings.Replace(formatter, PwdFieldTmpl, pwd[:lengthOfPassword(formatter, totalLength)], 1), nil -} - -func ValidatePwdSettings(formatter string, totalLength int) error { - // Check for if there's no formatter. - if formatter == "" { - if totalLength < len(PasswordComplexityPrefix)+minimumLengthOfComplexString { - return fmt.Errorf("it's not possible to generate a _secure_ password of length %d, please boost length to %d, though Vault recommends higher", totalLength, minimumLengthOfComplexString+len(PasswordComplexityPrefix)) - } - return nil - } - - // Check for if there is a formatter. - if lengthOfPassword(formatter, totalLength) < minimumLengthOfComplexString { - return fmt.Errorf("since the desired length is %d, it isn't possible to generate a sufficiently complex password - please increase desired length or remove characters from the formatter", totalLength) - } - numPwdFields := strings.Count(formatter, PwdFieldTmpl) - if numPwdFields == 0 { - return fmt.Errorf("%s must contain password replacement field of %s", formatter, PwdFieldTmpl) - } - if numPwdFields > 1 { - return fmt.Errorf("%s must contain ONE password replacement field of %s", formatter, PwdFieldTmpl) - } - return nil -} - -func lengthOfPassword(formatter string, totalLength int) int { - lengthOfText := len(formatter) - len(PwdFieldTmpl) - return totalLength - lengthOfText -} - -// generatePassword returns a password of a length AT LEAST as long as the desired length, -// it may be longer. -func generatePassword(desiredLength int) (string, error) { - b, err := uuid.GenerateRandomBytes(desiredLength) - if err != nil { - return "", err - } - result := "" - // Though the result should immediately be longer than the desiredLength, - // do this in a loop to ensure there's absolutely no risk of a panic when slicing it down later. - for len(result) <= desiredLength { - // Encode to base64 because it's more complex. - result += base64.StdEncoding.EncodeToString(b) - } - return result, nil -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 170003e27..b6922fd84 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -433,7 +433,7 @@ github.com/hashicorp/vault-plugin-auth-oci github.com/hashicorp/vault-plugin-database-elasticsearch # github.com/hashicorp/vault-plugin-database-mongodbatlas v0.1.0-beta1.0.20200521152755-9cf156a44f9c github.com/hashicorp/vault-plugin-database-mongodbatlas -# github.com/hashicorp/vault-plugin-secrets-ad v0.6.4-beta1.0.20200518124111-3dceeb3ce90e +# github.com/hashicorp/vault-plugin-secrets-ad v0.6.6 github.com/hashicorp/vault-plugin-secrets-ad/plugin github.com/hashicorp/vault-plugin-secrets-ad/plugin/client github.com/hashicorp/vault-plugin-secrets-ad/plugin/util @@ -457,7 +457,7 @@ github.com/hashicorp/vault-plugin-secrets-openldap github.com/hashicorp/vault-plugin-secrets-openldap/client # github.com/hashicorp/vault/api v1.0.5-0.20200519221902-385fac77e20f => ./api github.com/hashicorp/vault/api -# github.com/hashicorp/vault/sdk v0.1.14-0.20200519221838-e0cfd64bc267 => ./sdk +# github.com/hashicorp/vault/sdk v0.1.14-0.20200527182800-ad90e0b39d2f => ./sdk github.com/hashicorp/vault/sdk/database/dbplugin github.com/hashicorp/vault/sdk/database/helper/connutil github.com/hashicorp/vault/sdk/database/helper/credsutil diff --git a/website/pages/api-docs/secret/ad/index.mdx b/website/pages/api-docs/secret/ad/index.mdx index 12e6879bd..0e5157aad 100644 --- a/website/pages/api-docs/secret/ad/index.mdx +++ b/website/pages/api-docs/secret/ad/index.mdx @@ -21,10 +21,20 @@ The `config` endpoint configures the LDAP connection and binding parameters, as ### Password parameters -- `ttl` (string, optional) - The default password time-to-live in seconds. Once the ttl has passed, a password will be rotated the next time it's requested. -- `max_ttl` (string, optional) - The maximum password time-to-live in seconds. No role will be allowed to set a custom ttl greater than the `max_ttl`. -- `length` (string, optional) - The desired password length. Defaults to 64. Minimum is 14. -- `formatter` (string, optional) - Text into which the base64 password should be inserted, formatted like so: `mycustom{{PASSWORD}}`. +- `ttl` `(int: "")` - The default password time-to-live in seconds. Once the ttl has passed, a password will + be rotated the next time it's requested. +- `max_ttl` `(int: "")` - The maximum password time-to-live in seconds. No role will be allowed to set a + custom ttl greater than the `max_ttl`. +- `password_policy` `(string: "")` - Name of the [password policy](/docs/concepts/password-policies) to use to + generate passwords from. Mutually exclusive with `length` and `formatter`. + +**Deprecated parameters**: +- `length` (string, optional) - The desired password length. Defaults to 64. Minimum is 14. Mutually exclusive + with `password_policy`. +- `formatter` (string, optional) - Text into which the base64 password should be inserted, formatted like so: + `mycustom{{PASSWORD}}`. Mutually exclusive with `password_policy`. + +The following statement is applicable when using `length` and/or `formatter`, but not `password_policy`: To meet Microsoft's password complexity requirements, all passwords begin with "?@09AZ" unless a `formatter` is provided. The `formatter` is for organizations with different, custom password requirements. It allows an organization to supply @@ -68,6 +78,9 @@ valid AD credentials with proper permissions. ### Sample Post Request + + + ```shell-session $ curl \ --header "X-Vault-Token: ..." \ @@ -75,6 +88,18 @@ $ curl \ --data @payload.json \ http://127.0.0.1:8200/v1/ad/config ``` + + + +```shell-session +$ vault write ad/config \ + binddn="domain-admin" \ + bindpass="pa$$w0rd" \ + url="ldaps://127.0.0.1" \ + userdn="dc=example,dc=com" +``` + + ### Sample Post Payload @@ -126,6 +151,9 @@ When adding a role, Vault verifies its associated service account exists. ### Sample Post Request + + + ```shell-session $ curl \ --header "X-Vault-Token: ..." \ @@ -133,6 +161,16 @@ $ curl \ --data @payload.json \ http://127.0.0.1:8200/v1/ad/roles/my-application ``` + + + +```shell-session +$ vault write ad/roles/my-application \ + service_account_name="my-application@example.com" \ + ttl=100 +``` + + ### Sample Post Payload @@ -172,12 +210,23 @@ The `creds` endpoint offers the credential information for a given role. ### Sample Get Request + + + ```shell-session $ curl \ --header "X-Vault-Token: ..." \ --request GET \ http://127.0.0.1:8200/v1/ad/creds/my-application ``` + + + +```shell-session +$ vault read ad/creds/my-application +``` + + ### Sample Get Response diff --git a/website/pages/docs/secrets/ad/index.mdx b/website/pages/docs/secrets/ad/index.mdx index c5cc41c3d..13123c568 100644 --- a/website/pages/docs/secrets/ad/index.mdx +++ b/website/pages/docs/secrets/ad/index.mdx @@ -3,10 +3,7 @@ layout: docs page_title: Active Directory - Secrets Engines sidebar_title: Active Directory description: >- - The Active Directory secrets engine for Vault generates passwords dynamically - based on - - roles. + The Active Directory secrets engine allowing Vault to generate dynamic credentials. --- # Active Directory Secrets Engine @@ -29,6 +26,17 @@ will check them in when their lending period (or, "ttl", in Vault's language) en ## Password Rotation +### Customizing Password Generation + +There are two ways of customizing how passwords are generated in the Active Directory secret engine: + +1. [Password Policies](/docs/concepts/password-policies) +2. `length` and `formatter` fields within the [configuration](api-docs/secret/ad#password-parameters) + +Utilizing password policies is the recommended path as the `length` and `formatter` fields have +been deprecated in favor of password policies. The `password_policy` field within the configuration +cannot be specified alongside either `length` or `formatter` to prevent a confusing configuration. + ### A Note on Lazy Rotation To drive home the point that passwords are rotated "lazily", consider this scenario: