diff --git a/nomad/encrypter_test.go b/nomad/encrypter_test.go index f620a16f4..f1fa95faf 100644 --- a/nomad/encrypter_test.go +++ b/nomad/encrypter_test.go @@ -298,7 +298,7 @@ func TestEncrypter_SignVerify(t *testing.T) { testutil.WaitForLeader(t, srv.RPC) alloc := mock.Alloc() - claim := alloc.ToTaskIdentityClaims("web") + claim := alloc.ToTaskIdentityClaims(nil, "web") e := srv.encrypter out, err := e.SignClaims(claim) diff --git a/nomad/plan_apply.go b/nomad/plan_apply.go index b7b929fe6..eebc713d3 100644 --- a/nomad/plan_apply.go +++ b/nomad/plan_apply.go @@ -415,7 +415,7 @@ func (p *planner) signAllocIdentities(job *structs.Job, allocations []*structs.A alloc.SignedIdentities = map[string]string{} tg := job.LookupTaskGroup(alloc.TaskGroup) for _, task := range tg.Tasks { - claims := alloc.ToTaskIdentityClaims(task.Name) + claims := alloc.ToTaskIdentityClaims(job, task.Name) token, err := encrypter.SignClaims(claims) if err != nil { return err diff --git a/nomad/secure_variables_endpoint.go b/nomad/secure_variables_endpoint.go index dd30655a8..9db43bf15 100644 --- a/nomad/secure_variables_endpoint.go +++ b/nomad/secure_variables_endpoint.go @@ -515,7 +515,7 @@ func (sv *SecureVariables) authValidatePrefix(claims *structs.IdentityClaims, ns } parts := strings.Split(pathOrPrefix, "/") - expect := []string{"nomad", "jobs", alloc.Job.ID, alloc.TaskGroup, claims.TaskName} + expect := []string{"nomad", "jobs", claims.JobID, alloc.TaskGroup, claims.TaskName} if len(parts) > len(expect) { return structs.ErrPermissionDenied } diff --git a/nomad/secure_variables_endpoint_test.go b/nomad/secure_variables_endpoint_test.go index 70db95f7b..1ab34a4b0 100644 --- a/nomad/secure_variables_endpoint_test.go +++ b/nomad/secure_variables_endpoint_test.go @@ -39,19 +39,29 @@ func TestSecureVariablesEndpoint_auth(t *testing.T) { alloc2.Job.Namespace = ns alloc2.Namespace = ns + alloc3 := mock.Alloc() + alloc3.ClientStatus = structs.AllocClientStatusRunning + alloc3.Job.Namespace = ns + alloc3.Namespace = ns + alloc3.Job.ParentID = jobID + store := srv.fsm.State() require.NoError(t, store.UpsertNamespaces(1000, []*structs.Namespace{{Name: ns}})) require.NoError(t, store.UpsertAllocs( - structs.MsgTypeTestSetup, 1001, []*structs.Allocation{alloc1, alloc2})) + structs.MsgTypeTestSetup, 1001, []*structs.Allocation{alloc1, alloc2, alloc3})) - claims1 := alloc1.ToTaskIdentityClaims("web") + claims1 := alloc1.ToTaskIdentityClaims(nil, "web") idToken, err := srv.encrypter.SignClaims(claims1) require.NoError(t, err) - claims2 := alloc2.ToTaskIdentityClaims("web") + claims2 := alloc2.ToTaskIdentityClaims(nil, "web") noPermissionsToken, err := srv.encrypter.SignClaims(claims2) require.NoError(t, err) + claims3 := alloc3.ToTaskIdentityClaims(alloc3.Job, "web") + idDispatchToken, err := srv.encrypter.SignClaims(claims3) + require.NoError(t, err) + // corrupt the signature of the token idTokenParts := strings.Split(idToken, ".") require.Len(t, idTokenParts, 3) @@ -125,6 +135,13 @@ func TestSecureVariablesEndpoint_auth(t *testing.T) { path: fmt.Sprintf("nomad/jobs/%s", jobID), expectedErr: nil, }, + { + name: "valid claim for path with dispatch job secret", + token: idDispatchToken, + cap: "n/a", + path: fmt.Sprintf("nomad/jobs/%s", jobID), + expectedErr: nil, + }, { name: "valid claim for path with namespace secret", token: idToken, @@ -201,6 +218,13 @@ func TestSecureVariablesEndpoint_auth(t *testing.T) { path: fmt.Sprintf("nomad/jobs/%s/web/web", jobID), expectedErr: structs.ErrPermissionDenied, }, + { + name: "invalid claim for dispatched ID", + token: idDispatchToken, + cap: "n/a", + path: fmt.Sprintf("nomad/jobs/%s", alloc3.JobID), + expectedErr: structs.ErrPermissionDenied, + }, { name: "acl token read policy is allowed to list", token: aclToken.SecretID, diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index 4d6e9bc38..411052787 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -10312,9 +10312,9 @@ func (a *Allocation) Reconnected() (bool, bool) { return true, a.Expired(lastReconnect) } -func (a *Allocation) ToIdentityClaims() *IdentityClaims { +func (a *Allocation) ToIdentityClaims(job *Job) *IdentityClaims { now := jwt.NewNumericDate(time.Now().UTC()) - return &IdentityClaims{ + claims := &IdentityClaims{ Namespace: a.Namespace, JobID: a.JobID, AllocationID: a.ID, @@ -10327,10 +10327,14 @@ func (a *Allocation) ToIdentityClaims() *IdentityClaims { IssuedAt: now, }, } + if job != nil && job.ParentID != "" { + claims.JobID = job.ParentID + } + return claims } -func (a *Allocation) ToTaskIdentityClaims(taskName string) *IdentityClaims { - claims := a.ToIdentityClaims() +func (a *Allocation) ToTaskIdentityClaims(job *Job, taskName string) *IdentityClaims { + claims := a.ToIdentityClaims(job) if claims != nil { claims.TaskName = taskName }