diff --git a/.changelog/28133.txt b/.changelog/28133.txt new file mode 100644 index 00000000000..646894e3553 --- /dev/null +++ b/.changelog/28133.txt @@ -0,0 +1,3 @@ +```release-note:improvement +consul: Added the issuing Nomad client's node ID to the metadata of Consul tokens created via workload identity +``` diff --git a/client/allocrunner/consul_hook.go b/client/allocrunner/consul_hook.go index bbc02e8e874..b36071839ed 100644 --- a/client/allocrunner/consul_hook.go +++ b/client/allocrunner/consul_hook.go @@ -170,6 +170,7 @@ func (h *consulHook) prepareConsulTokensForTask(task *structs.Task, tg *structs. AuthMethodName: consulConfig.TaskIdentityAuthMethod, Meta: map[string]string{ "requested_by": fmt.Sprintf("nomad_task_%s", task.Name), + "node_id": h.alloc.NodeID, }, } @@ -225,6 +226,7 @@ func (h *consulHook) prepareConsulTokensForServices(services []*structs.Service, AuthMethodName: consulConfig.ServiceIdentityAuthMethod, Meta: map[string]string{ "requested_by": fmt.Sprintf("nomad_service_%s", ti.InterpolatedWorkloadIdentifier), + "node_id": h.alloc.NodeID, }, } diff --git a/client/allocrunner/consul_hook_test.go b/client/allocrunner/consul_hook_test.go index a104b373987..988bb7b689d 100644 --- a/client/allocrunner/consul_hook_test.go +++ b/client/allocrunner/consul_hook_test.go @@ -10,6 +10,7 @@ import ( "testing" consulapi "github.com/hashicorp/consul/api" + hclog "github.com/hashicorp/go-hclog" "github.com/hashicorp/nomad/ci" "github.com/hashicorp/nomad/client/consul" cstate "github.com/hashicorp/nomad/client/state" @@ -23,7 +24,7 @@ import ( "github.com/shoenig/test/must" ) -func consulHookTestHarness(t *testing.T) *consulHook { +func consulHookTestHarness(t *testing.T) (*consulHook, *consul.MockConsulClient) { logger := testlog.HCLogger(t) alloc := mock.Alloc() @@ -69,23 +70,27 @@ func consulHookTestHarness(t *testing.T) *consulHook { hookResources := cstructs.NewAllocHookResources() + mockClient := &consul.MockConsulClient{} + consulHookCfg := consulHookConfig{ - alloc: alloc, - allocdir: nil, - widmgr: mockWIDMgr, - consulConfigs: consulConfigs, - consulClientConstructor: consul.NewMockConsulClient, - hookResources: hookResources, - db: db, - logger: logger, + alloc: alloc, + allocdir: nil, + widmgr: mockWIDMgr, + consulConfigs: consulConfigs, + consulClientConstructor: func(_ *structsc.ConsulConfig, _ hclog.Logger) (consul.Client, error) { + return mockClient, nil + }, + hookResources: hookResources, + db: db, + logger: logger, } - return newConsulHook(consulHookCfg) + return newConsulHook(consulHookCfg), mockClient } func Test_consulHook_prepareConsulTokensForTask(t *testing.T) { ci.Parallel(t) - hook := consulHookTestHarness(t) + hook, _ := consulHookTestHarness(t) task := hook.alloc.LookupTask("web") wid := task.GetIdentity("consul_default") @@ -178,7 +183,7 @@ func Test_consulHook_prepareConsulTokensForTask(t *testing.T) { func Test_consulHook_prepareConsulTokensForServices(t *testing.T) { ci.Parallel(t) - hook := consulHookTestHarness(t) + hook, _ := consulHookTestHarness(t) task := hook.alloc.LookupTask("web") services := task.Services env := taskenv.NewBuilder(mock.Node(), hook.alloc, task, "global"). @@ -258,7 +263,7 @@ func Test_consulHook_Postrun(t *testing.T) { ci.Parallel(t) // no-op must be safe - hook := consulHookTestHarness(t) + hook, _ := consulHookTestHarness(t) must.NoError(t, hook.Postrun()) task := hook.alloc.LookupTask("web") @@ -277,3 +282,21 @@ func Test_consulHook_Postrun(t *testing.T) { tokens = hook.resourcesBackend.getConsulTokens() must.MapEmpty(t, tokens["default"]) } + +func Test_consulHook_consulTokenMetaIncludesNodeID(t *testing.T) { + ci.Parallel(t) + + hook, mockClient := consulHookTestHarness(t) + task := hook.alloc.LookupTask("web") + + tokens := map[string]map[string]*consulapi.ACLToken{} + must.NoError(t, hook.prepareConsulTokensForTask(task, nil, tokens)) + + found := false + for _, req := range mockClient.Requests { + if req.Meta["node_id"] == hook.alloc.NodeID { + found = true + } + } + must.True(t, found, must.Sprint("expected node_id in the Consul login request meta")) +} diff --git a/client/consul/consul_testing.go b/client/consul/consul_testing.go index 51876d142b7..891bab1851d 100644 --- a/client/consul/consul_testing.go +++ b/client/consul/consul_testing.go @@ -14,7 +14,8 @@ import ( ) type MockConsulClient struct { - tokens map[string]*consulapi.ACLToken + tokens map[string]*consulapi.ACLToken + Requests []JWTLoginRequest } func NewMockConsulClient(config *config.ConsulConfig, logger hclog.Logger) (Client, error) { @@ -25,6 +26,8 @@ func NewMockConsulClient(config *config.ConsulConfig, logger hclog.Logger) (Clie // the request ID for the AccessorID and the md5 checksum of the request ID for // the SecretID func (mc *MockConsulClient) DeriveTokenWithJWT(req JWTLoginRequest) (*consulapi.ACLToken, error) { + mc.Requests = append(mc.Requests, req) + if t, ok := mc.tokens[req.JWT]; ok { return t, nil }