mirror of
https://github.com/kemko/nomad.git
synced 2026-01-01 16:05:42 +03:00
When we renew Vault tokens, we use the lease duration to determine how often to renew. But we also set an `increment` value which is never updated from the initial 30s. For periodic tokens this is not a problem because the `increment` field is ignored on renewal. But for non-periodic tokens this prevents the token TTL from being properly incremented. This behavior has been in place since the initial Vault client implementation in #1606 but before the switch to workload identity most (all?) tokens being created were periodic tokens so this was never detected. Fix this bug by updating the request's `increment` field to the lease duration on each renewal. Also switch out a `time.After` call in backoff of the derive token caller with a safe timer so that we don't have to spawn a new goroutine per loop, and have tighter control over when that's GC'd. Ref: https://github.com/hashicorp/nomad/pull/1606 Ref: https://github.com/hashicorp/nomad/issues/25812
89 lines
2.5 KiB
Go
89 lines
2.5 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package taskrunner
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/nomad/ci"
|
|
"github.com/hashicorp/nomad/client/vaultclient"
|
|
"github.com/hashicorp/nomad/nomad/mock"
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
"github.com/hashicorp/nomad/plugins/drivers/fsisolation"
|
|
"github.com/shoenig/test/must"
|
|
)
|
|
|
|
func TestTaskRunner_DisableFileForVaultToken_UpgradePath(t *testing.T) {
|
|
ci.Parallel(t)
|
|
ci.SkipTestWithoutRootAccess(t)
|
|
|
|
// Create test allocation with a Vault block.
|
|
alloc := mock.BatchAlloc()
|
|
task := alloc.Job.TaskGroups[0].Tasks[0]
|
|
task.Config = map[string]any{
|
|
"run_for": "0s",
|
|
}
|
|
task.Vault = &structs.Vault{
|
|
Cluster: structs.VaultDefaultCluster,
|
|
}
|
|
|
|
// Setup a test Vault client.
|
|
token := "1234"
|
|
handler := func(ctx context.Context, req vaultclient.JWTLoginRequest) (string, bool, int, error) {
|
|
return token, true, 30, nil
|
|
}
|
|
vc, err := vaultclient.NewMockVaultClient(structs.VaultDefaultCluster)
|
|
must.NoError(t, err)
|
|
vaultClient := vc.(*vaultclient.MockVaultClient)
|
|
vaultClient.SetDeriveTokenWithJWTFn(handler)
|
|
|
|
conf, cleanup := testTaskRunnerConfig(t, alloc, task.Name, vaultClient)
|
|
defer cleanup()
|
|
|
|
// Remove private dir and write the Vault token to the secrets dir to
|
|
// simulate an old task.
|
|
err = conf.TaskDir.Build(fsisolation.None, nil, task.User)
|
|
must.NoError(t, err)
|
|
|
|
err = syscall.Unmount(conf.TaskDir.PrivateDir, 0)
|
|
must.NoError(t, err)
|
|
err = os.Remove(conf.TaskDir.PrivateDir)
|
|
must.NoError(t, err)
|
|
|
|
tokenPath := filepath.Join(conf.TaskDir.SecretsDir, vaultTokenFile)
|
|
err = os.WriteFile(tokenPath, []byte(token), 0666)
|
|
must.NoError(t, err)
|
|
|
|
// Start task runner and wait for task to finish.
|
|
tr, err := NewTaskRunner(conf)
|
|
must.NoError(t, err)
|
|
defer tr.Kill(context.Background(), structs.NewTaskEvent("cleanup"))
|
|
go tr.Run()
|
|
time.Sleep(500 * time.Millisecond)
|
|
|
|
testWaitForTaskToDie(t, tr)
|
|
|
|
// Verify task exited successfully.
|
|
finalState := tr.TaskState()
|
|
must.Eq(t, structs.TaskStateDead, finalState.State)
|
|
must.False(t, finalState.Failed)
|
|
|
|
// Verify token is in secrets dir.
|
|
tokenPath = filepath.Join(conf.TaskDir.SecretsDir, vaultTokenFile)
|
|
data, err := os.ReadFile(tokenPath)
|
|
must.NoError(t, err)
|
|
must.Eq(t, token, string(data))
|
|
|
|
// Varify token is not in private dir since the allocation doesn't have
|
|
// this path.
|
|
tokenPath = filepath.Join(conf.TaskDir.PrivateDir, vaultTokenFile)
|
|
_, err = os.Stat(tokenPath)
|
|
must.ErrorIs(t, err, os.ErrNotExist)
|
|
}
|