VAULT-42143 Update role and KV counting to official only (#12057) (#12288)

* VAULT-42143 Update role counting to official only

* KV thing

* remove old func

* update name

Co-authored-by: Violet Hynes <violet.hynes@hashicorp.com>
This commit is contained in:
Vault Automation 2026-02-10 17:09:20 -05:00 committed by GitHub
parent d5b3c8839c
commit aeaed4484c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 176 additions and 44 deletions

View file

@ -56,7 +56,7 @@ func (c *Core) UpdateMaxThirdPartyPluginCounts(ctx context.Context, currentMonth
if err != nil {
return 0, err
}
currentThirdPartyPluginCounts, err := c.ListExternalSecretPlugins(ctx)
currentThirdPartyPluginCounts, err := c.ListDeduplicatedExternalSecretPlugins(ctx)
if err != nil {
return 0, err
}
@ -157,7 +157,7 @@ func (c *Core) UpdateMaxKvCounts(ctx context.Context, localPathPrefix string, cu
local := localPathPrefix == billing.LocalPrefix
// Get the current count of kv version 1 secrets
currentKvCounts, err := c.GetKvUsageMetricsByNamespace(ctx, "1", "", local, !local)
currentKvCounts, err := c.GetKvUsageMetricsByNamespace(ctx, "1", "", local, !local, false)
if err != nil {
c.logger.Error("error getting count of kv version 1 secrets", "error", err)
return 0, err
@ -165,7 +165,7 @@ func (c *Core) UpdateMaxKvCounts(ctx context.Context, localPathPrefix string, cu
totalKvCounts := getTotalSecretsAcrossAllNamespaces(currentKvCounts)
// Get the current count of kv version 2 secrets
currentKvCounts, err = c.GetKvUsageMetricsByNamespace(ctx, "2", "", local, !local)
currentKvCounts, err = c.GetKvUsageMetricsByNamespace(ctx, "2", "", local, !local, false)
if err != nil {
c.logger.Error("error getting current count of kv version 2 secrets", "error", err)
return 0, err
@ -212,7 +212,7 @@ func (c *Core) UpdateMaxRoleCounts(ctx context.Context, localPathPrefix string,
defer c.consumptionBilling.BillingStorageLock.Unlock()
local := localPathPrefix == billing.LocalPrefix
currentRoleCounts := c.getRoleCountsInternal(local, !local)
currentRoleCounts := c.getRoleCountsInternal(local, !local, true)
maxRoleCounts, err := c.getStoredRoleCountsLocked(ctx, localPathPrefix, currentMonth)
if maxRoleCounts == nil {

View file

@ -18,6 +18,7 @@ import (
"github.com/hashicorp/vault/limits"
"github.com/hashicorp/vault/physical/raft"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/helper/pluginutil"
"github.com/hashicorp/vault/sdk/logical"
)
@ -419,6 +420,59 @@ type kvMount struct {
RunningPluginVersion string
}
// findOfficialKvMounts differs from findKvMounts in that it will ignore any sideloaded
// or externally compiled KV mounts that are still of type KV.
// It's a simple function that's slightly reimplemented to prevent needing a context
// in findKvMounts.
func (c *Core) findOfficialKvMounts(ctx context.Context) []*kvMount {
mounts := make([]*kvMount, 0)
c.mountsLock.RLock()
defer c.mountsLock.RUnlock()
// we don't grab the statelock, so this code might run during or after the seal process.
// Therefore, we need to check if c.mounts is nil. If we do not, this will panic when
// run after seal.
if c.mounts == nil {
return mounts
}
for _, entry := range c.mounts.Entries {
if entry.Type == pluginconsts.SecretEngineKV || entry.Type == pluginconsts.SecretEngineGeneric {
version, ok := entry.Options["version"]
if !ok || version == "" {
version = "1"
}
pluginName := getAdjustedPluginType(entry)
if pluginName == "" {
continue
}
pluginVersion := entry.RunningVersion
runner, err := c.pluginCatalog.Get(ctx, pluginName, consts.PluginTypeSecrets, pluginVersion)
if err != nil {
continue
}
if !(isOfficialOrBuiltin(runner)) {
continue
}
mounts = append(mounts, &kvMount{
Namespace: entry.namespace,
MountPoint: entry.Path,
MountAccessor: entry.Accessor,
Version: version,
NumSecrets: 0,
Local: entry.Local,
RunningPluginVersion: entry.RunningVersion,
})
}
}
return mounts
}
func (c *Core) findKvMounts() []*kvMount {
mounts := make([]*kvMount, 0)
@ -788,22 +842,24 @@ type RoleCounts struct {
TerraformCloudDynamicRoles int `json:"terraformcloud_dynamic_roles"`
}
func (c *Core) getRoleCountsInternal(includeLocal bool, includeReplicated bool) *RoleCounts {
// getRoleCountsInternal gets the role counts for plugins.
// includeLocal determines if local mounts are included
// includeReplicated determines if replicated mounts are included
// officialPluginsOnly determines if this function should include only plugins that are official,
// which would exclude, for example, a custom built version of these plugins.
func (c *Core) getRoleCountsInternal(includeLocal bool, includeReplicated bool, officialPluginsOnly bool) *RoleCounts {
if c.Sealed() {
c.logger.Debug("core is sealed, cannot access mounts table")
return nil
}
c.mountsLock.RLock()
defer c.mountsLock.RUnlock()
ctx := namespace.RootContext(c.activeContext)
apiList := func(entry *MountEntry, apiPath string) []string {
listRequest := &logical.Request{
Operation: logical.ListOperation,
Path: entry.namespace.Path + entry.Path + apiPath,
}
ctx := namespace.ContextWithNamespace(c.activeContext, namespace.RootNamespace)
resp, err := c.router.Route(ctx, listRequest)
if err != nil || resp == nil {
return nil
@ -819,6 +875,9 @@ func (c *Core) getRoleCountsInternal(includeLocal bool, includeReplicated bool)
return keys
}
c.mountsLock.RLock()
defer c.mountsLock.RUnlock()
var roles RoleCounts
for _, entry := range c.mounts.Entries {
if !entry.Local && !includeReplicated {
@ -827,9 +886,26 @@ func (c *Core) getRoleCountsInternal(includeLocal bool, includeReplicated bool)
if entry.Local && !includeLocal {
continue
}
secretType := entry.Type
switch secretType {
pluginName := getAdjustedPluginType(entry)
if pluginName == "" {
continue
}
pluginVersion := entry.RunningVersion
if officialPluginsOnly {
runner, err := c.pluginCatalog.Get(ctx, pluginName, consts.PluginTypeSecrets, pluginVersion)
if err != nil {
continue
}
if !(isOfficialOrBuiltin(runner)) {
continue
}
}
switch pluginName {
case pluginconsts.SecretEngineAWS:
dynamicRoles := apiList(entry, "roles")
roles.AWSDynamicRoles += len(dynamicRoles)
@ -902,21 +978,24 @@ func (c *Core) getRoleCountsInternal(includeLocal bool, includeReplicated bool)
}
func (c *Core) GetRoleCounts() *RoleCounts {
return c.getRoleCountsInternal(true, true)
return c.getRoleCountsInternal(true, true, false)
}
func (c *Core) GetRoleCountsForCluster() *RoleCounts {
return c.getRoleCountsInternal(true, c.isPrimary())
return c.getRoleCountsInternal(true, c.isPrimary(), false)
}
// GetKvUsageMetrics returns a map of namespace paths to KV secret counts.
func (c *Core) GetKvUsageMetrics(ctx context.Context, kvVersion string) (map[string]int, error) {
return c.GetKvUsageMetricsByNamespace(ctx, kvVersion, "", true, true)
return c.GetKvUsageMetricsByNamespace(ctx, kvVersion, "", true, true, true)
}
// GetKvUsageMetricsByNamespace returns a map of namespace paths to KV secret counts within a specific namespace.
func (c *Core) GetKvUsageMetricsByNamespace(ctx context.Context, kvVersion string, nsPath string, includeLocal bool, includeReplicated bool) (map[string]int, error) {
func (c *Core) GetKvUsageMetricsByNamespace(ctx context.Context, kvVersion string, nsPath string, includeLocal bool, includeReplicated bool, includeUnofficial bool) (map[string]int, error) {
mounts := c.findKvMounts()
if !includeUnofficial {
mounts = c.findOfficialKvMounts(ctx)
}
results := make(map[string]int)
if kvVersion == "1" || kvVersion == "2" {
@ -964,27 +1043,28 @@ func (c *Core) GetKvUsageMetricsByNamespace(ctx context.Context, kvVersion strin
return results, nil
}
// ListExternalSecretPlugins returns the enabled secret engines
// that are not builtin and not official-tier.
//
// This is useful for identifying "third-party" secrets mounts (e.g. community or
// partner tier external plugins) while excluding builtins and official HashiCorp
// plugins.
// Note: This will include all mounts that have been built externally (even if they are
// Hashicorp owned). This will happen if the plugin was built from a Github repo or from an
// artifact.
func (c *Core) ListExternalSecretPlugins(ctx context.Context) ([]*MountEntry, error) {
// isOfficialOrBuiltin determines if a plugin is official based on its runner.
// We treat it as official if runner is nil to avoid overcharging, but ensure
// that it is properly scanned if it _is_ an official mount.
func isOfficialOrBuiltin(runner *pluginutil.PluginRunner) bool {
return runner == nil || runner.Builtin || runner.Tier == consts.PluginTierOfficial
}
// ListOfficialAndExternalSecretPlugins gets a list of all secret plugins, official and external.
// The union of both sets is the set of all secret plugins.
// Returns a list of official plugins, external plugins, and error, in that order.
func (c *Core) ListOfficialAndExternalSecretPlugins(ctx context.Context) ([]*MountEntry, []*MountEntry, error) {
if c == nil || c.pluginCatalog == nil {
return nil, fmt.Errorf("core or plugin catalog is nil")
return nil, nil, fmt.Errorf("core or plugin catalog is nil")
}
mounts, err := c.ListMounts()
if err != nil {
return nil, fmt.Errorf("error listing mounts: %w", err)
return nil, nil, fmt.Errorf("error listing mounts: %w", err)
}
seen := make(map[string]struct{})
var result []*MountEntry
var official []*MountEntry
var external []*MountEntry
for _, entry := range mounts {
if entry == nil {
continue
@ -996,10 +1076,73 @@ func (c *Core) ListExternalSecretPlugins(ctx context.Context) ([]*MountEntry, er
continue
}
pluginName := entry.Type
if pluginName == mountTypePlugin && entry.Config.PluginName != "" {
pluginName = entry.Config.PluginName
pluginName := getAdjustedPluginType(entry)
if pluginName == "" {
continue
}
pluginVersion := entry.RunningVersion
runner, err := c.pluginCatalog.Get(ctx, pluginName, consts.PluginTypeSecrets, pluginVersion)
if err != nil {
continue
}
if isOfficialOrBuiltin(runner) {
official = append(official, entry)
} else {
external = append(external, entry)
}
}
return official, external, nil
}
// ListOfficialSecretPlugins gets a list of all 'official'/builtin secret plugins.
func (c *Core) ListOfficialSecretPlugins(ctx context.Context) ([]*MountEntry, error) {
internalPlugins, _, err := c.ListOfficialAndExternalSecretPlugins(ctx)
if err != nil {
return nil, err
}
return internalPlugins, nil
}
// getAdjustedPluginType gets the adjusted plugin type for an entry. In most cases
// this will be entry.Type, but it will correctly return the type for legacy (pre-Vault 1.0) plugins.
func getAdjustedPluginType(entry *MountEntry) string {
if entry == nil {
return ""
}
pluginName := entry.Type
if pluginName == mountTypePlugin && entry.Config.PluginName != "" {
pluginName = entry.Config.PluginName
}
return pluginName
}
// ListDeduplicatedExternalSecretPlugins returns the enabled secret engines
// that are not builtin and not official-tier.
//
// This is useful for identifying "third-party" secrets mounts (e.g. community or
// partner tier external plugins) while excluding builtins and official HashiCorp
// plugins.
// Note: This will include all mounts that have been built externally (even if they are
// Hashicorp owned). This will happen if the plugin was built from a Github repo or from an
// artifact.
func (c *Core) ListDeduplicatedExternalSecretPlugins(ctx context.Context) ([]*MountEntry, error) {
_, externalPlugins, err := c.ListOfficialAndExternalSecretPlugins(ctx)
if err != nil {
return nil, err
}
seen := make(map[string]struct{})
var result []*MountEntry
for _, entry := range externalPlugins {
if entry == nil {
continue
}
pluginName := getAdjustedPluginType(entry)
if pluginName == "" {
continue
}
@ -1013,17 +1156,6 @@ func (c *Core) ListExternalSecretPlugins(ctx context.Context) ([]*MountEntry, er
continue
}
runner, err := c.pluginCatalog.Get(ctx, pluginName, consts.PluginTypeSecrets, pluginVersion)
if err != nil || runner == nil {
// If we can't resolve the plugin runner (e.g. missing catalog entry),
// conservatively skip it rather than risk misclassifying it.
continue
}
if runner.Builtin || runner.Tier == consts.PluginTierOfficial {
continue
}
result = append(result, entry)
seen[key] = struct{}{}
}

View file

@ -546,7 +546,7 @@ func TestCore_ListExternalSecretPlugins(t *testing.T) {
)
core.mountsLock.Unlock()
got, err := core.ListExternalSecretPlugins(ctx)
got, err := core.ListDeduplicatedExternalSecretPlugins(ctx)
require.NoError(t, err)
// Expect only the non-builtin external secrets plugin v2, v3 and v1.