diff --git a/api/tasks.go b/api/tasks.go index 308b48b7c..8b94e488c 100644 --- a/api/tasks.go +++ b/api/tasks.go @@ -929,6 +929,7 @@ func (tmpl *Template) Canonicalize() { type Vault struct { Policies []string `hcl:"policies,optional"` + Role string `hcl:"role,optional"` Namespace *string `mapstructure:"namespace" hcl:"namespace,optional"` Env *bool `hcl:"env,optional"` DisableFile *bool `mapstructure:"disable_file" hcl:"disable_file,optional"` diff --git a/command/agent/job_endpoint.go b/command/agent/job_endpoint.go index c0b9008ae..2df06df26 100644 --- a/command/agent/job_endpoint.go +++ b/command/agent/job_endpoint.go @@ -1285,6 +1285,7 @@ func ApiTaskToStructsTask(job *structs.Job, group *structs.TaskGroup, if apiTask.Vault != nil { structsTask.Vault = &structs.Vault{ + Role: apiTask.Vault.Role, Policies: apiTask.Vault.Policies, Namespace: *apiTask.Vault.Namespace, Env: *apiTask.Vault.Env, diff --git a/command/agent/job_endpoint_test.go b/command/agent/job_endpoint_test.go index 4fb9df086..b66c67abf 100644 --- a/command/agent/job_endpoint_test.go +++ b/command/agent/job_endpoint_test.go @@ -2784,6 +2784,7 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) { }, }, Vault: &api.Vault{ + Role: "nomad-task", Namespace: pointer.Of("ns1"), Policies: []string{"a", "b", "c"}, Env: pointer.Of(true), @@ -3206,6 +3207,7 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) { }, }, Vault: &structs.Vault{ + Role: "nomad-task", Namespace: "ns1", Policies: []string{"a", "b", "c"}, Env: true, diff --git a/nomad/structs/diff_test.go b/nomad/structs/diff_test.go index 412c8cd82..1244bc731 100644 --- a/nomad/structs/diff_test.go +++ b/nomad/structs/diff_test.go @@ -6912,6 +6912,7 @@ func TestTaskDiff(t *testing.T) { Old: &Task{}, New: &Task{ Vault: &Vault{ + Role: "nomad-task", Policies: []string{"foo", "bar"}, Env: true, DisableFile: true, @@ -6950,6 +6951,12 @@ func TestTaskDiff(t *testing.T) { Old: "", New: "true", }, + { + Type: DiffTypeAdded, + Name: "Role", + Old: "", + New: "nomad-task", + }, }, Objects: []*ObjectDiff{ { @@ -7047,6 +7054,7 @@ func TestTaskDiff(t *testing.T) { Name: "Vault edited", Old: &Task{ Vault: &Vault{ + Role: "nomad-task", Namespace: "ns1", Policies: []string{"foo", "bar"}, Env: true, @@ -7057,6 +7065,7 @@ func TestTaskDiff(t *testing.T) { }, New: &Task{ Vault: &Vault{ + Role: "nomad-task-2", Namespace: "ns2", Policies: []string{"bar", "baz"}, Env: false, @@ -7102,6 +7111,12 @@ func TestTaskDiff(t *testing.T) { Old: "ns1", New: "ns2", }, + { + Type: DiffTypeEdited, + Name: "Role", + Old: "nomad-task", + New: "nomad-task-2", + }, }, Objects: []*ObjectDiff{ { @@ -7132,6 +7147,7 @@ func TestTaskDiff(t *testing.T) { Contextual: true, Old: &Task{ Vault: &Vault{ + Role: "nomad-task", Namespace: "ns1", Policies: []string{"foo", "bar"}, Env: true, @@ -7142,6 +7158,7 @@ func TestTaskDiff(t *testing.T) { }, New: &Task{ Vault: &Vault{ + Role: "nomad-task", Namespace: "ns1", Policies: []string{"bar", "baz"}, Env: true, @@ -7187,6 +7204,12 @@ func TestTaskDiff(t *testing.T) { Old: "ns1", New: "ns1", }, + { + Type: DiffTypeNone, + Name: "Role", + Old: "nomad-task", + New: "nomad-task", + }, }, Objects: []*ObjectDiff{ { diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index c7871b392..df09ba821 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -9808,6 +9808,12 @@ const ( // Vault stores the set of permissions a task needs access to from Vault. type Vault struct { + // Role is the Vault role used to login to Vault using a JWT. + // + // If empty, defaults to the server's create_from_role value or the Vault + // cluster default role. + Role string + // Policies is the set of policies that the task needs access to Policies []string @@ -9836,6 +9842,8 @@ func (v *Vault) Equal(o *Vault) bool { return v == o } switch { + case v.Role != o.Role: + return false case !slices.Equal(v.Policies, o.Policies): return false case v.Namespace != o.Namespace: diff --git a/nomad/structs/structs_test.go b/nomad/structs/structs_test.go index 40e50baed..07e2a739e 100644 --- a/nomad/structs/structs_test.go +++ b/nomad/structs/structs_test.go @@ -7668,12 +7668,16 @@ func TestVault_Equal(t *testing.T) { must.NotEqual[*Vault](t, nil, new(Vault)) must.StructEqual(t, &Vault{ + Role: "nomad-task", Policies: []string{"one"}, Namespace: "global", Env: true, ChangeMode: "signal", ChangeSignal: "SIGILL", }, []must.Tweak[*Vault]{{ + Field: "Role", + Apply: func(v *Vault) { v.Role = "nomad-task-2" }, + }, { Field: "Policies", Apply: func(v *Vault) { v.Policies = []string{"two"} }, }, { diff --git a/website/content/docs/job-specification/vault.mdx b/website/content/docs/job-specification/vault.mdx index cfe5a1893..0656d7273 100644 --- a/website/content/docs/job-specification/vault.mdx +++ b/website/content/docs/job-specification/vault.mdx @@ -83,7 +83,6 @@ with Vault as well. isolation. - - `namespace` `(string: "")` - Specifies the Vault Namespace to use for the task. The Nomad client will retrieve a Vault token that is scoped to this particular namespace. @@ -92,6 +91,10 @@ with Vault as well. the task requires. The Nomad client will retrieve a Vault token that is limited to those policies. +- `role` `(string: "")` - Specifies the Vault role used when retrieving a token + from Vault using JWT and workload identity. If not specified the client's + [`create_from_role`][] value is used. + ## `vault` Examples The following examples only show the `vault` blocks. Remember that the @@ -191,6 +194,7 @@ vault { } ``` +[`create_from_role`]: /nomad/docs/configuration/vault#create_from_role [docker]: /nomad/docs/drivers/docker "Docker Driver" [restart]: /nomad/docs/job-specification/restart "Nomad restart Job Specification" [template]: /nomad/docs/job-specification/template "Nomad template Job Specification"