{"record":{"id":"6095a79ce5c92834","repo":"k3s-io/k3s","slug":"old-serviceaccount-signing-key-not-in-new-servicea","errorCode":null,"errorMessage":"old ServiceAccount signing key not in new ServiceAccount key list","messagePattern":"old ServiceAccount signing key not in new ServiceAccount key list","errorType":"validation","errorClass":null,"httpStatus":500,"severity":"error","filePath":"pkg/server/handlers/cert.go","lineNumber":254,"sourceCode":"// is also present in the new key list, to ensure that old signatures can still be validated.\nfunc validateServiceKey(oldKeyPath, newKeyPath string) error {\n\toldKeys, err := keyutil.PublicKeysFromFile(oldKeyPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tnewKeys, err := keyutil.PublicKeysFromFile(newKeyPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, key := range newKeys {\n\t\tif reflect.DeepEqual(oldKeys[0], key) {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn errors.New(\"old ServiceAccount signing key not in new ServiceAccount key list\")\n}\n","sourceCodeStart":236,"sourceCodeEnd":256,"githubUrl":"https://github.com/k3s-io/k3s/blob/6ba341e396edc16b8dcae978a7c5e3ac7ee5606e/pkg/server/handlers/cert.go#L236-L256","documentation":"caCertReplace in pkg/server/handlers/cert.go, when a new ServiceAccount signing key is supplied during rotate-ca, requires the current (old) signing key to appear in the new key file. oldKeys[0] is compared against every key in newKeys (reflect.DeepEqual); if absent it errors, because existing service account tokens would become invalid with no grace period for validation.","triggerScenarios":"'k3s certificate rotate-ca' with a --sa-key file that contains only the brand-new key and not the key currently at /var/lib/rancher/k3s/server/tls/service.key. The check compares parsed public keys, so re-encoding the old key in a different format still matches - only omission fails.","commonSituations":"Generating a fresh SA key pair and pointing rotate-ca at it alone; forgetting that rotation must be additive for tokens to stay valid.","solutions":["Append the old key to the new key file: 'cat /var/lib/rancher/k3s/server/tls/service.key new-sa.key > sa-bundle.key' and pass that as --sa-key.","Confirm both keys parse: 'openssl pkey -in sa-bundle.key -pubout' twice (or count 'BEGIN PRIVATE KEY' blocks) before submitting.","After rotation completes cluster-wide, the old key can be retired in a later step."],"exampleFix":"# before: only new key -> error\nopenssl genrsa -out sa.key 2048\nk3s certificate rotate-ca --sa-key=sa.key\n\n# after: old + new in one file\n cat /var/lib/rancher/k3s/server/tls/service.key sa.key > sa-bundle.key\nk3s certificate rotate-ca --sa-key=sa-bundle.key","handlingStrategy":"validation","validationCode":"// Ensure the old SA signing key is present in the new key file\noldPub, _ := keyutil.PublicKeysFromFile(\"/var/lib/rancher/k3s/server/tls/service.key\")\nnewPubs, _ := keyutil.PublicKeysFromFile(saBundlePath)\nfound := false\nfor _, k := range newPubs {\n    if reflect.DeepEqual(oldPub[0], k) {\n        found = true\n    }\n}\nif !found {\n    return errors.New(\"bundle must include the current service.key\")\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"not in new ServiceAccount key list\") {\n    // cat old service.key + new key into one file and retry rotate-ca\n}","preventionTips":["Always append the existing service.key to the new SA key bundle before rotate-ca.","Count 'BEGIN PRIVATE KEY' blocks - it must be >= 2 during rotation.","Retire the old key only after every node trusts the new one."],"tags":["k3s","certificates","service-account","key-rotation","validation"],"backgroundTag":null,"analyzedSha":"6ba341e396edc16b8dcae978a7c5e3ac7ee5606e","analyzedAt":"2026-08-15T16:27:54.286Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}