{"record":{"id":"df419642e2d749e1","repo":"kubernetes/kops","slug":"failed-to-verify-claim-signature-for-node","errorCode":null,"errorMessage":"failed to verify claim signature for node","messagePattern":"failed to verify claim signature for node","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/bootstrap/pkibootstrap/pkiverifier/verifier.go","lineNumber":126,"sourceCode":"// Note that golang doesn't support secp256k1: https://groups.google.com/g/golang-nuts/c/Mbkug5t3ZYA\n\nfunc (v *verifier) VerifyToken(ctx context.Context, rawRequest *http.Request, authToken string, body []byte) (*bootstrap.VerifyResult, error) {\n\t// Reminder: we shouldn't trust any data we get from the client until we've checked the signature (and even then...)\n\t// Thankfully the GCE SDK does seem to escape the parameters correctly, for example.\n\n\ttoken, tokenData, err := v.parseTokenData(pkibootstrap.AuthenticationTokenPrefix, authToken, body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Verify the token has a valid signature.\n\tresult, signingKey, err := v.getSigningKey(ctx, tokenData)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !verifySignature(signingKey, token.Data, token.Signature) {\n\t\treturn nil, fmt.Errorf(\"failed to verify claim signature for node\")\n\t}\n\n\treturn result, nil\n}\n\nfunc (v *verifier) getSigningKey(ctx context.Context, tokenData *pkibootstrap.AuthTokenData) (*bootstrap.VerifyResult, crypto.PublicKey, error) {\n\tnodeName := tokenData.Instance\n\tid := types.NamespacedName{\n\t\tNamespace: \"kops-system\",\n\t\tName:      nodeName,\n\t}\n\tvar host kops.Host\n\tif err := v.client.Get(ctx, id, &host); err != nil {\n\t\tif apierrors.IsNotFound(err) {\n\t\t\treturn nil, nil, fmt.Errorf(\"host not found for %v\", id)\n\t\t}\n\t\treturn nil, nil, fmt.Errorf(\"error getting host %v: %w\", id, err)\n\t}","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/pkg/bootstrap/pkibootstrap/pkiverifier/verifier.go#L108-L144","documentation":"The ECDSA signature on the token's Data payload failed verification against the public key registered for the claimed node. The claims parsed, the audience/timestamp/hash passed, and a Host object with a public key was found — but ecdsa.VerifyASN1 (the only supported key type) returned false. This means either the token was not signed by the private key corresponding to the Host's spec.publicKey, or the signed payload differs from token.Data.","triggerScenarios":"VerifyToken (verifier.go:125) raises this when verifySignature returns false: the node's private key was rotated/regenerated but the kops Host object (kops-system/<nodeName>) still holds the old public key; the Host's spec.publicKey is for a different node; the signing key is an unsupported type (verifySignature warns \"key type %T not supported\" and returns false, e.g. RSA key); or the token bytes were corrupted after signing.","commonSituations":"Key rotation on the node (re-running bootstrap with a new key) without updating the Host CR; the wrong private key file passed to NewAuthenticatorFromFile; Host CR created with a placeholder or mismatched PEM public key; using an RSA/Ed25519 key where only ECDSA (e.g. prime256v1 per the comments at verifier.go:106) is supported; replayed or tampered tokens caught as designed.","solutions":["Confirm the node signs with the private key matching the Host's spec.publicKey: regenerate the Host's publicKey from the node's current public key (`openssl ecparam -name prime256v1 -genkey ...; openssl ec -in ec-priv-key.pem -pubout`).","Check kops-controller logs for the \"key type %T not supported\" warning — if present, switch the node key to an ECDSA key (Go supports P-256/P-384/P-521, not secp256k1).","Ensure the Host object name (tokenData.Instance) maps to the node actually sending the request; fix Instance or the Host CR if they diverge.","Rule out body/token corruption in transit (a proxy rewriting the Authorization header) and retry with a freshly minted token.","If the key was intentionally rotated, update the kops Host spec.publicKey (or delete and recreate the Host object) so the controller trusts the new key."],"exampleFix":"// before: RSA key on node, unsupported by verifier\nsigner, _ := rsa.GenerateKey(rand.Reader, 2048)\nauth, _ := pkibootstrap.NewAuthenticator(hostname, signer)\n// after: ECDSA P-256 key, matching the Host's registered public key\nsigner, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)\nauth, _ := pkibootstrap.NewAuthenticator(hostname, signer)","handlingStrategy":"try-catch","validationCode":"// Pre-flight on the node: confirm the registered Host public key matches the signer's public key\npubPEM, _ := x509.MarshalPKIXPublicKey(signer.Public())\nvar buf bytes.Buffer\npem.Encode(&buf, &pem.Block{Type: \"PUBLIC KEY\", Bytes: pubPEM})\n// fetch kops Host kops-system/<hostname> and compare:\n// strings.TrimSpace(host.Spec.PublicKey) == buf.String()  -> safe to send token\n// if mismatched, update host.Spec.PublicKey before bootstrapping","typeGuard":"func isSupportedSigningKey(key crypto.PublicKey) bool {\n\t_, ok := key.(*ecdsa.PublicKey)\n\treturn ok\n}","tryCatchPattern":"result, err := verifier.VerifyToken(ctx, req, authToken, body)\nif err != nil {\n\tif strings.Contains(err.Error(), \"failed to verify claim signature for node\") {\n\t\t// key mismatch: node key rotated or Host CR stale; rotate Host spec.publicKey then re-mint token and retry once\n\t\tif rotErr := rotateHostPublicKey(ctx, tokenInstanceName); rotErr != nil {\n\t\t\treturn nil, rotErr\n\t\t}\n\t\treturn retryWithFreshToken(ctx, req)\n\t}\n\treturn nil, err\n}","preventionTips":["Keep an ECDSA (P-256) key on nodes — verifySignature only supports *ecdsa.PublicKey and silently fails for other types.","Whenever a node's private key changes, update the kops Host spec.publicKey in the same operation.","Use pkibootstrap.NewAuthenticatorFromFile with the exact private key corresponding to the registered public key; verify the fingerprint at startup.","Watch kops-controller logs for \"key type %T not supported\" — it means the verifier rejected the key type before ECDSA verification.","Restrict who can edit kops-system Host objects to prevent public key tampering."],"tags":["go","pki","ecdsa","signature-verification","authentication"],"backgroundTag":"signature-verification-failed","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}