From 2c16be25e3906a41eeb48ba929efc2f12e679348 Mon Sep 17 00:00:00 2001 From: Mike Palmiotto Date: Wed, 31 Aug 2022 16:11:14 -0400 Subject: [PATCH] Add deprecation status to auth/secrets list (#16849) * auth: Add Deprecation Status to auth list -detailed * secrets: Add Deprecation Status to secrets list -detailed * Add changelog entry for deprecation status list --- api/sys_mounts.go | 1 + changelog/16849.txt | 15 ++++++++ command/auth_list.go | 5 ++- command/secrets_list.go | 5 ++- command/secrets_list_test.go | 2 +- http/sys_auth_test.go | 8 ++-- vault/logical_system.go | 22 +++++++---- vault/mount.go | 37 +++++++++++++++++++ vault/testing.go | 11 +++++- website/content/docs/commands/auth/list.mdx | 16 ++++++-- .../content/docs/commands/secrets/list.mdx | 18 ++++++--- 11 files changed, 114 insertions(+), 26 deletions(-) create mode 100644 changelog/16849.txt diff --git a/api/sys_mounts.go b/api/sys_mounts.go index 665376f28..522f87e6e 100644 --- a/api/sys_mounts.go +++ b/api/sys_mounts.go @@ -286,6 +286,7 @@ type MountOutput struct { RunningVersion string `json:"running_version"` Sha string `json:"sha"` RunningSha string `json:"running_sha"` + DeprecationStatus string `json:"deprecation_status" mapstructure:"deprecation_status"` } type MountConfigOutput struct { diff --git a/changelog/16849.txt b/changelog/16849.txt new file mode 100644 index 000000000..a320eedcd --- /dev/null +++ b/changelog/16849.txt @@ -0,0 +1,15 @@ +```release-note:change +auth: `GET /sys/auth` endpoint now returns an additional `deprecation_status` field in the response data for builtins. +``` +```release-note:change +auth: `GET /sys/auth/:name` endpoint now returns an additional `deprecation_status` field in the response data for builtins. +``` +```release-note:change +secrets: `GET /sys/mounts` endpoint now returns an additional `deprecation_status` field in the response data for builtins. +``` +```release-note:change +secrets: `GET /sys/mounts/:name` endpoint now returns an additional `deprecation_status` field in the response data for builtins. +``` +```release-note:improvement +cli: `auth` and `secrets` list `-detailed` commands now show Deprecation Status for builtin plugins. +``` diff --git a/command/auth_list.go b/command/auth_list.go index 8da66309b..2edb8cf3d 100644 --- a/command/auth_list.go +++ b/command/auth_list.go @@ -145,7 +145,7 @@ func (c *AuthListCommand) detailedMounts(auths map[string]*api.AuthMount) []stri } } - out := []string{"Path | Plugin | Accessor | Default TTL | Max TTL | Token Type | Replication | Seal Wrap | External Entropy Access | Options | Description | UUID | Version"} + out := []string{"Path | Plugin | Accessor | Default TTL | Max TTL | Token Type | Replication | Seal Wrap | External Entropy Access | Options | Description | UUID | Version | Deprecation Status"} for _, path := range paths { mount := auths[path] @@ -162,7 +162,7 @@ func (c *AuthListCommand) detailedMounts(auths map[string]*api.AuthMount) []stri pluginName = mount.Config.PluginName } - out = append(out, fmt.Sprintf("%s | %s | %s | %s | %s | %s | %s | %t | %v | %s | %s | %s | %s", + out = append(out, fmt.Sprintf("%s | %s | %s | %s | %s | %s | %s | %t | %v | %s | %s | %s | %s | %s", path, pluginName, mount.Accessor, @@ -176,6 +176,7 @@ func (c *AuthListCommand) detailedMounts(auths map[string]*api.AuthMount) []stri mount.Description, mount.UUID, mount.Version, + mount.DeprecationStatus, )) } diff --git a/command/secrets_list.go b/command/secrets_list.go index e9ce1ff31..e67a1e821 100644 --- a/command/secrets_list.go +++ b/command/secrets_list.go @@ -145,7 +145,7 @@ func (c *SecretsListCommand) detailedMounts(mounts map[string]*api.MountOutput) } } - out := []string{"Path | Plugin | Accessor | Default TTL | Max TTL | Force No Cache | Replication | Seal Wrap | External Entropy Access | Options | Description | UUID "} + out := []string{"Path | Plugin | Accessor | Default TTL | Max TTL | Force No Cache | Replication | Seal Wrap | External Entropy Access | Options | Description | UUID | Deprecation Status"} for _, path := range paths { mount := mounts[path] @@ -162,7 +162,7 @@ func (c *SecretsListCommand) detailedMounts(mounts map[string]*api.MountOutput) pluginName = mount.Config.PluginName } - out = append(out, fmt.Sprintf("%s | %s | %s | %s | %s | %t | %s | %t | %v | %s | %s | %s", + out = append(out, fmt.Sprintf("%s | %s | %s | %s | %s | %t | %s | %t | %v | %s | %s | %s | %s", path, pluginName, mount.Accessor, @@ -175,6 +175,7 @@ func (c *SecretsListCommand) detailedMounts(mounts map[string]*api.MountOutput) mount.Options, mount.Description, mount.UUID, + mount.DeprecationStatus, )) } diff --git a/command/secrets_list_test.go b/command/secrets_list_test.go index 9edb62820..1aeee5bf6 100644 --- a/command/secrets_list_test.go +++ b/command/secrets_list_test.go @@ -42,7 +42,7 @@ func TestSecretsListCommand_Run(t *testing.T) { { "detailed", []string{"-detailed"}, - "Default TTL", + "Deprecation Status", 0, }, } diff --git a/http/sys_auth_test.go b/http/sys_auth_test.go index 40f41dc78..53001b0f0 100644 --- a/http/sys_auth_test.go +++ b/http/sys_auth_test.go @@ -96,7 +96,7 @@ func TestSysEnableAuth(t *testing.T) { TestServerAuth(t, addr, token) resp := testHttpPost(t, token, addr+"/v1/sys/auth/foo", map[string]interface{}{ - "type": "noop", + "type": "approle", "description": "foo", }) testResponseStatus(t, resp, 204) @@ -114,8 +114,9 @@ func TestSysEnableAuth(t *testing.T) { "data": map[string]interface{}{ "foo/": map[string]interface{}{ "description": "foo", - "type": "noop", + "type": "approle", "external_entropy_access": false, + "deprecation_status": "supported", "config": map[string]interface{}{ "default_lease_ttl": json.Number("0"), "max_lease_ttl": json.Number("0"), @@ -151,8 +152,9 @@ func TestSysEnableAuth(t *testing.T) { }, "foo/": map[string]interface{}{ "description": "foo", - "type": "noop", + "type": "approle", "external_entropy_access": false, + "deprecation_status": "supported", "config": map[string]interface{}{ "default_lease_ttl": json.Number("0"), "max_lease_ttl": json.Number("0"), diff --git a/vault/logical_system.go b/vault/logical_system.go index 7b768ea26..e5a63b8ab 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -879,7 +879,7 @@ func (b *SystemBackend) handleRekeyDeleteRecovery(ctx context.Context, req *logi return b.handleRekeyDelete(ctx, req, data, true) } -func mountInfo(entry *MountEntry) map[string]interface{} { +func (b *SystemBackend) mountInfo(ctx context.Context, entry *MountEntry) map[string]interface{} { info := map[string]interface{}{ "type": entry.Type, "description": entry.Description, @@ -923,6 +923,11 @@ func mountInfo(entry *MountEntry) map[string]interface{} { entryConfig["token_type"] = entry.Config.TokenType.String() } + // Add deprecation status only if it exists + builtinType := b.Core.builtinTypeFromMountEntry(ctx, entry) + if status, ok := b.Core.builtinRegistry.DeprecationStatus(entry.Type, builtinType); ok { + info["deprecation_status"] = status.String() + } info["config"] = entryConfig return info @@ -957,7 +962,8 @@ func (b *SystemBackend) handleMountTable(ctx context.Context, req *logical.Reque } // Populate mount info - info := mountInfo(entry) + info := b.mountInfo(ctx, entry) + resp.Data[entry.Path] = info } @@ -1159,7 +1165,7 @@ func (b *SystemBackend) handleReadMount(ctx context.Context, req *logical.Reques } return &logical.Response{ - Data: mountInfo(entry), + Data: b.mountInfo(ctx, entry), }, nil } @@ -2149,7 +2155,7 @@ func (b *SystemBackend) handleAuthTable(ctx context.Context, req *logical.Reques continue } - info := mountInfo(entry) + info := b.mountInfo(ctx, entry) resp.Data[entry.Path] = info } @@ -2183,7 +2189,7 @@ func (b *SystemBackend) handleReadAuth(ctx context.Context, req *logical.Request } return &logical.Response{ - Data: mountInfo(entry), + Data: b.mountInfo(ctx, entry), }, nil } @@ -3849,7 +3855,7 @@ func (b *SystemBackend) pathInternalUIMountsRead(ctx context.Context, req *logic if ns.ID == entry.NamespaceID && hasAccess(ctx, entry) { if isAuthed { // If this is an authed request return all the mount info - secretMounts[entry.Path] = mountInfo(entry) + secretMounts[entry.Path] = b.mountInfo(ctx, entry) } else { secretMounts[entry.Path] = map[string]interface{}{ "type": entry.Type, @@ -3876,7 +3882,7 @@ func (b *SystemBackend) pathInternalUIMountsRead(ctx context.Context, req *logic if ns.ID == entry.NamespaceID && hasAccess(ctx, entry) { if isAuthed { // If this is an authed request return all the mount info - authMounts[entry.Path] = mountInfo(entry) + authMounts[entry.Path] = b.mountInfo(ctx, entry) } else { authMounts[entry.Path] = map[string]interface{}{ "type": entry.Type, @@ -3934,7 +3940,7 @@ func (b *SystemBackend) pathInternalUIMountRead(ctx context.Context, req *logica return errResp, logical.ErrPermissionDenied } resp := &logical.Response{ - Data: mountInfo(me), + Data: b.mountInfo(ctx, me), } resp.Data["path"] = me.Path diff --git a/vault/mount.go b/vault/mount.go index 7906ddfad..c0e143738 100644 --- a/vault/mount.go +++ b/vault/mount.go @@ -665,6 +665,43 @@ func (c *Core) mountInternal(ctx context.Context, entry *MountEntry, updateStora return nil } +// builtinTypeFromMountEntry attempts to find a builtin PluginType associated +// with the specified MountEntry. Returns consts.PluginTypeUnknown if not found. +func (c *Core) builtinTypeFromMountEntry(ctx context.Context, entry *MountEntry) consts.PluginType { + if c.builtinRegistry == nil || entry == nil { + return consts.PluginTypeUnknown + } + + builtinPluginType := func(name string, pluginType consts.PluginType) (consts.PluginType, bool) { + plugin, err := c.pluginCatalog.Get(ctx, name, pluginType, "") + if err == nil && plugin != nil && plugin.Builtin { + return plugin.Type, true + } + return consts.PluginTypeUnknown, false + } + + // auth plugins have their own dedicated mount table + if pluginType, err := consts.ParsePluginType(entry.Table); err == nil { + if builtinType, ok := builtinPluginType(entry.Type, pluginType); ok { + return builtinType + } + } + + // Check for possible matches + var builtinTypes []consts.PluginType + for _, pluginType := range [...]consts.PluginType{consts.PluginTypeSecrets, consts.PluginTypeDatabase} { + if builtinType, ok := builtinPluginType(entry.Type, pluginType); ok { + builtinTypes = append(builtinTypes, builtinType) + } + } + + if len(builtinTypes) == 1 { + return builtinTypes[0] + } + + return consts.PluginTypeUnknown +} + // Unmount is used to unmount a path. The boolean indicates whether the mount // was found. func (c *Core) unmount(ctx context.Context, path string) error { diff --git a/vault/testing.go b/vault/testing.go index c07b3b461..259b1fab3 100644 --- a/vault/testing.go +++ b/vault/testing.go @@ -2290,11 +2290,20 @@ func (m *mockBuiltinRegistry) Keys(pluginType consts.PluginType) []string { } func (m *mockBuiltinRegistry) Contains(name string, pluginType consts.PluginType) bool { + for _, key := range m.Keys(pluginType) { + if key == name { + return true + } + } return false } func (m *mockBuiltinRegistry) DeprecationStatus(name string, pluginType consts.PluginType) (consts.DeprecationStatus, bool) { - return consts.Supported, true + if m.Contains(name, pluginType) { + return consts.Supported, true + } + + return consts.Unknown, false } type NoopAudit struct { diff --git a/website/content/docs/commands/auth/list.mdx b/website/content/docs/commands/auth/list.mdx index fc92b224b..cba7803a2 100644 --- a/website/content/docs/commands/auth/list.mdx +++ b/website/content/docs/commands/auth/list.mdx @@ -11,6 +11,13 @@ description: |- The `auth list` command lists the auth methods enabled. The output lists the enabled auth methods and options for those methods. +## Deprecation Status Column + +As of 1.12, all builtin auth engines will have an associated Deprecation +Status. This status will be reflected in the `Deprecation Status` column, seen +below. All auth engines which are not provided by builtin plugins will show a +`Deprecation Status` of "n/a". + ## Examples List all auth methods: @@ -27,10 +34,11 @@ List detailed auth method information: ```shell-session $ vault auth list -detailed -Path Type Accessor Plugin Default TTL Max TTL Replication Description ----- ---- -------- ------ ----------- ------- ----------- ----------- -token/ token auth_token_b2166f9e n/a system system replicated token based credentials -userpass/ userpass auth_userpass_eea6507e n/a system system replicated n/a +Path Plugin Accessor Default TTL Max TTL Token Type Replication Seal Wrap External Entropy Access Options Description UUID Deprecation Status +---- ------ -------- ----------- ------- ---------- ----------- --------- ----------------------- ------- ----------- ---- ------------------ +app-id/ app-id auth_app-id_c88ad56f system system default-service replicated false false map[] n/a a7c702b4-0dba-02b6-483c-2fd6be33240a pending removal +approle/ approle auth_approle_95df932e system system default-service replicated false false map[] n/a 931df9d1-8737-b7dc-4ca2-3e0e892fce92 supported +token/ token auth_token_aafab997 system system default-service replicated false false map[] token based credentials 6eb5db7b-ac7f-4304-1f52-9b802c6f06c1 n/a ``` ## Usage diff --git a/website/content/docs/commands/secrets/list.mdx b/website/content/docs/commands/secrets/list.mdx index f1d71a758..82a3b88af 100644 --- a/website/content/docs/commands/secrets/list.mdx +++ b/website/content/docs/commands/secrets/list.mdx @@ -15,6 +15,13 @@ This command also outputs information about the enabled path including configured TTLs and human-friendly descriptions. A TTL of "system" indicates that the system default is in use. +## Deprecation Status Column + +As of 1.12, all builtin secrets engines will have an associated Deprecation +Status. This status will be reflected in the `Deprecation Status` column, seen +below. All secrets engines which are not provided by builtin plugins will show a +`Deprecation Status` of "n/a". + ## Examples List all enabled secrets engines: @@ -32,11 +39,12 @@ List all enabled secrets engines with detailed output: ```shell-session $ vault secrets list -detailed -Path Type Accessor Plugin Default TTL Max TTL Force No Cache Replication Description ----- ---- -------- ------ ----------- ------- -------------- ----------- ----------- -cubbyhole/ cubbyhole cubbyhole_10fbb584 n/a n/a n/a false local per-token private secret storage -secret/ kv kv_167ce199 n/a system system false replicated key/value secret storage -sys/ system system_a9fd745d n/a n/a n/a false replicated system endpoints used for control, policy and debugging +Path Plugin Accessor Default TTL Max TTL Force No Cache Replication Seal Wrap External Entropy Access Options Description UUID Deprecation Status +---- ------ -------- ----------- ------- -------------- ----------- --------- ----------------------- ------- ----------- ---- ------------------ +cubbyhole/ cubbyhole cubbyhole_b16d1bc0 n/a n/a false local false false map[] per-token private secret storage 8c64d56b-9d46-d667-1155-a8c1a83a5d01 n/a +identity/ identity identity_3d67c936 system system false replicated false false map[] identity store 5aa1e59c-33b5-9dec-05d6-c80c9a800557 n/a +postgresql/ postgresql postgresql_f0a54308 system system false replicated false false map[] n/a 8cdc1d2d-0713-eaa6-17e3-49790a60650b deprecated +sys/ system system_c86bd362 n/a n/a false replicated true false map[] system endpoints used for control, policy and debugging e3193999-0875-d38d-3458-21d9f2762c80 n/a ``` ## Usage