Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VAULT-528 Fix Vault Agent being unable to render secrets with delete_version_after set. #25387

Merged
merged 4 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/25387.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
agent: Fix issue where Vault Agent was unable to render KVv2 secrets with delete_version_after set.
```
155 changes: 155 additions & 0 deletions command/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ package command

import (
"bufio"
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"io"
"net"
Expand Down Expand Up @@ -3204,6 +3206,159 @@ auto_auth {
require.Truef(t, found, "unable to find consul-template partial message in logs", runnerLogMessage)
}

// TestAgent_DeleteAfterVersion_Rendering Validates that Vault Agent
// can correctly render a secret with delete_after_version set.
func TestAgent_DeleteAfterVersion_Rendering(t *testing.T) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully needless to say, but this test fails with an older version of consul-template (without the fix), and correctly validates the fix works.

logger := logging.NewVaultLogger(hclog.Trace)
cluster := vault.NewTestCluster(t,
&vault.CoreConfig{
Logger: logger,
},
&vault.TestClusterOptions{
NumCores: 1,
HandlerFunc: vaulthttp.Handler,
})
cluster.Start()
defer cluster.Cleanup()

vault.TestWaitActive(t, cluster.Cores[0].Core)
serverClient := cluster.Cores[0].Client

// Set up KVv2
err := serverClient.Sys().Mount("kv-v2", &api.MountInput{
Type: "kv-v2",
})
require.NoError(t, err)

// Configure the mount to set delete_version_after on all of its secrets
_, err = serverClient.Logical().Write("kv-v2/config", map[string]interface{}{
"delete_version_after": "1h",
})
require.NoError(t, err)

// Set up the secret (which will have delete_version_after set to 1h)
data, err := serverClient.KVv2("kv-v2").Put(context.Background(), "foo", map[string]interface{}{
"bar": "baz",
})
require.NoError(t, err)

// Ensure Deletion Time was correctly set
require.NotZero(t, data.VersionMetadata.DeletionTime)
require.True(t, data.VersionMetadata.DeletionTime.After(time.Now()))
require.NotNil(t, data.VersionMetadata.CreatedTime)
require.True(t, data.VersionMetadata.DeletionTime.After(data.VersionMetadata.CreatedTime))

// Unset the environment variable so that Agent picks up the right test
// cluster address
defer os.Setenv(api.EnvVaultAddress, os.Getenv(api.EnvVaultAddress))
os.Setenv(api.EnvVaultAddress, serverClient.Address())

// create temp dir for this test run
tmpDir, err := os.MkdirTemp("", "TestAgent_DeleteAfterVersion_Rendering")
require.NoError(t, err)

tokenFileName := makeTempFile(t, "token-file", serverClient.Token())
defer os.Remove(tokenFileName)

autoAuthConfig := fmt.Sprintf(`
auto_auth {
method {
type = "token_file"
config = {
token_file_path = "%s"
}
}
}`, tokenFileName)

// Create a config file
config := `
vault {
address = "%s"
tls_skip_verify = true
}

%s

%s
`

fileName := "secret.txt"
templateConfig := fmt.Sprintf(`
template {
destination = "%s/%s"
contents = "{{ with secret \"kv-v2/foo\" }}{{ .Data.data.bar }}{{ end }}"
}
`, tmpDir, fileName)

config = fmt.Sprintf(config, serverClient.Address(), autoAuthConfig, templateConfig)
configPath := makeTempFile(t, "config.hcl", config)
defer os.Remove(configPath)

// Start the agent
ui, cmd := testAgentCommand(t, logger)
cmd.client = serverClient
cmd.startedCh = make(chan struct{})

wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
code := cmd.Run([]string{"-config", configPath})
if code != 0 {
t.Errorf("non-zero return code when running agent: %d", code)
t.Logf("STDOUT from agent:\n%s", ui.OutputWriter.String())
t.Logf("STDERR from agent:\n%s", ui.ErrorWriter.String())
}
wg.Done()
}()

select {
case <-cmd.startedCh:
case <-time.After(5 * time.Second):
t.Errorf("timeout")
}

// We need to shut down the Agent command
defer func() {
cmd.ShutdownCh <- struct{}{}
wg.Wait()
}()

filePath := fmt.Sprintf("%s/%s", tmpDir, fileName)

waitForFiles := func() error {
tick := time.Tick(100 * time.Millisecond)
timeout := time.After(10 * time.Second)
// We need to wait for the templates to render...
for {
select {
case <-timeout:
t.Fatalf("timed out waiting for templates to render, last error: %v", err)
case <-tick:
}

_, err := os.Stat(filePath)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
continue
}
return err
}

return nil
}
}

err = waitForFiles()
require.NoError(t, err)

// Ensure the file has the
fileData, err := os.ReadFile(filePath)
require.NoError(t, err)
if string(fileData) != "baz" {
t.Fatalf("Unexpected file contents. Expected 'baz', got %s", string(fileData))
}
}

// Get a randomly assigned port and then free it again before returning it.
// There is still a race when trying to use it, but should work better
// than a static port.
Expand Down
24 changes: 10 additions & 14 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ require (
github.com/hashicorp/cap v0.5.0
github.com/hashicorp/cap/ldap v0.0.0-20230914221201-c4eecc7e31f7
github.com/hashicorp/cli v1.1.6
github.com/hashicorp/consul-template v0.36.0
github.com/hashicorp/consul/api v1.26.1
github.com/hashicorp/consul-template v0.36.1-0.20240213145952-6c83e89b48af
github.com/hashicorp/consul/api v1.27.0
github.com/hashicorp/errwrap v1.1.0
github.com/hashicorp/eventlogger v0.2.8
github.com/hashicorp/go-bexpr v0.1.12
Expand Down Expand Up @@ -123,7 +123,7 @@ require (
github.com/hashicorp/hcp-link v0.2.1
github.com/hashicorp/hcp-scada-provider v0.2.2
github.com/hashicorp/hcp-sdk-go v0.75.0
github.com/hashicorp/nomad/api v0.0.0-20230519153805-2275a83cbfdf
github.com/hashicorp/nomad/api v0.0.0-20240213164230-c364cb57298d
github.com/hashicorp/raft v1.6.0
github.com/hashicorp/raft-autopilot v0.2.0
github.com/hashicorp/raft-boltdb/v2 v2.3.0
Expand Down Expand Up @@ -217,14 +217,14 @@ require (
go.uber.org/atomic v1.11.0
go.uber.org/goleak v1.2.1
golang.org/x/crypto v0.19.0
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a
golang.org/x/net v0.21.0
golang.org/x/oauth2 v0.17.0
golang.org/x/sync v0.6.0
golang.org/x/sys v0.17.0
golang.org/x/term v0.17.0
golang.org/x/text v0.14.0
golang.org/x/tools v0.16.1
golang.org/x/tools v0.18.0
google.golang.org/api v0.163.0
google.golang.org/grpc v1.61.0
google.golang.org/protobuf v1.32.0
Expand Down Expand Up @@ -326,9 +326,6 @@ require (
github.com/coreos/pkg v0.0.0-20220810130054-c7d1c02cb6cf // indirect
github.com/couchbase/gocb/v2 v2.6.5 // indirect
github.com/couchbase/gocbcore/v10 v10.3.1 // indirect
github.com/couchbase/gocbcoreps v0.1.1 // indirect
github.com/couchbase/goprotostellar v1.0.1 // indirect
github.com/couchbaselabs/gocbconnstr/v2 v2.0.0-20230515165046-68b522a21131 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/danieljoos/wincred v1.1.2 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
Expand Down Expand Up @@ -388,11 +385,10 @@ require (
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/gophercloud/gophercloud v0.1.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect
github.com/hashicorp/cronexpr v1.1.1 // indirect
github.com/hashicorp/cronexpr v1.1.2 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-msgpack/v2 v2.1.1 // indirect
github.com/hashicorp/go-secure-stdlib/fileutil v0.1.0 // indirect
Expand All @@ -404,11 +400,11 @@ require (
github.com/hashicorp/mdns v1.0.4 // indirect
github.com/hashicorp/net-rpc-msgpackrpc/v2 v2.0.0 // indirect
github.com/hashicorp/serf v0.10.1 // indirect
github.com/hashicorp/vault/api/auth/kubernetes v0.5.0 // indirect
github.com/hashicorp/vault/api/auth/kubernetes v0.6.0 // indirect
github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
github.com/imdario/mergo v0.3.15 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.14.0 // indirect
github.com/jackc/pgio v1.0.0 // indirect
Expand Down Expand Up @@ -494,7 +490,7 @@ require (
github.com/snowflakedb/gosnowflake v1.7.2 // indirect
github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d // indirect
github.com/sony/gobreaker v0.5.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/std-uritemplate/std-uritemplate/go v0.0.50 // indirect
github.com/stretchr/objx v0.5.0 // indirect
Expand Down
Loading
Loading