mirror of
https://github.com/kemko/nomad.git
synced 2026-01-01 16:05:42 +03:00
vault: load default config for tasks without vault (#19439)
It is often expected that a task that needs access to Vault defines a `vault` block to specify the Vault policy to use to derive a token. But in some scenarios, like when the Nomad client is connected to a local Vault agent that is responsible for authn/authz, the task is not required to defined a `vault` block. In these situations, the `default` Vault cluster should be used to render the template.
This commit is contained in:
3
.changelog/19439.txt
Normal file
3
.changelog/19439.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
```release-note:bug
|
||||
vault: Fixed a bug that caused `template` blocks to ignore Nomad configuration for Vault and use the default address of `https://127.0.0.1:8200` when the job does not have a `vault` block defined
|
||||
```
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
cstructs "github.com/hashicorp/nomad/client/structs"
|
||||
"github.com/hashicorp/nomad/client/taskenv"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
structsc "github.com/hashicorp/nomad/nomad/structs/config"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -212,14 +211,12 @@ func (h *templateHook) Poststart(ctx context.Context, req *interfaces.TaskPostst
|
||||
func (h *templateHook) newManager() (unblock chan struct{}, err error) {
|
||||
unblock = make(chan struct{})
|
||||
|
||||
var vaultConfig *structsc.VaultConfig
|
||||
if h.task.Vault != nil {
|
||||
vaultCluster := h.task.GetVaultClusterName()
|
||||
vaultConfig = h.config.clientConfig.GetVaultConfigs(h.logger)[vaultCluster]
|
||||
vaultCluster := h.task.GetVaultClusterName()
|
||||
vaultConfig := h.config.clientConfig.GetVaultConfigs(h.logger)[vaultCluster]
|
||||
|
||||
if vaultConfig == nil {
|
||||
return nil, fmt.Errorf("Vault cluster %q is disabled or not configured", vaultCluster)
|
||||
}
|
||||
// Fail if task has a vault block but not client config was found.
|
||||
if h.task.Vault != nil && vaultConfig == nil {
|
||||
return nil, fmt.Errorf("Vault cluster %q is disabled or not configured", vaultCluster)
|
||||
}
|
||||
|
||||
tg := h.config.alloc.Job.LookupTaskGroup(h.config.alloc.TaskGroup)
|
||||
|
||||
@@ -6,8 +6,12 @@ package taskrunner
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
consulapi "github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/nomad/ci"
|
||||
@@ -17,10 +21,12 @@ import (
|
||||
"github.com/hashicorp/nomad/client/config"
|
||||
cstructs "github.com/hashicorp/nomad/client/structs"
|
||||
"github.com/hashicorp/nomad/client/taskenv"
|
||||
"github.com/hashicorp/nomad/helper/pointer"
|
||||
"github.com/hashicorp/nomad/helper/testlog"
|
||||
"github.com/hashicorp/nomad/helper/uuid"
|
||||
"github.com/hashicorp/nomad/nomad/mock"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
structsc "github.com/hashicorp/nomad/nomad/structs/config"
|
||||
"github.com/shoenig/test/must"
|
||||
)
|
||||
|
||||
@@ -135,3 +141,129 @@ func Test_templateHook_Prestart_ConsulWI(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_templateHook_Prestart_Vault(t *testing.T) {
|
||||
ci.Parallel(t)
|
||||
|
||||
secretsResp := `
|
||||
{
|
||||
"data": {
|
||||
"data": {
|
||||
"secret": "secret"
|
||||
},
|
||||
"metadata": {
|
||||
"created_time": "2023-10-18T15:58:29.65137Z",
|
||||
"custom_metadata": null,
|
||||
"deletion_time": "",
|
||||
"destroyed": false,
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// Start test server to simulate Vault cluster responses.
|
||||
reqCh := make(chan any)
|
||||
defaultVaultServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
reqCh <- struct{}{}
|
||||
fmt.Fprintln(w, secretsResp)
|
||||
}))
|
||||
t.Cleanup(defaultVaultServer.Close)
|
||||
|
||||
// Setup client with Vault config.
|
||||
clientConfig := config.DefaultConfig()
|
||||
clientConfig.TemplateConfig.DisableSandbox = true
|
||||
clientConfig.VaultConfigs = map[string]*structsc.VaultConfig{
|
||||
structs.VaultDefaultCluster: {
|
||||
Name: structs.VaultDefaultCluster,
|
||||
Enabled: pointer.Of(true),
|
||||
Addr: defaultVaultServer.URL,
|
||||
},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
vault *structs.Vault
|
||||
expectedCluster string
|
||||
}{
|
||||
{
|
||||
name: "use default cluster",
|
||||
vault: &structs.Vault{
|
||||
Cluster: structs.VaultDefaultCluster,
|
||||
},
|
||||
expectedCluster: structs.VaultDefaultCluster,
|
||||
},
|
||||
{
|
||||
name: "use default cluster if no vault block is provided",
|
||||
vault: nil,
|
||||
expectedCluster: structs.VaultDefaultCluster,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Setup alloc and task to connect to Vault cluster.
|
||||
alloc := mock.MinAlloc()
|
||||
task := alloc.Job.TaskGroups[0].Tasks[0]
|
||||
task.Vault = tc.vault
|
||||
|
||||
// Setup template hook.
|
||||
taskDir := t.TempDir()
|
||||
hookConfig := &templateHookConfig{
|
||||
alloc: alloc,
|
||||
logger: testlog.HCLogger(t),
|
||||
lifecycle: trtesting.NewMockTaskHooks(),
|
||||
events: &trtesting.MockEmitter{},
|
||||
clientConfig: clientConfig,
|
||||
envBuilder: taskenv.NewBuilder(mock.Node(), alloc, task, clientConfig.Region),
|
||||
templates: []*structs.Template{
|
||||
{
|
||||
EmbeddedTmpl: `{{with secret "secret/data/test"}}{{.Data.data.secret}}{{end}}`,
|
||||
ChangeMode: structs.TemplateChangeModeNoop,
|
||||
DestPath: path.Join(taskDir, "out.txt"),
|
||||
},
|
||||
},
|
||||
}
|
||||
hook := newTemplateHook(hookConfig)
|
||||
|
||||
// Start template hook with a timeout context to ensure it exists.
|
||||
req := &interfaces.TaskPrestartRequest{
|
||||
Task: task,
|
||||
TaskDir: &allocdir.TaskDir{Dir: taskDir},
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
t.Cleanup(cancel)
|
||||
|
||||
// Start in a goroutine because Prestart() blocks until first
|
||||
// render.
|
||||
hookErrCh := make(chan error)
|
||||
go func() {
|
||||
err := hook.Prestart(ctx, req, nil)
|
||||
hookErrCh <- err
|
||||
}()
|
||||
|
||||
var gotRequest bool
|
||||
LOOP:
|
||||
for {
|
||||
select {
|
||||
// Register mock Vault server received a request.
|
||||
case <-reqCh:
|
||||
gotRequest = true
|
||||
|
||||
// Verify test doesn't timeout.
|
||||
case <-ctx.Done():
|
||||
must.NoError(t, ctx.Err())
|
||||
return
|
||||
|
||||
// Verify hook.Prestart() doesn't errors.
|
||||
case err := <-hookErrCh:
|
||||
must.NoError(t, err)
|
||||
break LOOP
|
||||
}
|
||||
}
|
||||
|
||||
// Verify mock Vault server received a request.
|
||||
must.True(t, gotRequest)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user