workload identity: use parent ID for dispatch/periodic jobs (#13748)
Workload identities grant implicit access to policies, and operators will not want to craft separate policies for each invocation of a periodic or dispatch job. Use the parent job's ID as the JobID claim.
This commit is contained in:
parent
9c43c28575
commit
d11da1df5c
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue