mirror of
https://github.com/hashicorp/vault.git
synced 2026-02-18 18:38:08 -05:00
* 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:
parent
d5b3c8839c
commit
aeaed4484c
3 changed files with 176 additions and 44 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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{}{}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in a new issue