diff --git a/CHANGELOG.md b/CHANGELOG.md
index ebbb675705..bf7a7bda25 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -49,6 +49,12 @@ Canonical reference for changes, improvements, and bugfixes for Boundary.
sessions are ongoing ([PR](https://github.com/hashicorp/boundary/pull/2612))
* sessions: Fixed a panic in a worker when a user with an active
session is deleted ([PR](https://github.com/hashicorp/boundary/pull/2629))
+* sessions: Fixed a bug where reading a session after its associated project
+ had been deleted would result in an error
+ ([PR](https://github.com/hashicorp/boundary/pull/2615))
+* config: Fixed a bug where supplying multiple KMS blocks with the same purpose
+ would silently ignore all but the last block
+ ([PR](https://github.com/hashicorp/boundary/pull/2639))
### Deprecations/Changes
diff --git a/globals/kms.go b/globals/kms.go
index 739d12e7d3..525713de5b 100644
--- a/globals/kms.go
+++ b/globals/kms.go
@@ -2,6 +2,7 @@ package globals
const (
KmsPurposeRoot = "root"
+ KmsPurposePreviousRoot = "previous-root"
KmsPurposeWorkerAuth = "worker-auth"
KmsPurposeWorkerAuthStorage = "worker-auth-storage"
KmsPurposeRecovery = "recovery"
diff --git a/go.mod b/go.mod
index 4f3d47bf43..7ae4c505f8 100644
--- a/go.mod
+++ b/go.mod
@@ -30,7 +30,7 @@ require (
github.com/hashicorp/go-bexpr v0.1.10
github.com/hashicorp/go-cleanhttp v0.5.2
github.com/hashicorp/go-hclog v1.2.2
- github.com/hashicorp/go-kms-wrapping/v2 v2.0.6-0.20220722192355-a843f53fa48d
+ github.com/hashicorp/go-kms-wrapping/v2 v2.0.6-0.20221122211539-47c893099f13
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-retryablehttp v0.7.0
github.com/hashicorp/go-rootcerts v1.0.2
@@ -92,7 +92,7 @@ require github.com/hashicorp/go-dbw v0.0.0-20220910135738-ed4505749995
require (
github.com/DATA-DOG/go-sqlmock v1.5.0
github.com/cenkalti/backoff/v4 v4.1.0
- github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220914160710-1c6d04de2431
+ github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20221122211539-47c893099f13
github.com/hashicorp/nodeenrollment v0.1.17
github.com/kelseyhightower/envconfig v1.4.0
golang.org/x/exp v0.0.0-20220921164117-439092de6870
diff --git a/go.sum b/go.sum
index 55a2f851db..b033b0c190 100644
--- a/go.sum
+++ b/go.sum
@@ -679,12 +679,12 @@ github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjh
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g=
-github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220914160710-1c6d04de2431 h1:2fmBl291s44mKzvBjF1TfqJgw1GHk97jxkGbalp4TEM=
-github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220914160710-1c6d04de2431/go.mod h1:qbLdiZhFd182vRSOlhBV/OarUcJVTOF2WklPwHs8mYQ=
+github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20221122211539-47c893099f13 h1:486QccSsJAKonkWSZmBn5GWzZCW3UV3OwMoOGUXJBO4=
+github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20221122211539-47c893099f13/go.mod h1:qbLdiZhFd182vRSOlhBV/OarUcJVTOF2WklPwHs8mYQ=
github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.2 h1:it114Kjpk79JVh6AfxFMP5oi1gb+RPzcSLiG3zF/J0w=
github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.2/go.mod h1:b9mq0bG5xJ10ZcTEQJLZ22O9y71hIt2MGTLqROA/SAM=
-github.com/hashicorp/go-kms-wrapping/v2 v2.0.6-0.20220722192355-a843f53fa48d h1:mOtPXWIp4cWKNt9S55IuYAdyUgNtCfUAEVIjcXDx59E=
-github.com/hashicorp/go-kms-wrapping/v2 v2.0.6-0.20220722192355-a843f53fa48d/go.mod h1:sDQAfwJGv25uGPZA04x87ERglCG6avnRcBT9wYoMII8=
+github.com/hashicorp/go-kms-wrapping/v2 v2.0.6-0.20221122211539-47c893099f13 h1:4YewFQjQJNAzXtahRax77IFi0emoYvPZGESfi+KCpGg=
+github.com/hashicorp/go-kms-wrapping/v2 v2.0.6-0.20221122211539-47c893099f13/go.mod h1:sDQAfwJGv25uGPZA04x87ERglCG6avnRcBT9wYoMII8=
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
diff --git a/internal/cmd/base/server_test.go b/internal/cmd/base/server_test.go
index e450029df2..07b0641370 100644
--- a/internal/cmd/base/server_test.go
+++ b/internal/cmd/base/server_test.go
@@ -2,6 +2,7 @@ package base
import (
"context"
+ "fmt"
"sync"
"testing"
@@ -10,6 +11,8 @@ import (
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/observability/event"
"github.com/hashicorp/go-hclog"
+ wrapping "github.com/hashicorp/go-kms-wrapping/v2"
+ "github.com/hashicorp/go-kms-wrapping/v2/extras/multi"
"github.com/hashicorp/go-secure-stdlib/configutil/v2"
"github.com/hashicorp/go-secure-stdlib/listenerutil"
"github.com/mitchellh/cli"
@@ -31,7 +34,7 @@ func Test_NewServer(t *testing.T) {
})
}
-func TestServer_SetupKMSes(t *testing.T) {
+func TestServer_SetupKMSes_Purposes(t *testing.T) {
tests := []struct {
name string
purposes []string
@@ -61,6 +64,41 @@ func TestServer_SetupKMSes(t *testing.T) {
globals.KmsPurposeWorkerAuthStorage, globals.KmsPurposeConfig,
},
},
+ {
+ name: "previous root without root",
+ purposes: []string{globals.KmsPurposePreviousRoot},
+ wantErrContains: fmt.Sprintf("KMS block contains '%s' without '%s'", globals.KmsPurposePreviousRoot, globals.KmsPurposeRoot),
+ },
+ {
+ name: "root and previous in the same stanza",
+ purposes: []string{globals.KmsPurposeRoot, globals.KmsPurposePreviousRoot},
+ wantErrContains: fmt.Sprintf("KMS blocks with purposes '%s' and '%s' must have different key IDs", globals.KmsPurposeRoot, globals.KmsPurposePreviousRoot),
+ },
+ {
+ name: "duplicate root purposes",
+ purposes: []string{globals.KmsPurposeRoot, globals.KmsPurposeRoot},
+ wantErrContains: fmt.Sprintf("Duplicate KMS block for purpose '%s'", globals.KmsPurposeRoot),
+ },
+ {
+ name: "duplicate previous root purposes",
+ purposes: []string{globals.KmsPurposePreviousRoot, globals.KmsPurposePreviousRoot},
+ wantErrContains: fmt.Sprintf("Duplicate KMS block for purpose '%s'", globals.KmsPurposePreviousRoot),
+ },
+ {
+ name: "duplicate worker auth purposes",
+ purposes: []string{globals.KmsPurposeWorkerAuth, globals.KmsPurposeWorkerAuth},
+ wantErrContains: fmt.Sprintf("Duplicate KMS block for purpose '%s'", globals.KmsPurposeWorkerAuth),
+ },
+ {
+ name: "duplicate worker auth storage purposes",
+ purposes: []string{globals.KmsPurposeWorkerAuthStorage, globals.KmsPurposeWorkerAuthStorage},
+ wantErrContains: fmt.Sprintf("Duplicate KMS block for purpose '%s'", globals.KmsPurposeWorkerAuthStorage),
+ },
+ {
+ name: "duplicate recovery purposes",
+ purposes: []string{globals.KmsPurposeRecovery, globals.KmsPurposeRecovery},
+ wantErrContains: fmt.Sprintf("Duplicate KMS block for purpose '%s'", globals.KmsPurposeRecovery),
+ },
}
logger := hclog.Default()
serLock := new(sync.Mutex)
@@ -102,6 +140,104 @@ func TestServer_SetupKMSes(t *testing.T) {
}
}
+func TestServer_SetupKMSes_RootMigration(t *testing.T) {
+ t.Parallel()
+ t.Run("correctly-pools-root-and-previous", func(t *testing.T) {
+ t.Parallel()
+ assert, require := assert.New(t), require.New(t)
+ logger := hclog.Default()
+ serLock := new(sync.Mutex)
+ conf := &configutil.SharedConfig{
+ Seals: []*configutil.KMS{
+ {
+ Type: "aead",
+ Purpose: []string{
+ globals.KmsPurposeRoot,
+ },
+ Config: map[string]string{
+ "key_id": "root",
+ },
+ },
+ {
+ Type: "aead",
+ Purpose: []string{
+ globals.KmsPurposePreviousRoot,
+ },
+ Config: map[string]string{
+ "key_id": "previous_root",
+ },
+ },
+ },
+ }
+ s := NewServer(&Command{Context: context.Background()})
+ require.NoError(s.SetupEventing(logger, serLock, "setup-kms-testing"))
+ err := s.SetupKMSes(s.Context, cli.NewMockUi(), &config.Config{SharedConfig: conf})
+ require.NoError(err)
+ require.NotNil(s.RootKms)
+ typ, err := s.RootKms.Type(s.Context)
+ require.NoError(err)
+ assert.Equal(wrapping.WrapperTypePooled, typ)
+ // Ensure that the root is the encryptor
+ keyId, err := s.RootKms.KeyId(s.Context)
+ require.NoError(err)
+ assert.Equal("root", keyId)
+ // Ensure that the previous root is in the wrapper too
+ assert.Equal([]string{"previous_root", "root"}, s.RootKms.(*multi.PooledWrapper).AllKeyIds())
+ })
+ t.Run("errors-on-previous-without-root", func(t *testing.T) {
+ t.Parallel()
+ require := require.New(t)
+ logger := hclog.Default()
+ serLock := new(sync.Mutex)
+ conf := &configutil.SharedConfig{
+ Seals: []*configutil.KMS{
+ {
+ Type: "aead",
+ Purpose: []string{
+ globals.KmsPurposePreviousRoot,
+ },
+ },
+ },
+ }
+ s := NewServer(&Command{Context: context.Background()})
+ require.NoError(s.SetupEventing(logger, serLock, "setup-kms-testing"))
+ err := s.SetupKMSes(s.Context, cli.NewMockUi(), &config.Config{SharedConfig: conf})
+ require.Error(err)
+ })
+ t.Run("errors-on-previous-and-root-with-same-key-id", func(t *testing.T) {
+ t.Parallel()
+ require := require.New(t)
+ logger := hclog.Default()
+ serLock := new(sync.Mutex)
+ conf := &configutil.SharedConfig{
+ Seals: []*configutil.KMS{
+ {
+ Type: "aead",
+ Purpose: []string{
+ globals.KmsPurposeRoot,
+ },
+ Config: map[string]string{
+ "key_id": "root",
+ },
+ },
+ {
+ Type: "aead",
+ Purpose: []string{
+ globals.KmsPurposePreviousRoot,
+ },
+ Config: map[string]string{
+ "key_id": "root",
+ },
+ },
+ },
+ }
+ s := NewServer(&Command{Context: context.Background()})
+ require.NoError(s.SetupEventing(logger, serLock, "setup-kms-testing"))
+ err := s.SetupKMSes(s.Context, cli.NewMockUi(), &config.Config{SharedConfig: conf})
+ require.Error(err)
+ })
+}
+
func TestServer_SetupEventing(t *testing.T) {
// DO NOT run these test in parallel since they have a dependency on
// event.sysEventer
diff --git a/internal/cmd/base/servers.go b/internal/cmd/base/servers.go
index 3bf765bf10..14afcf016f 100644
--- a/internal/cmd/base/servers.go
+++ b/internal/cmd/base/servers.go
@@ -27,11 +27,13 @@ import (
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/observability/event"
"github.com/hashicorp/boundary/internal/types/scope"
+ "github.com/hashicorp/boundary/internal/util"
kms_plugin_assets "github.com/hashicorp/boundary/plugins/kms"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/hashicorp/boundary/version"
"github.com/hashicorp/go-hclog"
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
+ "github.com/hashicorp/go-kms-wrapping/v2/extras/multi"
"github.com/hashicorp/go-multierror"
configutil "github.com/hashicorp/go-secure-stdlib/configutil/v2"
"github.com/hashicorp/go-secure-stdlib/gatedwriter"
@@ -544,6 +546,7 @@ func (b *Server) SetupKMSes(ctx context.Context, ui cli.Ui, config *config.Confi
sharedConfig := config.SharedConfig
var pluginLogger hclog.Logger
var err error
+ var previousRootKms wrapping.Wrapper
for _, kms := range sharedConfig.Seals {
for _, purpose := range kms.Purpose {
purpose = strings.ToLower(purpose)
@@ -554,7 +557,10 @@ func (b *Server) SetupKMSes(ctx context.Context, ui cli.Ui, config *config.Confi
if opts.withSkipWorkerAuthKmsInstantiation {
continue
}
- case globals.KmsPurposeRoot, globals.KmsPurposeConfig, globals.KmsPurposeWorkerAuthStorage:
+ case globals.KmsPurposeRoot,
+ globals.KmsPurposePreviousRoot,
+ globals.KmsPurposeConfig,
+ globals.KmsPurposeWorkerAuthStorage:
case globals.KmsPurposeRecovery:
if config.Controller != nil && config.DevRecoveryKey != "" {
kms.Config["key"] = config.DevRecoveryKey
@@ -615,22 +621,59 @@ func (b *Server) SetupKMSes(ctx context.Context, ui cli.Ui, config *config.Confi
kms.Purpose = origPurpose
switch purpose {
+ case globals.KmsPurposePreviousRoot:
+ if previousRootKms != nil {
+ return fmt.Errorf("Duplicate KMS block for purpose '%s'. You may need to remove all but the last KMS block for this purpose.", purpose)
+ }
+ previousRootKms = wrapper
case globals.KmsPurposeRoot:
+ if b.RootKms != nil {
+ return fmt.Errorf("Duplicate KMS block for purpose '%s'. You may need to remove all but the last KMS block for this purpose.", purpose)
+ }
b.RootKms = wrapper
case globals.KmsPurposeWorkerAuth:
+ if b.WorkerAuthKms != nil {
+ return fmt.Errorf("Duplicate KMS block for purpose '%s'. You may need to remove all but the last KMS block for this purpose.", purpose)
+ }
b.WorkerAuthKms = wrapper
case globals.KmsPurposeWorkerAuthStorage:
+ if b.WorkerAuthStorageKms != nil {
+ return fmt.Errorf("Duplicate KMS block for purpose '%s'. You may need to remove all but the last KMS block for this purpose.", purpose)
+ }
b.WorkerAuthStorageKms = wrapper
case globals.KmsPurposeRecovery:
+ if b.RecoveryKms != nil {
+ return fmt.Errorf("Duplicate KMS block for purpose '%s'. You may need to remove all but the last KMS block for this purpose.", purpose)
+ }
b.RecoveryKms = wrapper
case globals.KmsPurposeConfig:
// Do nothing, can be set in same file but not needed at runtime
+ continue
default:
return fmt.Errorf("KMS purpose of %q is unknown", purpose)
}
}
}
+ // Handle previous root KMS
+ if previousRootKms != nil {
+ if util.IsNil(b.RootKms) {
+ return fmt.Errorf("KMS block contains '%s' without '%s'", globals.KmsPurposePreviousRoot, globals.KmsPurposeRoot)
+ }
+ mw, err := multi.NewPooledWrapper(ctx, previousRootKms)
+ if err != nil {
+ return fmt.Errorf("failed to create multi wrapper: %w", err)
+ }
+ ok, err := mw.SetEncryptingWrapper(ctx, b.RootKms)
+ if err != nil {
+ return fmt.Errorf("failed to set root wrapper as active in multi wrapper: %w", err)
+ }
+ if !ok {
+ return fmt.Errorf("KMS blocks with purposes '%s' and '%s' must have different key IDs", globals.KmsPurposeRoot, globals.KmsPurposePreviousRoot)
+ }
+ b.RootKms = mw
+ }
+
// prepare a secure random reader
b.SecureRandomReader, err = configutil.CreateSecureRandomReaderFunc(config.SharedConfig, b.RootKms)
if err != nil {
diff --git a/internal/cmd/commands/server/listener_reload_test.go b/internal/cmd/commands/server/listener_reload_test.go
index a62cdaa83d..27fa5efa44 100644
--- a/internal/cmd/commands/server/listener_reload_test.go
+++ b/internal/cmd/commands/server/listener_reload_test.go
@@ -88,9 +88,7 @@ func TestServer_ReloadListener(t *testing.T) {
wd += "/test-fixtures/reload/"
td, err := os.MkdirTemp("", "boundary-test-")
- if err != nil {
- t.Fatal(err)
- }
+ require.NoError(err)
defer os.RemoveAll(td)
controllerKey := config.DevKeyGeneration()
@@ -103,13 +101,16 @@ func TestServer_ReloadListener(t *testing.T) {
UseDevAuthMethod: true,
UseDevTarget: true,
})
+ // Unset auto-created KMSes that are overwritten by config on startup
+ cmd.RootKms = nil
+ cmd.WorkerAuthKms = nil
+ cmd.RecoveryKms = nil
defer func() {
if cmd.DevDatabaseCleanupFunc != nil {
require.NoError(cmd.DevDatabaseCleanupFunc())
}
}()
- require.NoError(err)
// Setup initial certs
inBytes, err := os.ReadFile(wd + "bundle1.pem")
diff --git a/website/content/docs/concepts/security/data-encryption.mdx b/website/content/docs/concepts/security/data-encryption.mdx
index 9c9e356d44..61f1219f4f 100644
--- a/website/content/docs/concepts/security/data-encryption.mdx
+++ b/website/content/docs/concepts/security/data-encryption.mdx
@@ -31,10 +31,10 @@ Following best practices of using different encryption keys for different
purposes, Boundary has a number of encryption keys generated within each scope.
The `root` KMS key acts as a KEK (Key Encrypting Key) for the scope-specific
-KEKs (also referred to as the scope's `root` key). The scope's `root` KEK and
-the various DEKs (Data Encryption Keys) are created when a scope is created. The
-DEKs are encrypted with the scope's `root` KEK, and this is in turn encrypted
-with the KMS key marked for `root` purpose.
+KEKs (also referred to as the scope's `root` key). The scope's `root` KEK
+and the various DEKs (Data Encryption Keys) are created when a scope is created.
+The DEKs are encrypted with the scope's `root` KEK, and this is in turn
+encrypted with the KMS key marked for the `root` purpose.
The current scoped DEKs and their purposes are detailed below:
@@ -55,6 +55,60 @@ provided in this section is purely for informational purposes.
- `sessions`: This is used as a base key against which to derive
session-specific encryption keys.
+### Key version lifecycle Management
+
+You can control the lifecycles of the per-scope KEK and DEKs via the CLI or
+API using the key endpoints under the scopes section. To rotate all the keys
+in a scope, use the `rotate-keys` endpoint:
+
+```shell-session
+$ boundary scopes rotate-keys -scope-id p_A4jfDjZ9jf
+```
+
+This endpoint creates new key versions for all keys in the scope
+`p_A4jfDjZ9jf`, and makes these key versions the active key versions for
+encrypting new data. The previous key version(s) will continue to be used for
+decrypting existing data. You can use the `-rewrap` flag to immediately rewrap
+all DEK versions with the new KEK version. Otherwise, the DEK versions remain
+encrypted by the prior KEK version from when they were created.
+
+To list all keys in a scope and their versions, use the `list-keys` endpoint:
+
+```shell-session
+$ boundary scopes list-keys -scope-id p_A4jfDjZ9jf
+```
+
+To destroy a key version, use the `destroy-key-version` endpoint:
+
+```shell-session
+$ boundary scopes destroy-key-version -scope-id p_A4jfDjZ9jf -key-version-id kdkv_tr6ZN8opYr
+```
+
+The latest key version cannot be destroyed, so if you want to destroy the
+latest key version, you will need to call the `rotate-keys` endpoint first,
+to create a new set of key versions. The `oplog` purpose key versions also
+cannot be destroyed at this time.
+
+Destroying a key version sometimes requires background work before it can be
+completed. This is because DEK versions that currently encrypt data
+necessitate re-encrypting that data with the latest DEK version for each
+purpose before they can be deleted. You can monitor the progress of this
+re-encryption job via the `list-key-destruction-jobs` endpoint:
+
+```shell-session
+$ boundary scopes list-key-version-destruction-jobs -scope-id p_A4jfDjZ9jf
+```
+
+Once the job disappears from this list, the associated key version will have
+been destroyed and any existing data will have been re-encrypted.
+
+## The `previous-root` KMS key OSS Only
+
+The `previous-root` KMS key is used when migrating to a new `root` key. Adding
+the `previous-root` KMS key to your configuration informs the Controller to use
+it for decrypting the existing information in the database, allowing you to
+rotate and rewrap the KEKs to complete the migration to the new root key.
+
## The `worker-auth` KMS Key OSS Only
The `worker-auth` KMS key is a key shared by the Controller and Worker in order
diff --git a/website/content/docs/configuration/kms/aead.mdx b/website/content/docs/configuration/kms/aead.mdx
index bd18e520d3..0b79d1b11a 100644
--- a/website/content/docs/configuration/kms/aead.mdx
+++ b/website/content/docs/configuration/kms/aead.mdx
@@ -21,7 +21,8 @@ kms "aead" {
}
```
-- `purpose` - Purpose of this KMS, acceptable values are: `worker-auth`, `root`, `recovery`, or `config`.
+- `purpose` - Purpose of this KMS, acceptable values are: `worker-auth`, `worker-auth-storage`,
+ `root`, `previous-root`, `recovery`, or `config`.
- `aead_type` - The type of encryption this KMS uses. Currently only `aes-gcm` is implemented.
diff --git a/website/content/docs/configuration/kms/alicloudkms.mdx b/website/content/docs/configuration/kms/alicloudkms.mdx
index 38b8497362..bc8c13a6a0 100644
--- a/website/content/docs/configuration/kms/alicloudkms.mdx
+++ b/website/content/docs/configuration/kms/alicloudkms.mdx
@@ -31,7 +31,8 @@ kms "alicloudkms" {
These parameters apply to the `kms` stanza in the Boundary configuration file:
-- `purpose` - Purpose of this KMS, acceptable values are: `worker-auth`, `root`, `recovery`, or `config`.
+- `purpose` - Purpose of this KMS, acceptable values are: `worker-auth`, `worker-auth-storage`,
+ `root`, `previous-root`, `recovery`, or `config`.
- `region` `(string: "us-east-1")`: The AliCloud region where the encryption key
lives. May also be specified by the `ALICLOUD_REGION`
diff --git a/website/content/docs/configuration/kms/awskms.mdx b/website/content/docs/configuration/kms/awskms.mdx
index b10d26a827..e4e8d94c41 100644
--- a/website/content/docs/configuration/kms/awskms.mdx
+++ b/website/content/docs/configuration/kms/awskms.mdx
@@ -30,8 +30,8 @@ kms "awskms" {
These parameters apply to the `kms` stanza in the Boundary configuration file:
-- `purpose` - Purpose of this KMS, acceptable values are: `worker-auth`, `root`,
- `recovery`, or `config`.
+- `purpose` - Purpose of this KMS, acceptable values are: `worker-auth`, `worker-auth-storage`,
+ `root`, `previous-root`, `recovery`, or `config`.
- `region` `(string: "us-east-1")`: The AWS region where the encryption key
lives. If not provided, may be populated from the `AWS_REGION` or
diff --git a/website/content/docs/configuration/kms/azurekeyvault.mdx b/website/content/docs/configuration/kms/azurekeyvault.mdx
index f240695da8..81f7374a12 100644
--- a/website/content/docs/configuration/kms/azurekeyvault.mdx
+++ b/website/content/docs/configuration/kms/azurekeyvault.mdx
@@ -32,7 +32,8 @@ kms "azurekeyvault" {
These parameters apply to the `kms` stanza in the Vault configuration file:
-- `purpose` - Purpose of this KMS, acceptable values are: `worker-auth`, `root`, `recovery`, or `config`.
+- `purpose` - Purpose of this KMS, acceptable values are: `worker-auth`, `worker-auth-storage`,
+ `root`, `previous-root`, `recovery`, or `config`.
- `tenant_id` `(string: )`: The tenant id for the Azure Active Directory organization. May
also be specified by the `AZURE_TENANT_ID` environment variable.
diff --git a/website/content/docs/configuration/kms/gcpckms.mdx b/website/content/docs/configuration/kms/gcpckms.mdx
index c46cbd0f09..059e2456c6 100644
--- a/website/content/docs/configuration/kms/gcpckms.mdx
+++ b/website/content/docs/configuration/kms/gcpckms.mdx
@@ -31,7 +31,8 @@ kms "gcpckms" {
These parameters apply to the `kms` stanza in the Boundary configuration file:
-- `purpose` - Purpose of this KMS, acceptable values are: `worker-auth`, `root`, `recovery`, or `config`.
+- `purpose` - Purpose of this KMS, acceptable values are: `worker-auth`, `worker-auth-storage`,
+ `root`, `previous-root`, `recovery`, or `config`.
- `credentials` `(string: )`: The path to the credentials JSON file
to use. May be also specified by the `GOOGLE_CREDENTIALS` or
diff --git a/website/content/docs/configuration/kms/ocikms.mdx b/website/content/docs/configuration/kms/ocikms.mdx
index 460d727904..28de59a24f 100644
--- a/website/content/docs/configuration/kms/ocikms.mdx
+++ b/website/content/docs/configuration/kms/ocikms.mdx
@@ -30,7 +30,8 @@ kms "ocikms" {
These parameters apply to the `kms` stanza in the Boundary configuration file:
-- `purpose` - Purpose of this KMS, acceptable values are: `worker-auth`, `root`, `recovery`, or `config`.
+- `purpose` - Purpose of this KMS, acceptable values are: `worker-auth`, `worker-auth-storage`,
+ `root`, `previous-root`, `recovery`, or `config`.
- `key_id` `(string: )`: The OCI KMS key ID to use.
- `crypto_endpoint` `(string: )`: The OCI KMS cryptographic endpoint (or data plane endpoint)
to be used to make OCI KMS encryption/decryption requests.
diff --git a/website/content/docs/configuration/kms/transit.mdx b/website/content/docs/configuration/kms/transit.mdx
index 29522d0d9f..d18aca5a0b 100644
--- a/website/content/docs/configuration/kms/transit.mdx
+++ b/website/content/docs/configuration/kms/transit.mdx
@@ -41,8 +41,8 @@ kms "transit" {
These parameters apply to the `kms` stanza in the Vault configuration file:
-- `purpose` - Purpose of this KMS, acceptable values are: `worker-auth`, `root`,
- `recovery`, or `config`.
+- `purpose` - Purpose of this KMS, acceptable values are: `worker-auth`, `worker-auth-storage`,
+ `root`, `previous-root`, `recovery`, or `config`.
- `address` `(string: )`: The full address to the Vault cluster.
This may also be specified by the `VAULT_ADDR` environment variable.