Skip to content

Commit

Permalink
store key ID used for workload identity signing with alloc
Browse files Browse the repository at this point in the history
  • Loading branch information
tgross committed Oct 31, 2022
1 parent e880bce commit 6c3a830
Show file tree
Hide file tree
Showing 6 changed files with 29 additions and 14 deletions.
8 changes: 4 additions & 4 deletions nomad/encrypter.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ const keyIDHeader = "kid"

// SignClaims signs the identity claim for the task and returns an
// encoded JWT with both the claim and its signature
func (e *Encrypter) SignClaims(claim *structs.IdentityClaims) (string, error) {
func (e *Encrypter) SignClaims(claim *structs.IdentityClaims) (string, string, error) {

// If a key is rotated immediately following a leader election, plans that
// are in-flight may get signed before the new leader has the key. Allow for
Expand All @@ -164,7 +164,7 @@ func (e *Encrypter) SignClaims(claim *structs.IdentityClaims) (string, error) {
for {
select {
case <-ctx.Done():
return "", err
return "", "", err
default:
time.Sleep(50 * time.Millisecond)
keyset, err = e.activeKeySet()
Expand All @@ -180,10 +180,10 @@ func (e *Encrypter) SignClaims(claim *structs.IdentityClaims) (string, error) {

tokenString, err := token.SignedString(keyset.privateKey)
if err != nil {
return "", err
return "", "", err
}

return tokenString, nil
return tokenString, keyset.rootKey.Meta.KeyID, nil
}

// VerifyClaim accepts a previously-signed encoded claim and validates
Expand Down
2 changes: 1 addition & 1 deletion nomad/encrypter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ func TestEncrypter_SignVerify(t *testing.T) {
claim := alloc.ToTaskIdentityClaims(nil, "web")
e := srv.encrypter

out, err := e.SignClaims(claim)
out, _, err := e.SignClaims(claim)
require.NoError(t, err)

got, err := e.VerifyClaim(out)
Expand Down
3 changes: 2 additions & 1 deletion nomad/plan_apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -415,11 +415,12 @@ func (p *planner) signAllocIdentities(job *structs.Job, allocations []*structs.A
tg := job.LookupTaskGroup(alloc.TaskGroup)
for _, task := range tg.Tasks {
claims := alloc.ToTaskIdentityClaims(job, task.Name)
token, err := encrypter.SignClaims(claims)
token, keyID, err := encrypter.SignClaims(claims)
if err != nil {
return err
}
alloc.SignedIdentities[task.Name] = token
alloc.SigningKeyID = keyID
}
}
return nil
Expand Down
11 changes: 11 additions & 0 deletions nomad/state/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const (
TableVariablesQuotas = "variables_quota"
TableRootKeyMeta = "root_key_meta"
TableACLRoles = "acl_roles"
TableAllocs = "allocs"
)

const (
Expand All @@ -31,6 +32,7 @@ const (
indexKeyID = "key_id"
indexPath = "path"
indexName = "name"
indexSigningKey = "signing_key"
)

var (
Expand Down Expand Up @@ -686,6 +688,15 @@ func allocTableSchema() *memdb.TableSchema {
Field: "DeploymentID",
},
},

indexSigningKey: {
Name: indexSigningKey,
AllowMissing: true,
Unique: false,
Indexer: &memdb.UUIDFieldIndex{
Field: "SigningKeyID",
},
},
},
}
}
Expand Down
9 changes: 6 additions & 3 deletions nomad/structs/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -9717,11 +9717,14 @@ type Allocation struct {
// to stop running because it got preempted
PreemptedByAllocation string

// SignedIdentities is a map of task names to signed
// identity/capability claim tokens for those tasks. If needed, it
// is populated in the plan applier
// SignedIdentities is a map of task names to signed identity/capability
// claim tokens for those tasks. If needed, it is populated in the plan
// applier.
SignedIdentities map[string]string `json:"-"`

// SigningKeyID is the key used to sign the SignedIdentities field.
SigningKeyID string

// Raft Indexes
CreateIndex uint64
ModifyIndex uint64
Expand Down
10 changes: 5 additions & 5 deletions nomad/variables_endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,15 @@ func TestVariablesEndpoint_auth(t *testing.T) {
structs.MsgTypeTestSetup, 1001, []*structs.Allocation{alloc1, alloc2, alloc3, alloc4}))

claims1 := alloc1.ToTaskIdentityClaims(nil, "web")
idToken, err := srv.encrypter.SignClaims(claims1)
idToken, _, err := srv.encrypter.SignClaims(claims1)
must.NoError(t, err)

claims2 := alloc2.ToTaskIdentityClaims(nil, "web")
noPermissionsToken, err := srv.encrypter.SignClaims(claims2)
noPermissionsToken, _, err := srv.encrypter.SignClaims(claims2)
must.NoError(t, err)

claims3 := alloc3.ToTaskIdentityClaims(alloc3.Job, "web")
idDispatchToken, err := srv.encrypter.SignClaims(claims3)
idDispatchToken, _, err := srv.encrypter.SignClaims(claims3)
must.NoError(t, err)

// corrupt the signature of the token
Expand All @@ -83,7 +83,7 @@ func TestVariablesEndpoint_auth(t *testing.T) {
invalidIDToken := strings.Join(idTokenParts, ".")

claims4 := alloc4.ToTaskIdentityClaims(alloc4.Job, "web")
wiOnlyToken, err := srv.encrypter.SignClaims(claims4)
wiOnlyToken, _, err := srv.encrypter.SignClaims(claims4)
must.NoError(t, err)

policy := mock.ACLPolicy()
Expand Down Expand Up @@ -570,7 +570,7 @@ func TestVariablesEndpoint_ListFiltering(t *testing.T) {
structs.MsgTypeTestSetup, idx, []*structs.Allocation{alloc}))

claims := alloc.ToTaskIdentityClaims(alloc.Job, "web")
token, err := srv.encrypter.SignClaims(claims)
token, _, err := srv.encrypter.SignClaims(claims)
must.NoError(t, err)

writeVar := func(ns, path string) {
Expand Down

0 comments on commit 6c3a830

Please sign in to comment.