mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 19:17:58 +00:00
VAULT-25987 de-flake Test_NoAutoAuthSelfHealing_BadPolicy (#26547)
* VAULT-25987 de-flake Test_NoAutoAuthSelfHealing_BadPolicy * Send token to outputchannel too * Remove initial sink checks
This commit is contained in:
@@ -27,20 +27,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
lookupSelfTemplateContents = `{{ with secret "auth/token/lookup-self" }}{{ .Data.id }}{{ end }}`
|
||||
|
||||
kvDataTemplateContents = `"{{ with secret "secret/data/otherapp" }}{{ .Data.data.username }}{{ end }}"`
|
||||
|
||||
kvAccessPolicy = `
|
||||
path "/kv/*" {
|
||||
capabilities = ["create", "read", "update", "delete", "list"]
|
||||
}
|
||||
path "/secret/*" {
|
||||
capabilities = ["create", "read", "update", "delete", "list"]
|
||||
}`
|
||||
)
|
||||
|
||||
// TestAutoAuthSelfHealing_TokenFileAuth_SinkOutput tests that
|
||||
// if the token is revoked, Auto Auth is re-triggered and a valid new token
|
||||
// is written to a sink, and the template is correctly rendered with the new token
|
||||
@@ -48,14 +34,6 @@ func TestAutoAuthSelfHealing_TokenFileAuth_SinkOutput(t *testing.T) {
|
||||
// Unset the environment variable so that agent picks up the right test cluster address
|
||||
t.Setenv(api.EnvVaultAddress, "")
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
pathLookupSelf := filepath.Join(tmpDir, "lookup-self")
|
||||
pathVaultToken := filepath.Join(tmpDir, "vault-token")
|
||||
pathTokenFile := filepath.Join(tmpDir, "token-file")
|
||||
|
||||
secretRenderInterval := 1 * time.Second
|
||||
contextTimeout := 30 * time.Second
|
||||
|
||||
cluster := minimal.NewTestSoloCluster(t, nil)
|
||||
logger := corehelpers.NewTestLogger(t)
|
||||
serverClient := cluster.Cores[0].Client
|
||||
@@ -68,17 +46,12 @@ func TestAutoAuthSelfHealing_TokenFileAuth_SinkOutput(t *testing.T) {
|
||||
require.NotEmpty(t, secret.Auth.ClientToken)
|
||||
token := secret.Auth.ClientToken
|
||||
|
||||
// Write token to vault-token file
|
||||
tokenFile, err := os.Create(pathVaultToken)
|
||||
require.NoError(t, err)
|
||||
_, err = tokenFile.WriteString(token)
|
||||
require.NoError(t, err)
|
||||
err = tokenFile.Close()
|
||||
require.NoError(t, err)
|
||||
// Write token to the auto-auth token file
|
||||
pathVaultToken := makeTempFile(t, "token-file", token)
|
||||
|
||||
// Give us some leeway of 3 errors 1 from each of: auth handler, sink server template server.
|
||||
errCh := make(chan error, 3)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), contextTimeout)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
|
||||
// Create auth handler
|
||||
am, err := tokenfile.NewTokenFileAuthMethod(&auth.AuthConfig{
|
||||
@@ -89,6 +62,10 @@ func TestAutoAuthSelfHealing_TokenFileAuth_SinkOutput(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create sink file
|
||||
pathSinkFile := makeTempFile(t, "sink-file", "")
|
||||
require.NoError(t, err)
|
||||
|
||||
ahConfig := &auth.AuthHandlerConfig{
|
||||
Logger: logger.Named("auth.handler"),
|
||||
Client: serverClient,
|
||||
@@ -102,20 +79,14 @@ func TestAutoAuthSelfHealing_TokenFileAuth_SinkOutput(t *testing.T) {
|
||||
errCh <- ah.Run(ctx, am)
|
||||
}()
|
||||
|
||||
// Create sink file server
|
||||
_, err = os.Create(pathTokenFile)
|
||||
require.NoError(t, err)
|
||||
|
||||
config := &sink.SinkConfig{
|
||||
Logger: logger.Named("sink.file"),
|
||||
Config: map[string]interface{}{
|
||||
"path": pathTokenFile,
|
||||
"path": pathSinkFile,
|
||||
},
|
||||
}
|
||||
fs, err := file.NewFileSink(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
config.Sink = fs
|
||||
|
||||
ss := sink.NewSinkServer(&sink.SinkServerConfig{
|
||||
@@ -135,14 +106,14 @@ func TestAutoAuthSelfHealing_TokenFileAuth_SinkOutput(t *testing.T) {
|
||||
TLSSkipVerify: true,
|
||||
},
|
||||
TemplateConfig: &agentConfig.TemplateConfig{
|
||||
StaticSecretRenderInt: secretRenderInterval,
|
||||
StaticSecretRenderInt: 1 * time.Second,
|
||||
},
|
||||
AutoAuth: &agentConfig.AutoAuth{
|
||||
Sinks: []*agentConfig.Sink{
|
||||
{
|
||||
Type: "file",
|
||||
Config: map[string]interface{}{
|
||||
"path": pathLookupSelf,
|
||||
"path": pathSinkFile,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -154,33 +125,26 @@ func TestAutoAuthSelfHealing_TokenFileAuth_SinkOutput(t *testing.T) {
|
||||
ExitAfterAuth: false,
|
||||
}
|
||||
|
||||
pathTemplateOutput := makeTempFile(t, "template-output", "")
|
||||
originalTemplateFileInfo, err := os.Stat(pathSinkFile)
|
||||
require.NoError(t, err)
|
||||
templateTest := &ctconfig.TemplateConfig{
|
||||
Contents: pointerutil.StringPtr(lookupSelfTemplateContents),
|
||||
Destination: pointerutil.StringPtr(pathLookupSelf),
|
||||
Contents: pointerutil.StringPtr(`{{ with secret "auth/token/lookup-self" }}{{ .Data.id }}{{ end }}`),
|
||||
Destination: pointerutil.StringPtr(pathTemplateOutput),
|
||||
}
|
||||
templatesToRender := []*ctconfig.TemplateConfig{templateTest}
|
||||
|
||||
var server *template.Server
|
||||
server = template.NewServer(sc)
|
||||
server := template.NewServer(sc)
|
||||
go func() {
|
||||
errCh <- server.Run(ctx, ah.TemplateTokenCh, templatesToRender, ah.AuthInProgress, ah.InvalidToken)
|
||||
}()
|
||||
|
||||
// Trigger template render (mark the time as being earlier, based on the render interval)
|
||||
preTriggerTime := time.Now().Add(-secretRenderInterval)
|
||||
// Send token to template channel
|
||||
ah.TemplateTokenCh <- token
|
||||
fileInfo, err := waitForFiles(t, pathTokenFile, preTriggerTime)
|
||||
templateFileInfo, err := waitForFiles(t, pathTemplateOutput, originalTemplateFileInfo.ModTime())
|
||||
require.NoError(t, err)
|
||||
|
||||
templateFileInfo, err := waitForFiles(t, pathLookupSelf, preTriggerTime)
|
||||
require.NoError(t, err)
|
||||
|
||||
tokenInSink, err := os.ReadFile(pathTokenFile)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, token, string(tokenInSink))
|
||||
|
||||
// Revoke Token
|
||||
t.Logf("revoking token")
|
||||
err = serverClient.Auth().Token().RevokeOrphan(token)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -197,20 +161,20 @@ func TestAutoAuthSelfHealing_TokenFileAuth_SinkOutput(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Wait for auto-auth to complete
|
||||
_, err = waitForFiles(t, pathTokenFile, fileInfo.ModTime())
|
||||
_, err = waitForFiles(t, pathSinkFile, templateFileInfo.ModTime())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify the new token has been written to a file sink after re-authenticating using lookup-self
|
||||
tokenInSink, err = os.ReadFile(pathTokenFile)
|
||||
tokenInSink, err := os.ReadFile(pathSinkFile)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, newToken, string(tokenInSink))
|
||||
|
||||
// Wait for the template file to have re-rendered
|
||||
_, err = waitForFiles(t, pathLookupSelf, templateFileInfo.ModTime())
|
||||
_, err = waitForFiles(t, pathTemplateOutput, templateFileInfo.ModTime())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify the template has now been correctly rendered with the new token
|
||||
templateContents, err := os.ReadFile(pathLookupSelf)
|
||||
templateContents, err := os.ReadFile(pathTemplateOutput)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, newToken, string(templateContents))
|
||||
|
||||
@@ -238,46 +202,40 @@ func Test_NoAutoAuthSelfHealing_BadPolicy(t *testing.T) {
|
||||
// Unset the environment variable so that agent picks up the right test cluster address
|
||||
t.Setenv(api.EnvVaultAddress, "")
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
pathKVData := filepath.Join(tmpDir, "kvData")
|
||||
pathVaultToken := filepath.Join(tmpDir, "vault-token")
|
||||
pathTokenFile := filepath.Join(tmpDir, "token-file")
|
||||
policyName := "kv-access"
|
||||
secretRenderInterval := 1 * time.Second
|
||||
contextTimeout := 30 * time.Second
|
||||
|
||||
cluster := minimal.NewTestSoloCluster(t, nil)
|
||||
logger := corehelpers.NewTestLogger(t)
|
||||
serverClient := cluster.Cores[0].Client
|
||||
|
||||
// Write a policy with correct access to the secrets
|
||||
err := serverClient.Sys().PutPolicy(policyName, kvAccessPolicy)
|
||||
err := serverClient.Sys().PutPolicy(policyName, `
|
||||
path "/kv/*" {
|
||||
capabilities = ["create", "read", "update", "delete", "list"]
|
||||
}
|
||||
path "/secret/*" {
|
||||
capabilities = ["create", "read", "update", "delete", "list"]
|
||||
}`)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a token without enough policy access to the kv secrets
|
||||
secret, err := serverClient.Auth().Token().Create(&api.TokenCreateRequest{
|
||||
Policies: []string{"test-autoauth"},
|
||||
Policies: []string{"default"},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, secret)
|
||||
require.NotNil(t, secret.Auth)
|
||||
require.NotEmpty(t, secret.Auth.ClientToken)
|
||||
require.Len(t, secret.Auth.Policies, 2)
|
||||
require.Len(t, secret.Auth.Policies, 1)
|
||||
require.Contains(t, secret.Auth.Policies, "default")
|
||||
require.Contains(t, secret.Auth.Policies, "test-autoauth")
|
||||
token := secret.Auth.ClientToken
|
||||
|
||||
// Write token to vault-token file
|
||||
tokenFile, err := os.Create(pathVaultToken)
|
||||
require.NoError(t, err)
|
||||
_, err = tokenFile.WriteString(token)
|
||||
require.NoError(t, err)
|
||||
err = tokenFile.Close()
|
||||
require.NoError(t, err)
|
||||
pathVaultToken := makeTempFile(t, "vault-token", token)
|
||||
|
||||
// Give us some leeway of 3 errors 1 from each of: auth handler, sink server template server.
|
||||
errCh := make(chan error, 3)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), contextTimeout)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
|
||||
// Create auth handler
|
||||
am, err := tokenfile.NewTokenFileAuthMethod(&auth.AuthConfig{
|
||||
@@ -300,20 +258,19 @@ func Test_NoAutoAuthSelfHealing_BadPolicy(t *testing.T) {
|
||||
errCh <- ah.Run(ctx, am)
|
||||
}()
|
||||
|
||||
// Create sink file server
|
||||
_, err = os.Create(pathTokenFile)
|
||||
// Create sink file
|
||||
pathSinkFile := makeTempFile(t, "sink-file", "")
|
||||
fileInfo, err := os.Stat(pathSinkFile)
|
||||
require.NoError(t, err)
|
||||
|
||||
config := &sink.SinkConfig{
|
||||
Logger: logger.Named("sink.file"),
|
||||
Config: map[string]interface{}{
|
||||
"path": pathTokenFile,
|
||||
"path": pathSinkFile,
|
||||
},
|
||||
}
|
||||
fs, err := file.NewFileSink(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
config.Sink = fs
|
||||
|
||||
ss := sink.NewSinkServer(&sink.SinkServerConfig{
|
||||
@@ -333,15 +290,15 @@ func Test_NoAutoAuthSelfHealing_BadPolicy(t *testing.T) {
|
||||
TLSSkipVerify: true,
|
||||
},
|
||||
TemplateConfig: &agentConfig.TemplateConfig{
|
||||
StaticSecretRenderInt: secretRenderInterval,
|
||||
StaticSecretRenderInt: 1 * time.Second,
|
||||
},
|
||||
// Need to crate at least one sink output so that it does not exit after rendering
|
||||
// Need to create at least one sink output so that it does not exit after rendering
|
||||
AutoAuth: &agentConfig.AutoAuth{
|
||||
Sinks: []*agentConfig.Sink{
|
||||
{
|
||||
Type: "file",
|
||||
Config: map[string]interface{}{
|
||||
"path": pathKVData,
|
||||
"path": pathSinkFile,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -353,27 +310,23 @@ func Test_NoAutoAuthSelfHealing_BadPolicy(t *testing.T) {
|
||||
ExitAfterAuth: false,
|
||||
}
|
||||
|
||||
pathTemplateDestination := makeTempFile(t, "kv-data", "")
|
||||
fileInfo, err = os.Stat(pathTemplateDestination)
|
||||
require.NoError(t, err)
|
||||
templateDestModTime := fileInfo.ModTime()
|
||||
templateTest := &ctconfig.TemplateConfig{
|
||||
Contents: pointerutil.StringPtr(kvDataTemplateContents),
|
||||
Destination: pointerutil.StringPtr(pathKVData),
|
||||
Contents: pointerutil.StringPtr(`"{{ with secret "secret/data/otherapp" }}{{ .Data.data.username }}{{ end }}"`),
|
||||
Destination: pointerutil.StringPtr(pathTemplateDestination),
|
||||
}
|
||||
templatesToRender := []*ctconfig.TemplateConfig{templateTest}
|
||||
|
||||
var server *template.Server
|
||||
server = template.NewServer(&sc)
|
||||
server := template.NewServer(&sc)
|
||||
go func() {
|
||||
errCh <- server.Run(ctx, ah.TemplateTokenCh, templatesToRender, ah.AuthInProgress, ah.InvalidToken)
|
||||
}()
|
||||
|
||||
// Trigger template render (mark the time as being earlier, based on the render interval)
|
||||
preTriggerTime := time.Now().Add(-secretRenderInterval)
|
||||
// Send token to the template channel
|
||||
ah.TemplateTokenCh <- token
|
||||
_, err = waitForFiles(t, pathTokenFile, preTriggerTime)
|
||||
require.NoError(t, err)
|
||||
|
||||
tokenInSink, err := os.ReadFile(pathTokenFile)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, token, string(tokenInSink))
|
||||
|
||||
// Create new token with the correct policy access
|
||||
tokenSecret, err := serverClient.Auth().Token().Create(&api.TokenCreateRequest{
|
||||
@@ -388,20 +341,28 @@ func Test_NoAutoAuthSelfHealing_BadPolicy(t *testing.T) {
|
||||
require.Contains(t, tokenSecret.Auth.Policies, policyName)
|
||||
newToken := tokenSecret.Auth.ClientToken
|
||||
|
||||
// Write token to file
|
||||
err = os.WriteFile(pathVaultToken, []byte(token), 0o600)
|
||||
// Write new token to token file (where Agent would re-auto-auth from if
|
||||
// it were triggered)
|
||||
err = os.WriteFile(pathVaultToken, []byte(newToken), 0o600)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Wait for any potential *incorrect* re-triggers of auto auth
|
||||
time.Sleep(secretRenderInterval * 3)
|
||||
time.Sleep(time.Second * 3)
|
||||
|
||||
// Auto auth should not have been re-triggered because of just a permission denied error
|
||||
// Verify that the new token has NOT been written to the token sink
|
||||
tokenInSink, err = os.ReadFile(pathTokenFile)
|
||||
tokenInSink, err := os.ReadFile(pathSinkFile)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, newToken, string(tokenInSink))
|
||||
require.Equal(t, token, string(tokenInSink))
|
||||
|
||||
fileInfo, err = os.Stat(pathTemplateDestination)
|
||||
require.NoError(t, err)
|
||||
newTemplateDestModTime := fileInfo.ModTime()
|
||||
// Verify that the template hasn't been rendered
|
||||
// since we still have invalid permissions
|
||||
require.Equal(t, templateDestModTime, newTemplateDestModTime)
|
||||
|
||||
cancel()
|
||||
wrapUpTimeout := 5 * time.Second
|
||||
for {
|
||||
@@ -424,11 +385,11 @@ func waitForFiles(t *testing.T, filePath string, prevModTime time.Time) (os.File
|
||||
var fileInfo os.FileInfo
|
||||
tick := time.Tick(100 * time.Millisecond)
|
||||
timeout := time.After(5 * time.Second)
|
||||
// We need to wait for the templates to render...
|
||||
// We need to wait for the files to be updated...
|
||||
for {
|
||||
select {
|
||||
case <-timeout:
|
||||
return nil, fmt.Errorf("timed out waiting for templates to render, last error: %w", err)
|
||||
return nil, fmt.Errorf("timed out waiting for files, last error: %w", err)
|
||||
case <-tick:
|
||||
}
|
||||
|
||||
@@ -441,9 +402,29 @@ func waitForFiles(t *testing.T, filePath string, prevModTime time.Time) (os.File
|
||||
}
|
||||
// Keep waiting until the file has been updated since the previous mod time
|
||||
if !fileInfo.ModTime().After(prevModTime) {
|
||||
err = fmt.Errorf("file not yet updated, prevModTime+%s, currentModTime=%s", prevModTime, fileInfo.ModTime())
|
||||
continue
|
||||
}
|
||||
|
||||
return fileInfo, nil
|
||||
}
|
||||
}
|
||||
|
||||
// makeTempFile creates a temp file with the specified name, populates it with the
|
||||
// supplied contents and closes it. The path to the file is returned, also the file
|
||||
// will be automatically removed when the test which created it, finishes.
|
||||
func makeTempFile(t *testing.T, name, contents string) string {
|
||||
t.Helper()
|
||||
|
||||
f, err := os.Create(filepath.Join(t.TempDir(), name))
|
||||
require.NoError(t, err)
|
||||
path := f.Name()
|
||||
|
||||
_, err = f.WriteString(contents)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = f.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
@@ -1465,6 +1465,8 @@ func makeTempFile(t *testing.T, name, contents string) string {
|
||||
return path
|
||||
}
|
||||
|
||||
// populateTempFile creates a temp file with the specified name, populates it with the
|
||||
// supplied contents and closes it. The file pointer is returned.
|
||||
func populateTempFile(t *testing.T, name, contents string) *os.File {
|
||||
t.Helper()
|
||||
|
||||
@@ -3436,9 +3438,7 @@ func generateListenerAddress(t *testing.T) string {
|
||||
t.Helper()
|
||||
|
||||
ln1, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
listenAddr := ln1.Addr().String()
|
||||
ln1.Close()
|
||||
return listenAddr
|
||||
|
||||
Reference in New Issue
Block a user