kubernetes/kops · error

unmarshalling authorization token data: %w

Error message

unmarshalling authorization token data: %w

What it means

This error is returned by parseTokenData in the kops-controller PKI verifier when the inner claims payload (AuthToken.Data, the signed JSON claims blob inside the outer AuthToken envelope) fails to json.Unmarshal into pkibootstrap.AuthTokenData. The outer token parsed fine and base64 decoding succeeded, but the Data field is not valid JSON matching the AuthTokenData schema (instance, keyID, requestHash, timestamp, audience). It indicates a malformed or truncated token, almost always produced by a client-side serialization problem rather than an attacker.

Source

Thrown at pkg/bootstrap/pkibootstrap/pkiverifier/verifier.go:84

func (v *verifier) parseTokenData(tokenPrefix string, authToken string, body []byte) (*pkibootstrap.AuthToken, *pkibootstrap.AuthTokenData, error) {
	if !strings.HasPrefix(authToken, tokenPrefix) {
		return nil, nil, bootstrap.ErrNotThisVerifier
	}
	authToken = strings.TrimPrefix(authToken, tokenPrefix)

	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

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Ensure the node is running the nodeup/kops version matching the kops-controller, so CreateToken (pkg/bootstrap/pkibootstrap/pkisigner.go:104) builds the token instead of hand-rolling it.
  2. Decode the token offline to inspect it: strip the prefix, base64-std-decode, json.Unmarshal into AuthToken, then check that Data is valid JSON with the expected AuthTokenData fields.
  3. Verify no proxy/ingress in front of kops-controller rewrites the Authorization header or truncates the body.
  4. Check that Data was set to the exact []byte payload that was signed (json.Marshal of AuthTokenData), not a re-encoded or pretty-printed copy.

Example fix

// before: hand-built token with string fields
token := map[string]string{"data": base64.StdEncoding.EncodeToString(payload), "signature": sigB64}
// after: use the library's types so field types match AuthTokenData
token := &pkibootstrap.AuthToken{Data: payload, Signature: signature}
b, _ := json.Marshal(token)
header := pkibootstrap.AuthenticationTokenPrefix + base64.StdEncoding.EncodeToString(b)
Defensive patterns

Strategy: validation

Validate before calling

// Decode and shape-check the token before sending it to kops-controller
func tokenDataLooksValid(authHeader string) bool {
	const prefix = "kops.k8s.io/1.30/pki" // AuthenticationTokenPrefix of your kOps version
	if !strings.HasPrefix(authHeader, prefix) {
		return false
	}
	raw, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(authHeader, prefix))
	if err != nil {
		return false
	}
	var t pkibootstrap.AuthToken
	if err := json.Unmarshal(raw, &t); err != nil || len(t.Data) == 0 {
		return false
	}
	var d pkibootstrap.AuthTokenData
	return json.Unmarshal(t.Data, &d) == nil && d.Instance != ""
}

Type guard

func isAuthTokenData(b []byte) (*pkibootstrap.AuthTokenData, bool) {
	var d pkibootstrap.AuthTokenData
	if err := json.Unmarshal(b, &d); err != nil {
		return nil, false
	}
	return &d, true
}

Try / catch

result, err := verifier.VerifyToken(ctx, req, authToken, body)
if err != nil {
	if strings.Contains(err.Error(), "unmarshalling authorization token data") {
		// token payload is malformed; log the decoded claims for diagnosis and fail the request
		klog.Errorf("bootstrap token has invalid Data payload: %v", err)
		return nil, fmt.Errorf("malformed bootstrap token: %w", err)
	}
	return nil, err
}

Prevention

When it happens

Trigger: Occurs inside VerifyToken (called by kops-controller when nodeup posts a bootstrap request with a Authorization header carrying the AuthenticationTokenPrefix token) whenever json.Unmarshal(token.Data, &AuthTokenData{}) fails: Data is empty/null, Data is not valid JSON (e.g. base64 of raw bytes instead of JSON), the JSON is truncated, or field types mismatch (e.g. requestHash encoded as a base64 string instead of []byte, timestamp as a string instead of int64) due to a client/server version skew in the token format.

Common situations: A custom or hand-rolled node bootstrap client that builds the Authorization header manually and double-encodes or omits the Data field; a kOps version skew where nodeup from a different release emits an older token layout; a proxy or middleware that rewrites/strips the Authorization header body; debugging reproducers that paste a partially-copied token.

Related errors


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