mirror of
https://github.com/hashicorp/vault.git
synced 2026-05-05 20:36:26 +02:00
VAULT-19239 Add capability to disable dynamic secret caching for Vault Proxy (#23801)
* VAULT-19239 create disable static secret caching config * VAULT-19239 missed file * VAULT-19239 didn't finish a log line * VAULT-19239 adjust test to use new option * Fix typo Co-authored-by: Kuba Wieczorek <kuba.wieczorek@hashicorp.com> --------- Co-authored-by: Kuba Wieczorek <kuba.wieczorek@hashicorp.com>
This commit is contained in:
parent
22a2e74fcc
commit
3e054cbd4c
@ -489,11 +489,12 @@ func (c *AgentCommand) Run(args []string) int {
|
||||
// Create the lease cache proxier and set its underlying proxier to
|
||||
// the API proxier.
|
||||
leaseCache, err = cache.NewLeaseCache(&cache.LeaseCacheConfig{
|
||||
Client: proxyClient,
|
||||
BaseContext: ctx,
|
||||
Proxier: apiProxy,
|
||||
Logger: cacheLogger.Named("leasecache"),
|
||||
UserAgentToUse: useragent.ProxyAPIProxyString(),
|
||||
Client: proxyClient,
|
||||
BaseContext: ctx,
|
||||
Proxier: apiProxy,
|
||||
Logger: cacheLogger.Named("leasecache"),
|
||||
CacheDynamicSecrets: true,
|
||||
UserAgentToUse: useragent.ProxyAPIProxyString(),
|
||||
})
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Error creating lease cache: %v", err))
|
||||
|
||||
@ -6,7 +6,6 @@ package agent
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
@ -123,7 +122,7 @@ func TestCache_UsingAutoAuthToken(t *testing.T) {
|
||||
}
|
||||
roleID1 := resp.Data["role_id"].(string)
|
||||
|
||||
rolef, err := ioutil.TempFile("", "auth.role-id.test.")
|
||||
rolef, err := os.CreateTemp("", "auth.role-id.test.")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -132,7 +131,7 @@ func TestCache_UsingAutoAuthToken(t *testing.T) {
|
||||
defer os.Remove(role)
|
||||
t.Logf("input role_id_file_path: %s", role)
|
||||
|
||||
secretf, err := ioutil.TempFile("", "auth.secret-id.test.")
|
||||
secretf, err := os.CreateTemp("", "auth.secret-id.test.")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -143,7 +142,7 @@ func TestCache_UsingAutoAuthToken(t *testing.T) {
|
||||
|
||||
// We close these right away because we're just basically testing
|
||||
// permissions and finding a usable file name
|
||||
ouf, err := ioutil.TempFile("", "auth.tokensink.test.")
|
||||
ouf, err := os.CreateTemp("", "auth.tokensink.test.")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -176,11 +175,12 @@ func TestCache_UsingAutoAuthToken(t *testing.T) {
|
||||
// Create the lease cache proxier and set its underlying proxier to
|
||||
// the API proxier.
|
||||
leaseCache, err := cache.NewLeaseCache(&cache.LeaseCacheConfig{
|
||||
Client: client,
|
||||
BaseContext: ctx,
|
||||
Proxier: apiProxy,
|
||||
Logger: cacheLogger.Named("leasecache"),
|
||||
UserAgentToUse: "test",
|
||||
Client: client,
|
||||
BaseContext: ctx,
|
||||
Proxier: apiProxy,
|
||||
Logger: cacheLogger.Named("leasecache"),
|
||||
CacheDynamicSecrets: true,
|
||||
UserAgentToUse: "test",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -269,13 +269,13 @@ func TestCache_UsingAutoAuthToken(t *testing.T) {
|
||||
t.Fatal("expected notexist err")
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(role, []byte(roleID1), 0o600); err != nil {
|
||||
if err := os.WriteFile(role, []byte(roleID1), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
logger.Trace("wrote test role 1", "path", role)
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(secret, []byte(secretID1), 0o600); err != nil {
|
||||
if err := os.WriteFile(secret, []byte(secretID1), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
logger.Trace("wrote test secret 1", "path", secret)
|
||||
@ -287,7 +287,7 @@ func TestCache_UsingAutoAuthToken(t *testing.T) {
|
||||
if time.Now().After(timeout) {
|
||||
t.Fatal("did not find a written token after timeout")
|
||||
}
|
||||
val, err := ioutil.ReadFile(out)
|
||||
val, err := os.ReadFile(out)
|
||||
if err == nil {
|
||||
os.Remove(out)
|
||||
if len(val) == 0 {
|
||||
|
||||
11
command/agentproxyshared/cache/api_proxy_test.go
vendored
11
command/agentproxyshared/cache/api_proxy_test.go
vendored
@ -272,11 +272,12 @@ func setupClusterAndAgentCommon(ctx context.Context, t *testing.T, coreConfig *v
|
||||
// Create the lease cache proxier and set its underlying proxier to
|
||||
// the API proxier.
|
||||
leaseCache, err = NewLeaseCache(&LeaseCacheConfig{
|
||||
Client: clienToUse,
|
||||
BaseContext: ctx,
|
||||
Proxier: apiProxy,
|
||||
Logger: cacheLogger.Named("leasecache"),
|
||||
UserAgentToUse: "test",
|
||||
Client: clienToUse,
|
||||
BaseContext: ctx,
|
||||
Proxier: apiProxy,
|
||||
Logger: cacheLogger.Named("leasecache"),
|
||||
CacheDynamicSecrets: true,
|
||||
UserAgentToUse: "test",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
47
command/agentproxyshared/cache/lease_cache.go
vendored
47
command/agentproxyshared/cache/lease_cache.go
vendored
@ -106,6 +106,10 @@ type LeaseCache struct {
|
||||
// cache static secrets, as well as dynamic secrets.
|
||||
cacheStaticSecrets bool
|
||||
|
||||
// cacheDynamicSecrets is used to determine if the cache should
|
||||
// cache dynamic secrets
|
||||
cacheDynamicSecrets bool
|
||||
|
||||
// capabilityManager is used when static secrets are enabled to
|
||||
// manage the capabilities of cached tokens.
|
||||
capabilityManager *StaticSecretCapabilityManager
|
||||
@ -114,13 +118,14 @@ type LeaseCache struct {
|
||||
// LeaseCacheConfig is the configuration for initializing a new
|
||||
// LeaseCache.
|
||||
type LeaseCacheConfig struct {
|
||||
Client *api.Client
|
||||
BaseContext context.Context
|
||||
Proxier Proxier
|
||||
Logger hclog.Logger
|
||||
UserAgentToUse string
|
||||
Storage *cacheboltdb.BoltStorage
|
||||
CacheStaticSecrets bool
|
||||
Client *api.Client
|
||||
BaseContext context.Context
|
||||
Proxier Proxier
|
||||
Logger hclog.Logger
|
||||
UserAgentToUse string
|
||||
Storage *cacheboltdb.BoltStorage
|
||||
CacheStaticSecrets bool
|
||||
CacheDynamicSecrets bool
|
||||
}
|
||||
|
||||
type inflightRequest struct {
|
||||
@ -167,17 +172,18 @@ func NewLeaseCache(conf *LeaseCacheConfig) (*LeaseCache, error) {
|
||||
baseCtxInfo := cachememdb.NewContextInfo(conf.BaseContext)
|
||||
|
||||
return &LeaseCache{
|
||||
client: conf.Client,
|
||||
proxier: conf.Proxier,
|
||||
logger: conf.Logger,
|
||||
userAgentToUse: conf.UserAgentToUse,
|
||||
db: db,
|
||||
baseCtxInfo: baseCtxInfo,
|
||||
l: &sync.RWMutex{},
|
||||
idLocks: locksutil.CreateLocks(),
|
||||
inflightCache: gocache.New(gocache.NoExpiration, gocache.NoExpiration),
|
||||
ps: conf.Storage,
|
||||
cacheStaticSecrets: conf.CacheStaticSecrets,
|
||||
client: conf.Client,
|
||||
proxier: conf.Proxier,
|
||||
logger: conf.Logger,
|
||||
userAgentToUse: conf.UserAgentToUse,
|
||||
db: db,
|
||||
baseCtxInfo: baseCtxInfo,
|
||||
l: &sync.RWMutex{},
|
||||
idLocks: locksutil.CreateLocks(),
|
||||
inflightCache: gocache.New(gocache.NoExpiration, gocache.NoExpiration),
|
||||
ps: conf.Storage,
|
||||
cacheStaticSecrets: conf.CacheStaticSecrets,
|
||||
cacheDynamicSecrets: conf.CacheDynamicSecrets,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -486,6 +492,11 @@ func (c *LeaseCache) Send(ctx context.Context, req *SendRequest) (*SendResponse,
|
||||
index.ID = dynamicSecretCacheId
|
||||
}
|
||||
|
||||
// Short-circuit if we've been configured to not cache dynamic secrets
|
||||
if !c.cacheDynamicSecrets {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Short-circuit if the secret is not renewable
|
||||
tokenRenewable, err := secret.TokenIsRenewable()
|
||||
if err != nil {
|
||||
|
||||
@ -43,12 +43,13 @@ func testNewLeaseCache(t *testing.T, responses []*SendResponse) *LeaseCache {
|
||||
t.Fatal(err)
|
||||
}
|
||||
lc, err := NewLeaseCache(&LeaseCacheConfig{
|
||||
Client: client,
|
||||
BaseContext: context.Background(),
|
||||
Proxier: NewMockProxier(responses),
|
||||
Logger: logging.NewVaultLogger(hclog.Trace).Named("cache.leasecache"),
|
||||
CacheStaticSecrets: true,
|
||||
UserAgentToUse: "test",
|
||||
Client: client,
|
||||
BaseContext: context.Background(),
|
||||
Proxier: NewMockProxier(responses),
|
||||
Logger: logging.NewVaultLogger(hclog.Trace).Named("cache.leasecache"),
|
||||
CacheStaticSecrets: true,
|
||||
CacheDynamicSecrets: true,
|
||||
UserAgentToUse: "test",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -65,12 +66,13 @@ func testNewLeaseCacheWithDelay(t *testing.T, cacheable bool, delay int) *LeaseC
|
||||
}
|
||||
|
||||
lc, err := NewLeaseCache(&LeaseCacheConfig{
|
||||
Client: client,
|
||||
BaseContext: context.Background(),
|
||||
Proxier: &mockDelayProxier{cacheable, delay},
|
||||
Logger: logging.NewVaultLogger(hclog.Trace).Named("cache.leasecache"),
|
||||
CacheStaticSecrets: true,
|
||||
UserAgentToUse: "test",
|
||||
Client: client,
|
||||
BaseContext: context.Background(),
|
||||
Proxier: &mockDelayProxier{cacheable, delay},
|
||||
Logger: logging.NewVaultLogger(hclog.Trace).Named("cache.leasecache"),
|
||||
CacheStaticSecrets: true,
|
||||
CacheDynamicSecrets: true,
|
||||
UserAgentToUse: "test",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -86,13 +88,14 @@ func testNewLeaseCacheWithPersistence(t *testing.T, responses []*SendResponse, s
|
||||
require.NoError(t, err)
|
||||
|
||||
lc, err := NewLeaseCache(&LeaseCacheConfig{
|
||||
Client: client,
|
||||
BaseContext: context.Background(),
|
||||
Proxier: NewMockProxier(responses),
|
||||
Logger: logging.NewVaultLogger(hclog.Trace).Named("cache.leasecache"),
|
||||
Storage: storage,
|
||||
CacheStaticSecrets: true,
|
||||
UserAgentToUse: "test",
|
||||
Client: client,
|
||||
BaseContext: context.Background(),
|
||||
Proxier: NewMockProxier(responses),
|
||||
Logger: logging.NewVaultLogger(hclog.Trace).Named("cache.leasecache"),
|
||||
Storage: storage,
|
||||
CacheStaticSecrets: true,
|
||||
CacheDynamicSecrets: true,
|
||||
UserAgentToUse: "test",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@ -22,11 +22,12 @@ func testNewLeaseCache(t *testing.T, responses []*cache.SendResponse) *cache.Lea
|
||||
t.Fatal(err)
|
||||
}
|
||||
lc, err := cache.NewLeaseCache(&cache.LeaseCacheConfig{
|
||||
Client: client,
|
||||
BaseContext: context.Background(),
|
||||
Proxier: cache.NewMockProxier(responses),
|
||||
Logger: logging.NewVaultLogger(hclog.Trace).Named("cache.leasecache"),
|
||||
UserAgentToUse: "test",
|
||||
Client: client,
|
||||
BaseContext: context.Background(),
|
||||
Proxier: cache.NewMockProxier(responses),
|
||||
Logger: logging.NewVaultLogger(hclog.Trace).Named("cache.leasecache"),
|
||||
CacheDynamicSecrets: true,
|
||||
UserAgentToUse: "test",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@ -447,13 +447,17 @@ func (c *ProxyCommand) Run(args []string) int {
|
||||
Proxier: apiProxy,
|
||||
Logger: cacheLogger.Named("leasecache"),
|
||||
CacheStaticSecrets: config.Cache.CacheStaticSecrets,
|
||||
UserAgentToUse: useragent.AgentProxyString(),
|
||||
// dynamic secrets are configured as default-on to preserve backwards compatibility
|
||||
CacheDynamicSecrets: !config.Cache.DisableCachingDynamicSecrets,
|
||||
UserAgentToUse: useragent.AgentProxyString(),
|
||||
})
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Error creating lease cache: %v", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
cacheLogger.Info("cache configured", "cache_static_secrets", config.Cache.CacheStaticSecrets, "disable_caching_dynamic_secrets", config.Cache.DisableCachingDynamicSecrets)
|
||||
|
||||
// Configure persistent storage and add to LeaseCache
|
||||
if config.Cache.Persist != nil {
|
||||
deferFunc, oldToken, err := agentproxyshared.AddPersistentStorageToLeaseCache(ctx, leaseCache, config.Cache.Persist, cacheLogger)
|
||||
|
||||
@ -104,6 +104,7 @@ type Cache struct {
|
||||
Persist *agentproxyshared.PersistConfig `hcl:"persist"`
|
||||
InProcDialer transportDialer `hcl:"-"`
|
||||
CacheStaticSecrets bool `hcl:"cache_static_secrets"`
|
||||
DisableCachingDynamicSecrets bool `hcl:"disable_caching_dynamic_secrets"`
|
||||
StaticSecretTokenCapabilityRefreshIntervalRaw interface{} `hcl:"static_secret_token_capability_refresh_interval"`
|
||||
StaticSecretTokenCapabilityRefreshInterval time.Duration `hcl:"-"`
|
||||
}
|
||||
@ -260,6 +261,10 @@ func (c *Config) ValidateConfig() error {
|
||||
return fmt.Errorf("cache.cache_static_secrets=true requires an auto-auth block configured, to use the token to connect with Vault's event system")
|
||||
}
|
||||
|
||||
if c.Cache != nil && !c.Cache.CacheStaticSecrets && c.Cache.DisableCachingDynamicSecrets {
|
||||
return fmt.Errorf("to enable the cache, the cache must be configured to either cache static secrets or dynamic secrets")
|
||||
}
|
||||
|
||||
if c.AutoAuth == nil && c.Cache == nil && len(c.Listeners) == 0 {
|
||||
return fmt.Errorf("no auto_auth, cache, or listener block found in config")
|
||||
}
|
||||
|
||||
@ -119,6 +119,19 @@ func TestLoadConfigFile_ProxyCache(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestLoadConfigFile_NoCachingEnabled tests that you cannot enable a cache
|
||||
// without either of the options to enable caching secrets
|
||||
func TestLoadConfigFile_NoCachingEnabled(t *testing.T) {
|
||||
cfg, err := LoadConfigFile("./test-fixtures/config-cache-but-no-secrets.hcl")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := cfg.ValidateConfig(); err == nil {
|
||||
t.Fatalf("expected error, as you cannot configure a cache without caching secrets")
|
||||
}
|
||||
}
|
||||
|
||||
// TestLoadConfigFile_StaticSecretCachingWithoutAutoAuth tests that loading
|
||||
// a config file with static secret caching enabled but no auto auth will fail.
|
||||
func TestLoadConfigFile_StaticSecretCachingWithoutAutoAuth(t *testing.T) {
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
# Copyright (c) HashiCorp, Inc.
|
||||
# SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
pid_file = "./pidfile"
|
||||
|
||||
cache {
|
||||
cache_static_secrets = false
|
||||
disable_caching_dynamic_secrets = true
|
||||
}
|
||||
|
||||
listener "tcp" {
|
||||
address = "127.0.0.1:8300"
|
||||
tls_disable = true
|
||||
}
|
||||
|
||||
vault {
|
||||
address = "http://127.0.0.1:1111"
|
||||
tls_skip_verify = "true"
|
||||
}
|
||||
@ -686,6 +686,130 @@ vault {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// TestProxy_Cache_DisableDynamicSecretCaching tests that the cache will not cache a dynamic secret
|
||||
// if disabled in the options.
|
||||
func TestProxy_Cache_DisableDynamicSecretCaching(t *testing.T) {
|
||||
logger := logging.NewVaultLogger(hclog.Trace)
|
||||
cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{
|
||||
HandlerFunc: vaulthttp.Handler,
|
||||
})
|
||||
cluster.Start()
|
||||
defer cluster.Cleanup()
|
||||
|
||||
serverClient := cluster.Cores[0].Client
|
||||
|
||||
tokenFileName := makeTempFile(t, "token-file", serverClient.Token())
|
||||
defer os.Remove(tokenFileName)
|
||||
// We need auto-auth for static secret caching.
|
||||
// For ease, we use the token file path with the root token.
|
||||
autoAuthConfig := fmt.Sprintf(`
|
||||
auto_auth {
|
||||
method {
|
||||
type = "token_file"
|
||||
config = {
|
||||
token_file_path = "%s"
|
||||
}
|
||||
}
|
||||
}`, tokenFileName)
|
||||
|
||||
// Unset the environment variable so that proxy picks up the right test
|
||||
// cluster address
|
||||
defer os.Setenv(api.EnvVaultAddress, os.Getenv(api.EnvVaultAddress))
|
||||
os.Unsetenv(api.EnvVaultAddress)
|
||||
|
||||
cacheConfig := `
|
||||
cache {
|
||||
disable_caching_dynamic_secrets = true
|
||||
cache_static_secrets = true // We need to cache at least one kind of secret
|
||||
}
|
||||
`
|
||||
listenAddr := generateListenerAddress(t)
|
||||
listenConfig := fmt.Sprintf(`
|
||||
listener "tcp" {
|
||||
address = "%s"
|
||||
tls_disable = true
|
||||
}
|
||||
`, listenAddr)
|
||||
|
||||
config := fmt.Sprintf(`
|
||||
vault {
|
||||
address = "%s"
|
||||
tls_skip_verify = true
|
||||
}
|
||||
%s
|
||||
%s
|
||||
%s
|
||||
`, serverClient.Address(), cacheConfig, listenConfig, autoAuthConfig)
|
||||
configPath := makeTempFile(t, "config.hcl", config)
|
||||
defer os.Remove(configPath)
|
||||
|
||||
// Start proxy
|
||||
_, cmd := testProxyCommand(t, logger)
|
||||
cmd.startedCh = make(chan struct{})
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
cmd.Run([]string{"-config", configPath})
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-cmd.startedCh:
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Errorf("timeout")
|
||||
}
|
||||
|
||||
proxyClient, err := api.NewClient(api.DefaultConfig())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
proxyClient.SetToken(serverClient.Token())
|
||||
proxyClient.SetMaxRetries(0)
|
||||
err = proxyClient.SetAddress("http://" + listenAddr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
renewable := true
|
||||
tokenCreateRequest := &api.TokenCreateRequest{
|
||||
Policies: []string{"default"},
|
||||
TTL: "30m",
|
||||
Renewable: &renewable,
|
||||
}
|
||||
|
||||
// This was the simplest test I could find to trigger the caching behaviour,
|
||||
// i.e. the most concise I could make the test that I can tell
|
||||
// creating an orphan token returns Auth, is renewable, and isn't a token
|
||||
// that's managed elsewhere (since it's an orphan)
|
||||
secret, err := proxyClient.Auth().Token().CreateOrphan(tokenCreateRequest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if secret == nil || secret.Auth == nil {
|
||||
t.Fatalf("secret not as expected: %v", secret)
|
||||
}
|
||||
|
||||
token := secret.Auth.ClientToken
|
||||
|
||||
secret, err = proxyClient.Auth().Token().CreateOrphan(tokenCreateRequest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if secret == nil || secret.Auth == nil {
|
||||
t.Fatalf("secret not as expected: %v", secret)
|
||||
}
|
||||
|
||||
token2 := secret.Auth.ClientToken
|
||||
|
||||
if token == token2 {
|
||||
t.Fatalf("token create response was cached, as the tokens differ")
|
||||
}
|
||||
|
||||
close(cmd.ShutdownCh)
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// TestProxy_Cache_StaticSecret Tests that the cache successfully caches a static secret
|
||||
// going through the Proxy,
|
||||
func TestProxy_Cache_StaticSecret(t *testing.T) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user