{"record":{"id":"82bab6cca1452046","repo":"kubernetes/kops","slug":"keypair-is-distrusted","errorCode":null,"errorMessage":"keypair is distrusted","messagePattern":"keypair is distrusted","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/kops/promote_keypair.go","lineNumber":182,"sourceCode":"\tif keypairID == \"\" {\n\t\thighestCandidateId := big.NewInt(0)\n\t\tfor id, item := range keyset.Items {\n\t\t\tif item.PrivateKey != nil && item.DistrustTimestamp == nil && item.Certificate != nil {\n\t\t\t\titemId, ok := big.NewInt(0).SetString(id, 10)\n\t\t\t\tif ok && highestCandidateId.Cmp(itemId) < 0 {\n\t\t\t\t\thighestCandidateId = itemId\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tkeypairID = highestCandidateId.String()\n\t\tif keypairID == keyset.Primary.Id {\n\t\t\tfmt.Fprintf(out, \"No %s keypair newer than current primary %s\\n\", name, keypairID)\n\t\t\treturn nil\n\t\t}\n\t} else if item := keyset.Items[keypairID]; item != nil {\n\t\tif item.DistrustTimestamp != nil {\n\t\t\treturn fmt.Errorf(\"keypair is distrusted\")\n\t\t}\n\t\tif item.PrivateKey == nil {\n\t\t\treturn fmt.Errorf(\"keypair has no private key\")\n\t\t}\n\t\tif item.Certificate == nil {\n\t\t\treturn fmt.Errorf(\"keypair has no certificate\")\n\t\t}\n\t} else {\n\t\treturn fmt.Errorf(\"keypair not found\")\n\t}\n\n\tkeyset.Primary = keyset.Items[keypairID]\n\terr = keyStore.StoreKeyset(ctx, name, keyset)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"writing keyset: %v\", err)\n\t}\n\n\tfmt.Fprintf(out, \"Promoted %s %s\\n\", name, keypairID)","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/cmd/kops/promote_keypair.go#L164-L200","documentation":"When an explicit keypair ID is given, promoteKeypair refuses to promote it if the keyset item carries a DistrustTimestamp — the keypair has been deliberately distrusted (e.g. via `kops distrust keypair` or automatic rotation) and can no longer be used for signing. Promoting it would reintroduce a compromised/retired credential, so the operation fails.","triggerScenarios":"`kops promote keypair <keyset> <id>` where keyset.Items[id].DistrustTimestamp != nil — i.e. the ID was previously distrusted with `kops distrust keypair` or distrusted during a completed rotation.","commonSituations":"Trying to roll back a rotation by re-promoting an old, already-distrusted keypair; scripting promotion with a hardcoded ID captured before the keypair was distrusted; recovering from a bad rotation by re-enabling a retired cert (which kOps blocks for safety).","solutions":["Choose a different, still-trusted keypair ID: run `kops get keypairs <keyset>` and pick one without a DISTRUST timestamp.","If no trusted newer keypair exists, issue a fresh one (`kops create keypair <keyset>` or re-run rotation) and promote that.","Do not re-promote a distrusted keypair; if a rollback is truly required, investigate kOps' documented rotation-rollback procedure rather than force-promoting.","Remove hardcoded keypair IDs from automation and use the implicit \"promote newest\" mode (omit the ID)."],"exampleFix":"// before: promotes a distrusted keypair\nkops promote keypair service-account 5938372002934847 --name c.example.com\n// error: keypair is distrusted\n// after: list keypairs, pick a trusted one (or omit ID to auto-select newest)\nkops get keypairs service-account --name c.example.com\nkops promote keypair service-account --name c.example.com","handlingStrategy":"validation","validationCode":"// shell: ensure the chosen keypair ID is not distrusted before promoting\nkops get keypairs \"$KEYSET\" --name \"$CLUSTER\" --state \"$KOPS_STATE_STORE\" -o yaml \\\n  | awk -v id=\"$KEYPAIR_ID\" '/id: /{cur=$2} /distrustTimestamp:/{d[cur]=1} END{exit !d[id]}' \\\n  && { echo \"$KEYPAIR_ID is distrusted; choose another ID\"; exit 1; } || true","typeGuard":null,"tryCatchPattern":"if kops promote keypair \"$KEYSET\" \"$KEYPAIR_ID\" --name \"$CLUSTER\" 2>&1 | grep -q 'keypair is distrusted'; then\n  echo \"$KEYPAIR_ID was distrusted (rotation/compromise); issuing a fresh keypair instead\"\n  # select a trusted ID from kops get keypairs and retry, or omit the ID to auto-promote the newest\nelse\n  kops promote keypair \"$KEYSET\" \"$KEYPAIR_ID\" --name \"$CLUSTER\"\nfi","preventionTips":["Never hardcode keypair IDs in rotation scripts; omit the ID to auto-promote the newest trusted candidate.","Check DISTRUST timestamps in `kops get keypairs` output before selecting an ID.","Do not attempt to roll back rotations by re-promoting distrusted keys.","Capture keypair IDs only at issue time and treat them as invalid after any distrust operation."],"tags":["keypair","distrust","rotation","security"],"backgroundTag":"keypair-distrusted","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}