kubernetes/kops · error
incorrect Audience
Error message
incorrect Audience
What it means
The verifier rejects the token because its Audience claim does not equal pkibootstrap.AudienceNodeAuthentication ("kops.k8s.io/node-bootstrap"). This is a replay/cross-use guard: tokens minted for one purpose (or another audience) must not be replayed to the node-bootstrap endpoint. The token itself decoded and its signature was NOT yet checked at this point — this check runs before signature verification.
Source
Thrown at pkg/bootstrap/pkibootstrap/pkiverifier/verifier.go:89
tokenBytes, err := base64.StdEncoding.DecodeString(authToken)
if err != nil {
return nil, nil, fmt.Errorf("decoding authorization token: %w", err)
}
token := &pkibootstrap.AuthToken{}
if err = json.Unmarshal(tokenBytes, token); err != nil {
return nil, nil, fmt.Errorf("unmarshalling authorization token: %w", err)
}
tokenData := &pkibootstrap.AuthTokenData{}
if err := json.Unmarshal(token.Data, tokenData); err != nil {
return nil, nil, fmt.Errorf("unmarshalling authorization token data: %w", err)
}
// Guard against replay attacks
if tokenData.Audience != pkibootstrap.AudienceNodeAuthentication {
return nil, nil, fmt.Errorf("incorrect Audience")
}
timeSkew := math.Abs(time.Since(time.Unix(tokenData.Timestamp, 0)).Seconds())
if timeSkew > float64(v.opt.MaxTimeSkew) {
return nil, nil, fmt.Errorf("incorrect Timestamp %v", tokenData.Timestamp)
}
// Verify the token has signed the body content.
requestHash := sha256.Sum256(body)
if !bytes.Equal(requestHash[:], tokenData.RequestHash) {
return nil, nil, fmt.Errorf("incorrect RequestHash")
}
return token, tokenData, nil
}
// Can generate keys with
// openssl ecparam -name prime256v1 -genkey -noout -out ec-priv-key.pem
// openssl ec -in ec-priv-key.pem -pubout > ec-pub-key.pemView on GitHub (pinned to 4c8573c808)
Solutions
- Regenerate the token on the node using the current pkiAuthenticator.CreateToken, which always sets Audience: AudienceNodeAuthentication.
- Align nodeup and kops-controller to the same kOps version so both sides agree on the audience constant.
- If using a custom authenticator, set Audience: pkibootstrap.AudienceNodeAuthentication in the AuthTokenData before signing.
- Inspect the failing token's claims (base64-decode, then json.Unmarshal the Data field) to confirm the audience value actually sent.
Example fix
// before
data := AuthTokenData{Timestamp: time.Now().Unix(), RequestHash: requestHash[:], KeyID: keyID, Instance: hostname}
// after
data := AuthTokenData{Timestamp: time.Now().Unix(), Audience: pkibootstrap.AudienceNodeAuthentication, RequestHash: requestHash[:], KeyID: keyID, Instance: hostname} Defensive patterns
Strategy: validation
Validate before calling
// Verify the audience claim before sending the bootstrap request
func audienceIsNodeBootstrap(authHeader, prefix string) bool {
raw, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(authHeader, prefix))
if err != nil {
return false
}
var t pkibootstrap.AuthToken
if json.Unmarshal(raw, &t) != nil {
return false
}
var d pkibootstrap.AuthTokenData
if json.Unmarshal(t.Data, &d) != nil {
return false
}
return d.Audience == pkibootstrap.AudienceNodeAuthentication
} Type guard
func hasNodeBootstrapAudience(d *pkibootstrap.AuthTokenData) bool {
return d != nil && d.Audience == pkibootstrap.AudienceNodeAuthentication
} Try / catch
result, err := verifier.VerifyToken(ctx, req, authToken, body)
if err != nil {
if strings.Contains(err.Error(), "incorrect Audience") {
// do not retry with the same token; it was minted for the wrong audience
klog.Errorf("bootstrap token audience rejected: %v", err)
return nil, fmt.Errorf("token audience mismatch: %w", err)
}
return nil, err
} Prevention
- Reference pkibootstrap.AudienceNodeAuthentication as a constant in custom minting code — never hardcode the string.
- Keep nodeup and kops-controller versions aligned; the audience contract can change between releases.
- Never reuse tokens minted for other kOps endpoints or audiences.
- Unit-test any custom authenticator against the real verifier before deploying.
When it happens
Trigger: parseTokenData (verifier.go:88) raises this whenever tokenData.Audience != "kops.k8s.io/node-bootstrap": the claim is empty (older client that omits audience), the client sets a different audience string, the token was minted for a different kOps endpoint/audience and replayed here, or a custom authenticator builds AuthTokenData without setting Audience.
Common situations: Version skew between nodeup and kops-controller (audience field added/renamed between releases); a homegrown bootstrap client that forgot the Audience field; copying tokens between environments or endpoints; tampering attempts caught by the guard as designed.
Related errors
- unmarshalling authorization token data: %w
- incorrect Timestamp %v
- incorrect RequestHash
- failed to verify claim signature for node
- node identity is required
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/66fbb2d68abb141c.
Report an issue: GitHub.