{"record":{"id":"44268516cd031004","repo":"slackhq/nebula","slug":"unknown-public-key-for-s-s","errorCode":null,"errorMessage":"unknown public key for %s (%s)","messagePattern":"unknown public key for (.+?) \\((.+?)\\)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sshd/server.go","lineNumber":74,"sourceCode":"\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false\n\t\t},\n\t\tUserKeyFallback: func(c ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {\n\t\t\tpk := string(pubKey.Marshal())\n\t\t\tfp := ssh.FingerprintSHA256(pubKey)\n\n\t\t\ts.authLock.RLock()\n\t\t\tdefer s.authLock.RUnlock()\n\t\t\ttk, ok := s.trustedKeys[c.User()]\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"unknown user %s\", c.User())\n\t\t\t}\n\n\t\t\t_, ok = tk[pk]\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"unknown public key for %s (%s)\", c.User(), fp)\n\t\t\t}\n\n\t\t\treturn &ssh.Permissions{\n\t\t\t\t// Record the public key used for authentication.\n\t\t\t\tExtensions: map[string]string{\n\t\t\t\t\t\"fp\":   fp,\n\t\t\t\t\t\"user\": c.User(),\n\t\t\t\t},\n\t\t\t}, nil\n\n\t\t},\n\t}\n\n\ts.config = &ssh.ServerConfig{\n\t\tPublicKeyCallback: cc.Authenticate,\n\t\tServerVersion:     fmt.Sprintf(\"SSH-2.0-Nebula???\"),\n\t}\n","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/sshd/server.go#L56-L92","documentation":"After the user is found, the SSH authenticator compares the presented public key against the trusted keys registered for that user. If the key's fingerprint is not in the set, auth fails with 'unknown public key'. The message includes the username and the SHA256 fingerprint of the offered key to aid debugging.","triggerScenarios":"SSH client authenticates to nebula's SSH debug server with a key that is not listed under that user's trusted keys in the config — wrong key file, key rotation, or agent offering an unexpected key.","commonSituations":"Rotated SSH keys without updating nebula config; ssh-agent offering the default key instead of the trusted one; copy/paste truncated the public key in the YAML; connecting as the right user but with a personal key not whitelisted.","solutions":["Add the fingerprint shown in the error message to that user's trusted keys in the nebula ssh config","Force the correct key: ssh -i /path/to/trusted_key -o IdentitiesOnly=yes","Verify the configured key is complete and correctly indented in the config, then reload","If the key was rotated, replace the old entry with the new public key and restart/reload nebula"],"exampleFix":"// before\ntrusted_users:\n  admin:\n    - \"ssh-ed25519 AAAAOLD...\"\n// after\ntrusted_users:\n  admin:\n    - \"ssh-ed25519 AAAANEW...\"","handlingStrategy":"validation","validationCode":"# Compute the fingerprint the server will compare against and confirm it is configured\nssh-keygen -lf ~/.ssh/id_ed25519.pub   # compare with entries under trusted_users in nebula.yaml","typeGuard":null,"tryCatchPattern":"try {\n  sshConnect(user, keyPath)\n} catch (e) {\n  if (e.message.includes(\"unknown public key\")) {\n    const fp = /\\(([A-Za-z0-9+/=]+)\\)/.exec(e.message)?.[1]\n    console.error(`key ${fp} not trusted for this user; add it to nebula config or use IdentitiesOnly`)\n  }\n  throw e\n}","preventionTips":["Use ssh -o IdentitiesOnly=yes -i <trusted_key> so the agent doesn't offer an untrusted key","Pin the key fingerprint in the nebula config and diff it after any key rotation","Paste full single-line public keys; verify with ssh-keygen -lf before deploying"],"tags":["ssh","authentication","public-key","nebula"],"backgroundTag":"unauthorized-public-key","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}