{"record":{"id":"2ea44416fca180d4","repo":"hashicorp/nomad","slug":"identities-without-an-audience-are-insecure","errorCode":null,"errorMessage":"identities without an audience are insecure","messagePattern":"identities without an audience are insecure","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"nomad/structs/workload_id.go","lineNumber":508,"sourceCode":"\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"ttl must be >= 0\"))\n\t}\n\n\tif wi.Filepath != \"\" && !wi.File {\n\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"file parameter must be true in order to specify filepath\"))\n\t}\n\n\treturn mErr.ErrorOrNil()\n}\n\nfunc (wi *WorkloadIdentity) Warnings() error {\n\tif wi == nil {\n\t\treturn fmt.Errorf(\"must not be nil\")\n\t}\n\n\tvar mErr multierror.Error\n\n\tif n := len(wi.Audience); n == 0 {\n\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"identities without an audience are insecure\"))\n\t} else if n > 1 {\n\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"while multiple audiences is allowed, it is more secure to use 1 audience per identity\"))\n\t}\n\n\tif wi.Name != \"\" && wi.Name != WorkloadIdentityDefaultName {\n\t\tif wi.TTL == 0 {\n\t\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"identities without an expiration are insecure\"))\n\t\t}\n\t}\n\n\t// Warn users about using env vars without restarts\n\tif wi.Env && wi.ChangeMode != WIChangeModeRestart {\n\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"using env=%t without change_mode=%q may result in task not getting updated identity\",\n\t\t\twi.Env, WIChangeModeRestart))\n\t}\n\n\treturn mErr.ErrorOrNil()\n}","sourceCodeStart":490,"sourceCodeEnd":526,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/workload_id.go#L490-L526","documentation":"WorkloadIdentity.Warnings flags identities that declare no Audience. Audience-less workload identities are considered insecure because the resulting JWT could be accepted by any workload validator; Nomad emits this as a warning, not a hard validation failure.","triggerScenarios":"An identity block with no audience entries (len(wi.Audience) == 0) when Warnings() is called, e.g. identity { name = \"aws\" } with no audience list in HCL or structs.WorkloadIdentity{Audience: nil} in Go.","commonSituations":"Omitting audience because the docs example omitted it; assuming Nomad injects a default audience; older job files written before audience best practices were adopted.","solutions":["Add one audience entry to the identity block, e.g. audience = [\"aws\"] matching the target workload's expected aud claim.","Set Audience in Go: wi.Audience = []string{\"aws\"} before validation.","If the warning is intentional for local testing, acknowledge and suppress it in your tooling, but add audiences before production."],"exampleFix":"// before\nidentity {\n  name = \"aws\"\n}\n// after\nidentity {\n  name = \"aws\"\n  audience = [\"aws\"]\n}","handlingStrategy":"validation","validationCode":"func validateAudience(wi *structs.WorkloadIdentity) error {\n  if len(wi.Audience) == 0 {\n    return fmt.Errorf(\"identity %q must declare at least one audience\", wi.Name)\n  }\n  return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Require an audience field in your job-spec templates/linter.","Match each audience to a specific workload validator (e.g. AWS role, Vault role).","Fail CI on identities missing audience rather than waiting for runtime warnings."],"tags":["nomad","workload-identity","security","jwt"],"backgroundTag":"missing-audience-claim","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}