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.pem

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Regenerate the token on the node using the current pkiAuthenticator.CreateToken, which always sets Audience: AudienceNodeAuthentication.
  2. Align nodeup and kops-controller to the same kOps version so both sides agree on the audience constant.
  3. If using a custom authenticator, set Audience: pkibootstrap.AudienceNodeAuthentication in the AuthTokenData before signing.
  4. 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

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


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/66fbb2d68abb141c. Report an issue: GitHub.