From bcb0ee30314c55258ddcce61fc8f8bc5bed8d965 Mon Sep 17 00:00:00 2001 From: Tim Gross Date: Wed, 7 Aug 2024 15:26:20 -0400 Subject: [PATCH] identity: prevent missing task identity from panicking server (#23763) Tasks have a default identity created during canonicalization. If this default identity is somehow missing, we'll hit panics when trying to create and sign the claims in the plan applier. Fallback to the default identity if it's missing from the task. This changeset will need a different implementation in 1.7.x+ent backports, as the constructor for identities was refactored significantly in #23708. The panic cannot occur in 1.6.x+ent. Fixes: https://github.com/hashicorp/nomad/issues/23758 --- .changelog/23763.txt | 3 +++ nomad/encrypter.go | 4 ++++ nomad/structs/structs.go | 5 +++-- nomad/structs/workload_id.go | 16 ++++++++++++++++ 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 .changelog/23763.txt diff --git a/.changelog/23763.txt b/.changelog/23763.txt new file mode 100644 index 000000000..e3089ee2d --- /dev/null +++ b/.changelog/23763.txt @@ -0,0 +1,3 @@ +```release-note:bug +identity: Fixed a bug where a missing default task identity could panic the leader +``` diff --git a/nomad/encrypter.go b/nomad/encrypter.go index 6b23bb9a7..0ab6c251a 100644 --- a/nomad/encrypter.go +++ b/nomad/encrypter.go @@ -242,6 +242,10 @@ const keyIDHeader = "kid" // SignClaims adds the Issuer claim prior to signing. func (e *Encrypter) SignClaims(claims *structs.IdentityClaims) (string, string, error) { + if claims == nil { + return "", "", errors.New("cannot sign empty claims") + } + // 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 // a short timeout-and-retry to avoid rejecting plans diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index 6879623a6..aad6b42a1 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -8055,9 +8055,10 @@ func (t *Task) Canonicalize(job *Job, tg *TaskGroup) { // If there was no default identity, always create one. if t.Identity == nil { - t.Identity = &WorkloadIdentity{} + t.Identity = DefaultWorkloadIdentity() + } else { + t.Identity.Canonicalize() } - t.Identity.Canonicalize() } func (t *Task) GoString() string { diff --git a/nomad/structs/workload_id.go b/nomad/structs/workload_id.go index 90a288508..c66c4bbb3 100644 --- a/nomad/structs/workload_id.go +++ b/nomad/structs/workload_id.go @@ -110,6 +110,9 @@ func NewIdentityClaimsBuilder(job *Job, alloc *Allocation, wihandle *WIHandle, w if tg == nil { return nil } + if wid == nil { + wid = DefaultWorkloadIdentity() + } return &IdentityClaimsBuilder{ alloc: alloc, @@ -318,9 +321,19 @@ type WorkloadIdentity struct { // escalate their privileges if they know what claim mappings to expect. } +func DefaultWorkloadIdentity() *WorkloadIdentity { + return &WorkloadIdentity{ + Name: WorkloadIdentityDefaultName, + Audience: []string{WorkloadIdentityDefaultAud}, + } +} + // IsConsul returns true if the identity name starts with the standard prefix // for Consul tasks and services. func (wi *WorkloadIdentity) IsConsul() bool { + if wi == nil { + return false + } return strings.HasPrefix(wi.Name, ConsulTaskIdentityNamePrefix) || strings.HasPrefix(wi.Name, ConsulServiceIdentityNamePrefix) } @@ -328,6 +341,9 @@ func (wi *WorkloadIdentity) IsConsul() bool { // IsVault returns true if the identity name starts with the standard prefix // for Vault tasks. func (wi *WorkloadIdentity) IsVault() bool { + if wi == nil { + return false + } return strings.HasPrefix(wi.Name, WorkloadIdentityVaultPrefix) }