diff --git a/api/client.go b/api/client.go
index 9f84dd83d..87f8c537e 100644
--- a/api/client.go
+++ b/api/client.go
@@ -51,6 +51,8 @@ const (
EnvRateLimit = "VAULT_RATE_LIMIT"
EnvHTTPProxy = "VAULT_HTTP_PROXY"
HeaderIndex = "X-Vault-Index"
+ HeaderForward = "X-Vault-Forward"
+ HeaderInconsistent = "X-Vault-Inconsistent"
)
// Deprecated values
@@ -1395,7 +1397,7 @@ func ParseReplicationState(raw string, hmacKey []byte) (*logical.WALState, error
// conjunction with RequireState.
func ForwardInconsistent() RequestCallback {
return func(req *Request) {
- req.Headers.Set("X-Vault-Inconsistent", "forward-active-node")
+ req.Headers.Set(HeaderInconsistent, "forward-active-node")
}
}
@@ -1404,7 +1406,7 @@ func ForwardInconsistent() RequestCallback {
// This feature must be enabled in Vault's configuration.
func ForwardAlways() RequestCallback {
return func(req *Request) {
- req.Headers.Set("X-Vault-Forward", "active-node")
+ req.Headers.Set(HeaderForward, "active-node")
}
}
diff --git a/api/sys_mounts.go b/api/sys_mounts.go
index 1d68a1063..8a0c5b985 100644
--- a/api/sys_mounts.go
+++ b/api/sys_mounts.go
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
+ "time"
"github.com/mitchellh/mapstructure"
)
@@ -65,7 +66,31 @@ func (c *Sys) Unmount(path string) error {
return err
}
+// Remount kicks off a remount operation, polls the status endpoint using
+// the migration ID till either success or failure state is observed
func (c *Sys) Remount(from, to string) error {
+ remountResp, err := c.StartRemount(from, to)
+ if err != nil {
+ return err
+ }
+
+ for {
+ remountStatusResp, err := c.RemountStatus(remountResp.MigrationID)
+ if err != nil {
+ return err
+ }
+ if remountStatusResp.MigrationInfo.MigrationStatus == "success" {
+ return nil
+ }
+ if remountStatusResp.MigrationInfo.MigrationStatus == "failure" {
+ return fmt.Errorf("Failure! Error encountered moving mount %s to %s, with migration ID %s", from, to, remountResp.MigrationID)
+ }
+ time.Sleep(1 * time.Second)
+ }
+}
+
+// StartRemount kicks off a mount migration and returns a response with the migration ID
+func (c *Sys) StartRemount(from, to string) (*MountMigrationOutput, error) {
body := map[string]interface{}{
"from": from,
"to": to,
@@ -73,16 +98,59 @@ func (c *Sys) Remount(from, to string) error {
r := c.c.NewRequest("POST", "/v1/sys/remount")
if err := r.SetJSONBody(body); err != nil {
- return err
+ return nil, err
}
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
resp, err := c.c.RawRequestWithContext(ctx, r)
- if err == nil {
- defer resp.Body.Close()
+ if err != nil {
+ return nil, err
}
- return err
+ defer resp.Body.Close()
+ secret, err := ParseSecret(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+ if secret == nil || secret.Data == nil {
+ return nil, errors.New("data from server response is empty")
+ }
+
+ var result MountMigrationOutput
+ err = mapstructure.Decode(secret.Data, &result)
+ if err != nil {
+ return nil, err
+ }
+
+ return &result, err
+}
+
+// RemountStatus checks the status of a mount migration operation with the provided ID
+func (c *Sys) RemountStatus(migrationID string) (*MountMigrationStatusOutput, error) {
+ r := c.c.NewRequest("GET", fmt.Sprintf("/v1/sys/remount/status/%s", migrationID))
+
+ ctx, cancelFunc := context.WithCancel(context.Background())
+ defer cancelFunc()
+ resp, err := c.c.RawRequestWithContext(ctx, r)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+ secret, err := ParseSecret(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+ if secret == nil || secret.Data == nil {
+ return nil, errors.New("data from server response is empty")
+ }
+
+ var result MountMigrationStatusOutput
+ err = mapstructure.Decode(secret.Data, &result)
+ if err != nil {
+ return nil, err
+ }
+
+ return &result, err
}
func (c *Sys) TuneMount(path string, config MountConfigInput) error {
@@ -187,3 +255,18 @@ type MountConfigOutput struct {
// Deprecated: This field will always be blank for newer server responses.
PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"`
}
+
+type MountMigrationOutput struct {
+ MigrationID string `mapstructure:"migration_id"`
+}
+
+type MountMigrationStatusOutput struct {
+ MigrationID string `mapstructure:"migration_id"`
+ MigrationInfo *MountMigrationStatusInfo `mapstructure:"migration_info"`
+}
+
+type MountMigrationStatusInfo struct {
+ SourceMount string `mapstructure:"source_mount"`
+ TargetMount string `mapstructure:"target_mount"`
+ MigrationStatus string `mapstructure:"status"`
+}
diff --git a/builtin/credential/approle/path_login.go b/builtin/credential/approle/path_login.go
index a392966fa..ba478c4ff 100644
--- a/builtin/credential/approle/path_login.go
+++ b/builtin/credential/approle/path_login.go
@@ -178,11 +178,14 @@ func (b *backend) pathLoginUpdate(ctx context.Context, req *logical.Request, dat
}
belongs, err := cidrutil.IPBelongsToCIDRBlocksSlice(req.Connection.RemoteAddr, entry.CIDRList)
- if !belongs || err != nil {
+ if err != nil {
+ return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
+ }
+
+ if !belongs {
return logical.ErrorResponse(fmt.Errorf(
- "source address %q unauthorized through CIDR restrictions on the secret ID: %w",
+ "source address %q unauthorized through CIDR restrictions on the secret ID",
req.Connection.RemoteAddr,
- err,
).Error()), nil
}
}
diff --git a/builtin/logical/ssh/path_config_ca.go b/builtin/logical/ssh/path_config_ca.go
index 42ae388a6..5b7593938 100644
--- a/builtin/logical/ssh/path_config_ca.go
+++ b/builtin/logical/ssh/path_config_ca.go
@@ -10,6 +10,7 @@ import (
"crypto/rsa"
"crypto/x509"
"encoding/pem"
+ "errors"
"fmt"
"io"
@@ -17,6 +18,8 @@ import (
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/logical"
"golang.org/x/crypto/ssh"
+
+ "github.com/mikesmitty/edkey"
)
const (
@@ -357,9 +360,9 @@ func generateSSHKeyPair(randomSource io.Reader, keyType string, keyBits int) (st
return "", "", err
}
- marshalled, err := x509.MarshalPKCS8PrivateKey(privateSeed)
- if err != nil {
- return "", "", err
+ marshalled := edkey.MarshalED25519PrivateKey(privateSeed)
+ if marshalled == nil {
+ return "", "", errors.New("unable to marshal ed25519 private key")
}
privateBlock = &pem.Block{
diff --git a/builtin/logical/ssh/path_config_ca_test.go b/builtin/logical/ssh/path_config_ca_test.go
index d346c5710..1a04d9dbe 100644
--- a/builtin/logical/ssh/path_config_ca_test.go
+++ b/builtin/logical/ssh/path_config_ca_test.go
@@ -191,17 +191,31 @@ func createDeleteHelper(t *testing.T, b logical.Backend, config *logical.Backend
}
resp, err := b.HandleRequest(context.Background(), caReq)
if err != nil || (resp != nil && resp.IsError()) {
- t.Fatalf("bad case %v: err: %v, resp:%v", index, err, resp)
+ t.Fatalf("bad case %v: err: %v, resp: %v", index, err, resp)
}
if !strings.Contains(resp.Data["public_key"].(string), caReq.Data["key_type"].(string)) {
t.Fatalf("bad case %v: expected public key of type %v but was %v", index, caReq.Data["key_type"], resp.Data["public_key"])
}
+ issueOptions := map[string]interface{}{
+ "public_key": testCAPublicKeyEd25519,
+ }
+ issueReq := &logical.Request{
+ Path: "sign/ca-issuance",
+ Operation: logical.UpdateOperation,
+ Storage: config.StorageView,
+ Data: issueOptions,
+ }
+ resp, err = b.HandleRequest(context.Background(), issueReq)
+ if err != nil || (resp != nil && resp.IsError()) {
+ t.Fatalf("bad case %v: err: %v, resp: %v", index, err, resp)
+ }
+
// Delete the configured keys
caReq.Operation = logical.DeleteOperation
resp, err = b.HandleRequest(context.Background(), caReq)
if err != nil || (resp != nil && resp.IsError()) {
- t.Fatalf("bad case %v: err: %v, resp:%v", index, err, resp)
+ t.Fatalf("bad case %v: err: %v, resp: %v", index, err, resp)
}
}
@@ -235,6 +249,24 @@ func TestSSH_ConfigCAKeyTypes(t *testing.T) {
{"ed25519", 0},
}
+ // Create a role for ssh signing.
+ roleOptions := map[string]interface{}{
+ "allow_user_certificates": true,
+ "allowed_users": "*",
+ "key_type": "ca",
+ "ttl": "30s",
+ }
+ roleReq := &logical.Request{
+ Operation: logical.UpdateOperation,
+ Path: "roles/ca-issuance",
+ Data: roleOptions,
+ Storage: config.StorageView,
+ }
+ _, err = b.HandleRequest(context.Background(), roleReq)
+ if err != nil {
+ t.Fatalf("Cannot create role to issue against: %s", err)
+ }
+
for index, scenario := range cases {
createDeleteHelper(t, b, config, index, scenario.keyType, scenario.keyBits)
}
diff --git a/builtin/logical/transit/backend.go b/builtin/logical/transit/backend.go
index 3cab23fc6..acc714d5f 100644
--- a/builtin/logical/transit/backend.go
+++ b/builtin/logical/transit/backend.go
@@ -190,7 +190,7 @@ func (b *backend) periodicFunc(ctx context.Context, req *logical.Request) error
}
// autoRotateKeys retrieves all transit keys and rotates those which have an
-// auto rotate interval defined which has passed. This operation only happens
+// auto rotate period defined which has passed. This operation only happens
// on primary nodes and performance secondary nodes which have a local mount.
func (b *backend) autoRotateKeys(ctx context.Context, req *logical.Request) error {
// Only check for autorotation once an hour to avoid unnecessarily iterating
@@ -247,15 +247,15 @@ func (b *backend) rotateIfRequired(ctx context.Context, req *logical.Request, ke
}
defer p.Unlock()
- // If the policy's automatic rotation interval is 0, it should not
+ // If the policy's automatic rotation period is 0, it should not
// automatically rotate.
- if p.AutoRotateInterval == 0 {
+ if p.AutoRotatePeriod == 0 {
return nil
}
// Retrieve the latest version of the policy and determine if it is time to rotate.
latestKey := p.Keys[strconv.Itoa(p.LatestVersion)]
- if time.Now().After(latestKey.CreationTime.Add(p.AutoRotateInterval)) {
+ if time.Now().After(latestKey.CreationTime.Add(p.AutoRotatePeriod)) {
if b.Logger().IsDebug() {
b.Logger().Debug("automatically rotating key", "key", key)
}
diff --git a/builtin/logical/transit/backend_test.go b/builtin/logical/transit/backend_test.go
index 6ad7b2d7d..c49768e73 100644
--- a/builtin/logical/transit/backend_test.go
+++ b/builtin/logical/transit/backend_test.go
@@ -1607,7 +1607,7 @@ func TestTransit_AutoRotateKeys(t *testing.T) {
Operation: logical.UpdateOperation,
Path: "keys/test2",
Data: map[string]interface{}{
- "auto_rotate_interval": 24 * time.Hour,
+ "auto_rotate_period": 24 * time.Hour,
},
}
resp, err = b.HandleRequest(context.Background(), req)
@@ -1651,7 +1651,7 @@ func TestTransit_AutoRotateKeys(t *testing.T) {
t.Fatalf("incorrect latest_version found, got: %d, want: %d", resp.Data["latest_version"], 1)
}
- // Update auto rotate interval on one key to be one nanosecond
+ // Update auto rotate period on one key to be one nanosecond
p, _, err := b.GetPolicy(context.Background(), keysutil.PolicyRequest{
Storage: storage,
Name: "test2",
@@ -1662,7 +1662,7 @@ func TestTransit_AutoRotateKeys(t *testing.T) {
if p == nil {
t.Fatal("expected non-nil policy")
}
- p.AutoRotateInterval = time.Nanosecond
+ p.AutoRotatePeriod = time.Nanosecond
err = p.Persist(context.Background(), storage)
if err != nil {
t.Fatal(err)
diff --git a/builtin/logical/transit/path_config.go b/builtin/logical/transit/path_config.go
index 336643227..a3d72731b 100644
--- a/builtin/logical/transit/path_config.go
+++ b/builtin/logical/transit/path_config.go
@@ -49,7 +49,7 @@ the latest version of the key is allowed.`,
Description: `Enables taking a backup of the named key in plaintext format. Once set, this cannot be disabled.`,
},
- "auto_rotate_interval": {
+ "auto_rotate_period": {
Type: framework.TypeDurationSecond,
Description: `Amount of time the key should live before
being automatically rotated. A value of 0
@@ -193,19 +193,19 @@ func (b *backend) pathConfigWrite(ctx context.Context, req *logical.Request, d *
}
}
- autoRotateIntervalRaw, ok, err := d.GetOkErr("auto_rotate_interval")
+ autoRotatePeriodRaw, ok, err := d.GetOkErr("auto_rotate_period")
if err != nil {
return nil, err
}
if ok {
- autoRotateInterval := time.Second * time.Duration(autoRotateIntervalRaw.(int))
+ autoRotatePeriod := time.Second * time.Duration(autoRotatePeriodRaw.(int))
// Provided value must be 0 to disable or at least an hour
- if autoRotateInterval != 0 && autoRotateInterval < time.Hour {
- return logical.ErrorResponse("auto rotate interval must be 0 to disable or at least an hour"), nil
+ if autoRotatePeriod != 0 && autoRotatePeriod < time.Hour {
+ return logical.ErrorResponse("auto rotate period must be 0 to disable or at least an hour"), nil
}
- if autoRotateInterval != p.AutoRotateInterval {
- p.AutoRotateInterval = autoRotateInterval
+ if autoRotatePeriod != p.AutoRotatePeriod {
+ p.AutoRotatePeriod = autoRotatePeriod
persistNeeded = true
}
}
diff --git a/builtin/logical/transit/path_config_test.go b/builtin/logical/transit/path_config_test.go
index 3888bc861..bc5843c65 100644
--- a/builtin/logical/transit/path_config_test.go
+++ b/builtin/logical/transit/path_config_test.go
@@ -294,44 +294,44 @@ func TestTransit_ConfigSettings(t *testing.T) {
func TestTransit_UpdateKeyConfigWithAutorotation(t *testing.T) {
tests := map[string]struct {
- initialAutoRotateInterval interface{}
- newAutoRotateInterval interface{}
- shouldError bool
- expectedValue time.Duration
+ initialAutoRotatePeriod interface{}
+ newAutoRotatePeriod interface{}
+ shouldError bool
+ expectedValue time.Duration
}{
"default (no value)": {
- initialAutoRotateInterval: "5h",
- shouldError: false,
- expectedValue: 5 * time.Hour,
+ initialAutoRotatePeriod: "5h",
+ shouldError: false,
+ expectedValue: 5 * time.Hour,
},
"0 (int)": {
- initialAutoRotateInterval: "5h",
- newAutoRotateInterval: 0,
- shouldError: false,
- expectedValue: 0,
+ initialAutoRotatePeriod: "5h",
+ newAutoRotatePeriod: 0,
+ shouldError: false,
+ expectedValue: 0,
},
"0 (string)": {
- initialAutoRotateInterval: "5h",
- newAutoRotateInterval: 0,
- shouldError: false,
- expectedValue: 0,
+ initialAutoRotatePeriod: "5h",
+ newAutoRotatePeriod: 0,
+ shouldError: false,
+ expectedValue: 0,
},
"5 seconds": {
- newAutoRotateInterval: "5s",
- shouldError: true,
+ newAutoRotatePeriod: "5s",
+ shouldError: true,
},
"5 hours": {
- newAutoRotateInterval: "5h",
- shouldError: false,
- expectedValue: 5 * time.Hour,
+ newAutoRotatePeriod: "5h",
+ shouldError: false,
+ expectedValue: 5 * time.Hour,
},
"negative value": {
- newAutoRotateInterval: "-1800s",
- shouldError: true,
+ newAutoRotatePeriod: "-1800s",
+ shouldError: true,
},
"invalid string": {
- newAutoRotateInterval: "this shouldn't work",
- shouldError: true,
+ newAutoRotatePeriod: "this shouldn't work",
+ shouldError: true,
},
}
@@ -364,11 +364,11 @@ func TestTransit_UpdateKeyConfigWithAutorotation(t *testing.T) {
keyName := hex.EncodeToString(keyNameBytes)
_, err = client.Logical().Write(fmt.Sprintf("transit/keys/%s", keyName), map[string]interface{}{
- "auto_rotate_interval": test.initialAutoRotateInterval,
+ "auto_rotate_period": test.initialAutoRotatePeriod,
})
resp, err := client.Logical().Write(fmt.Sprintf("transit/keys/%s/config", keyName), map[string]interface{}{
- "auto_rotate_interval": test.newAutoRotateInterval,
+ "auto_rotate_period": test.newAutoRotatePeriod,
})
switch {
case test.shouldError && err == nil:
@@ -385,7 +385,7 @@ func TestTransit_UpdateKeyConfigWithAutorotation(t *testing.T) {
if resp == nil {
t.Fatal("expected non-nil response")
}
- gotRaw, ok := resp.Data["auto_rotate_interval"].(json.Number)
+ gotRaw, ok := resp.Data["auto_rotate_period"].(json.Number)
if !ok {
t.Fatal("returned value is of unexpected type")
}
@@ -395,7 +395,7 @@ func TestTransit_UpdateKeyConfigWithAutorotation(t *testing.T) {
}
want := int64(test.expectedValue.Seconds())
if got != want {
- t.Fatalf("incorrect auto_rotate_interval returned, got: %d, want: %d", got, want)
+ t.Fatalf("incorrect auto_rotate_period returned, got: %d, want: %d", got, want)
}
}
})
diff --git a/builtin/logical/transit/path_keys.go b/builtin/logical/transit/path_keys.go
index b05b28e8c..bcaf326c2 100644
--- a/builtin/logical/transit/path_keys.go
+++ b/builtin/logical/transit/path_keys.go
@@ -95,7 +95,7 @@ if the key type supports public keys, this will
return the public key for the given context.`,
},
- "auto_rotate_interval": {
+ "auto_rotate_period": {
Type: framework.TypeDurationSecond,
Default: 0,
Description: `Amount of time the key should live before
@@ -132,10 +132,10 @@ func (b *backend) pathPolicyWrite(ctx context.Context, req *logical.Request, d *
keyType := d.Get("type").(string)
exportable := d.Get("exportable").(bool)
allowPlaintextBackup := d.Get("allow_plaintext_backup").(bool)
- autoRotateInterval := time.Second * time.Duration(d.Get("auto_rotate_interval").(int))
+ autoRotatePeriod := time.Second * time.Duration(d.Get("auto_rotate_period").(int))
- if autoRotateInterval != 0 && autoRotateInterval < time.Hour {
- return logical.ErrorResponse("auto rotate interval must be 0 to disable or at least an hour"), nil
+ if autoRotatePeriod != 0 && autoRotatePeriod < time.Hour {
+ return logical.ErrorResponse("auto rotate period must be 0 to disable or at least an hour"), nil
}
if !derived && convergent {
@@ -150,7 +150,7 @@ func (b *backend) pathPolicyWrite(ctx context.Context, req *logical.Request, d *
Convergent: convergent,
Exportable: exportable,
AllowPlaintextBackup: allowPlaintextBackup,
- AutoRotateInterval: autoRotateInterval,
+ AutoRotatePeriod: autoRotatePeriod,
}
switch keyType {
case "aes128-gcm96":
@@ -238,7 +238,7 @@ func (b *backend) pathPolicyRead(ctx context.Context, req *logical.Request, d *f
"supports_decryption": p.Type.DecryptionSupported(),
"supports_signing": p.Type.SigningSupported(),
"supports_derivation": p.Type.DerivationSupported(),
- "auto_rotate_interval": int64(p.AutoRotateInterval.Seconds()),
+ "auto_rotate_period": int64(p.AutoRotatePeriod.Seconds()),
},
}
diff --git a/builtin/logical/transit/path_keys_test.go b/builtin/logical/transit/path_keys_test.go
index c74b580d1..04c1d8da0 100644
--- a/builtin/logical/transit/path_keys_test.go
+++ b/builtin/logical/transit/path_keys_test.go
@@ -95,39 +95,39 @@ func TestTransit_Issue_2958(t *testing.T) {
func TestTransit_CreateKeyWithAutorotation(t *testing.T) {
tests := map[string]struct {
- autoRotateInterval interface{}
- shouldError bool
- expectedValue time.Duration
+ autoRotatePeriod interface{}
+ shouldError bool
+ expectedValue time.Duration
}{
"default (no value)": {
shouldError: false,
},
"0 (int)": {
- autoRotateInterval: 0,
- shouldError: false,
- expectedValue: 0,
+ autoRotatePeriod: 0,
+ shouldError: false,
+ expectedValue: 0,
},
"0 (string)": {
- autoRotateInterval: "0",
- shouldError: false,
- expectedValue: 0,
+ autoRotatePeriod: "0",
+ shouldError: false,
+ expectedValue: 0,
},
"5 seconds": {
- autoRotateInterval: "5s",
- shouldError: true,
+ autoRotatePeriod: "5s",
+ shouldError: true,
},
"5 hours": {
- autoRotateInterval: "5h",
- shouldError: false,
- expectedValue: 5 * time.Hour,
+ autoRotatePeriod: "5h",
+ shouldError: false,
+ expectedValue: 5 * time.Hour,
},
"negative value": {
- autoRotateInterval: "-1800s",
- shouldError: true,
+ autoRotatePeriod: "-1800s",
+ shouldError: true,
},
"invalid string": {
- autoRotateInterval: "this shouldn't work",
- shouldError: true,
+ autoRotatePeriod: "this shouldn't work",
+ shouldError: true,
},
}
@@ -160,7 +160,7 @@ func TestTransit_CreateKeyWithAutorotation(t *testing.T) {
keyName := hex.EncodeToString(keyNameBytes)
_, err = client.Logical().Write(fmt.Sprintf("transit/keys/%s", keyName), map[string]interface{}{
- "auto_rotate_interval": test.autoRotateInterval,
+ "auto_rotate_period": test.autoRotatePeriod,
})
switch {
case test.shouldError && err == nil:
@@ -177,7 +177,7 @@ func TestTransit_CreateKeyWithAutorotation(t *testing.T) {
if resp == nil {
t.Fatal("expected non-nil response")
}
- gotRaw, ok := resp.Data["auto_rotate_interval"].(json.Number)
+ gotRaw, ok := resp.Data["auto_rotate_period"].(json.Number)
if !ok {
t.Fatal("returned value is of unexpected type")
}
@@ -187,7 +187,7 @@ func TestTransit_CreateKeyWithAutorotation(t *testing.T) {
}
want := int64(test.expectedValue.Seconds())
if got != want {
- t.Fatalf("incorrect auto_rotate_interval returned, got: %d, want: %d", got, want)
+ t.Fatalf("incorrect auto_rotate_period returned, got: %d, want: %d", got, want)
}
}
})
diff --git a/changelog/14049.txt b/changelog/14049.txt
deleted file mode 100644
index 93af683bb..000000000
--- a/changelog/14049.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-```release-note:improvement
-ui: Adds multi-factor authentication support
-```
\ No newline at end of file
diff --git a/changelog/14067.txt b/changelog/14067.txt
new file mode 100644
index 000000000..bd24019ad
--- /dev/null
+++ b/changelog/14067.txt
@@ -0,0 +1,3 @@
+```release-note:improvement
+api: Define constants for X-Vault-Forward and X-Vault-Inconsistent headers
+```
diff --git a/changelog/14107.txt b/changelog/14107.txt
new file mode 100644
index 000000000..f17138c05
--- /dev/null
+++ b/changelog/14107.txt
@@ -0,0 +1,3 @@
+```release-note:bug
+auth/approle: Fix wrapping of nil errors in `login` endpoint
+```
diff --git a/command/secrets_move.go b/command/secrets_move.go
index a04ec090b..ff3331047 100644
--- a/command/secrets_move.go
+++ b/command/secrets_move.go
@@ -29,8 +29,8 @@ Usage: vault secrets move [options] SOURCE DESTINATION
secrets engine are revoked, but all configuration associated with the engine
is preserved.
- This command only works within a namespace; it cannot be used to move engines
- to different namespaces.
+ This command works within or across namespaces, both source and destination paths
+ can be prefixed with a namespace heirarchy relative to the current namespace.
WARNING! Moving an existing secrets engine will revoke any leases from the
old engine.
@@ -39,6 +39,11 @@ Usage: vault secrets move [options] SOURCE DESTINATION
$ vault secrets move secret/ generic/
+ Move the existing secrets engine at ns1/secret/ across namespaces to ns2/generic/,
+ where ns1 and ns2 are child namespaces of the current namespace:
+
+ $ vault secrets move ns1/secret/ ns2/generic/
+
` + c.Flags().Help()
return strings.TrimSpace(helpText)
@@ -84,11 +89,12 @@ func (c *SecretsMoveCommand) Run(args []string) int {
return 2
}
- if err := client.Sys().Remount(source, destination); err != nil {
+ remountResp, err := client.Sys().StartRemount(source, destination)
+ if err != nil {
c.UI.Error(fmt.Sprintf("Error moving secrets engine %s to %s: %s", source, destination, err))
return 2
}
- c.UI.Output(fmt.Sprintf("Success! Moved secrets engine %s to: %s", source, destination))
+ c.UI.Output(fmt.Sprintf("Success! Started moving secrets engine %s to %s, with migration ID %s", source, destination, remountResp.MigrationID))
return 0
}
diff --git a/command/secrets_move_test.go b/command/secrets_move_test.go
index 1af52d131..bca2a530f 100644
--- a/command/secrets_move_test.go
+++ b/command/secrets_move_test.go
@@ -3,6 +3,7 @@ package command
import (
"strings"
"testing"
+ "time"
"github.com/mitchellh/cli"
)
@@ -91,12 +92,16 @@ func TestSecretsMoveCommand_Run(t *testing.T) {
t.Errorf("expected %d to be %d", code, exp)
}
- expected := "Success! Moved secrets engine secret/ to: generic/"
+ expected := "Success! Started moving secrets engine secret/ to generic/"
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)
}
+ // Wait for the move command to complete. Ideally we'd check remount status
+ // explicitly but we don't have migration id here
+ time.Sleep(1 * time.Second)
+
mounts, err := client.Sys().ListMounts()
if err != nil {
t.Fatal(err)
diff --git a/go.mod b/go.mod
index 3689061ee..dea72614b 100644
--- a/go.mod
+++ b/go.mod
@@ -306,6 +306,7 @@ require (
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/miekg/dns v1.1.41 // indirect
+ github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a // indirect
github.com/mitchellh/hashstructure v1.0.0 // indirect
github.com/mitchellh/iochan v1.0.0 // indirect
github.com/mitchellh/pointerstructure v1.2.0 // indirect
diff --git a/go.sum b/go.sum
index fc331e840..af562945c 100644
--- a/go.sum
+++ b/go.sum
@@ -1153,6 +1153,8 @@ github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKju
github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
+github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a h1:eU8j/ClY2Ty3qdHnn0TyW3ivFoPC/0F1gQZz8yTxbbE=
+github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a/go.mod h1:v8eSC2SMp9/7FTKUncp7fH9IwPfw+ysMObcEz5FWheQ=
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
diff --git a/helper/namespace/namespace.go b/helper/namespace/namespace.go
index b6aba2bc3..6cdaed71c 100644
--- a/helper/namespace/namespace.go
+++ b/helper/namespace/namespace.go
@@ -133,3 +133,20 @@ func SplitIDFromString(input string) (string, string) {
return prefix + input[:idx], input[idx+1:]
}
+
+// MountPathDetails contains the details of a mount's location,
+// consisting of the namespace of the mount and the path of the
+// mount within the namespace
+type MountPathDetails struct {
+ Namespace *Namespace
+ MountPath string
+}
+
+func (mpd *MountPathDetails) GetRelativePath(currNs *Namespace) string {
+ subNsPath := strings.TrimPrefix(mpd.Namespace.Path, currNs.Path)
+ return subNsPath + mpd.MountPath
+}
+
+func (mpd *MountPathDetails) GetFullPath() string {
+ return mpd.Namespace.Path + mpd.MountPath
+}
diff --git a/http/sys_mount_test.go b/http/sys_mount_test.go
index 5c5bfabb5..71c454a9e 100644
--- a/http/sys_mount_test.go
+++ b/http/sys_mount_test.go
@@ -2,8 +2,10 @@ package http
import (
"encoding/json"
+ "fmt"
"reflect"
"testing"
+ "time"
"github.com/go-test/deep"
@@ -374,8 +376,24 @@ func TestSysRemount(t *testing.T) {
"from": "foo",
"to": "bar",
})
- testResponseStatus(t, resp, 204)
+ testResponseStatus(t, resp, 200)
+ // Poll until the remount succeeds
+ var remountResp map[string]interface{}
+ testResponseBody(t, resp, &remountResp)
+ vault.RetryUntil(t, 5*time.Second, func() error {
+ resp = testHttpGet(t, token, addr+"/v1/sys/remount/status/"+remountResp["migration_id"].(string))
+ testResponseStatus(t, resp, 200)
+
+ var remountStatusResp map[string]interface{}
+ testResponseBody(t, resp, &remountStatusResp)
+
+ status := remountStatusResp["data"].(map[string]interface{})["migration_info"].(map[string]interface{})["status"]
+ if status != "success" {
+ return fmt.Errorf("Expected migration status to be successful, got %q", status)
+ }
+ return nil
+ })
resp = testHttpGet(t, token, addr+"/v1/sys/mounts")
var actual map[string]interface{}
diff --git a/sdk/helper/keysutil/lock_manager.go b/sdk/helper/keysutil/lock_manager.go
index 71bfcac84..a424ca9e0 100644
--- a/sdk/helper/keysutil/lock_manager.go
+++ b/sdk/helper/keysutil/lock_manager.go
@@ -52,7 +52,7 @@ type PolicyRequest struct {
AllowPlaintextBackup bool
// How frequently the key should automatically rotate
- AutoRotateInterval time.Duration
+ AutoRotatePeriod time.Duration
}
type LockManager struct {
@@ -383,7 +383,7 @@ func (lm *LockManager) GetPolicy(ctx context.Context, req PolicyRequest, rand io
Derived: req.Derived,
Exportable: req.Exportable,
AllowPlaintextBackup: req.AllowPlaintextBackup,
- AutoRotateInterval: req.AutoRotateInterval,
+ AutoRotatePeriod: req.AutoRotatePeriod,
}
if req.Derived {
diff --git a/sdk/helper/keysutil/policy.go b/sdk/helper/keysutil/policy.go
index d4b82ab82..59afa99fd 100644
--- a/sdk/helper/keysutil/policy.go
+++ b/sdk/helper/keysutil/policy.go
@@ -374,9 +374,9 @@ type Policy struct {
// policy object.
StoragePrefix string `json:"storage_prefix"`
- // AutoRotateInterval defines how frequently the key should automatically
+ // AutoRotatePeriod defines how frequently the key should automatically
// rotate. Setting this to zero disables automatic rotation for the key.
- AutoRotateInterval time.Duration `json:"auto_rotate_interval"`
+ AutoRotatePeriod time.Duration `json:"auto_rotate_period"`
// versionPrefixCache stores caches of version prefix strings and the split
// version template.
diff --git a/ui/app/adapters/cluster.js b/ui/app/adapters/cluster.js
index f86004426..4b52350b1 100644
--- a/ui/app/adapters/cluster.js
+++ b/ui/app/adapters/cluster.js
@@ -126,19 +126,6 @@ export default ApplicationAdapter.extend({
return this.ajax(url, verb, options);
},
- mfaValidate({ mfa_request_id, mfa_constraints }) {
- const options = {
- data: {
- mfa_request_id,
- mfa_payload: mfa_constraints.reduce((obj, { selectedMethod, passcode }) => {
- obj[selectedMethod.id] = passcode ? [passcode] : [];
- return obj;
- }, {}),
- },
- };
- return this.ajax('/v1/sys/mfa/validate', 'POST', options);
- },
-
urlFor(endpoint) {
if (!ENDPOINTS.includes(endpoint)) {
throw new Error(
diff --git a/ui/app/components/auth-form.js b/ui/app/components/auth-form.js
index 8b1ed8aa8..5e7f8a7fc 100644
--- a/ui/app/components/auth-form.js
+++ b/ui/app/components/auth-form.js
@@ -18,13 +18,13 @@ const BACKENDS = supportedAuthBackends();
*
* @example ```js
* // All properties are passed in via query params.
- *
{{this.versionText.description}}
- {{#if (eq @model.config.queriesAvailable false)}} + {{#if this.licenseStartIsCurrentMonth}} +