{"record":{"id":"16d14c38df32eeb6","repo":"slackhq/nebula","slug":"error-while-adding-sshd-host-key-s","errorCode":null,"errorMessage":"error while adding sshd.host_key: %s","messagePattern":"error while adding sshd\\.host_key: (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ssh.go","lineNumber":112,"sourceCode":"\n\thostKeyPathOrKey := c.GetString(\"sshd.host_key\", \"\")\n\tif hostKeyPathOrKey == \"\" {\n\t\treturn nil, fmt.Errorf(\"sshd.host_key must be provided\")\n\t}\n\n\tvar hostKeyBytes []byte\n\tif strings.Contains(hostKeyPathOrKey, \"-----BEGIN\") {\n\t\thostKeyBytes = []byte(hostKeyPathOrKey)\n\t} else {\n\t\thostKeyBytes, err = os.ReadFile(hostKeyPathOrKey)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error while loading sshd.host_key file: %s\", err)\n\t\t}\n\t}\n\n\terr = ssh.SetHostKey(hostKeyBytes)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error while adding sshd.host_key: %s\", err)\n\t}\n\n\t// Clear existing trusted CAs and authorized keys\n\tssh.ClearTrustedCAs()\n\tssh.ClearAuthorizedKeys()\n\n\trawCAs := c.GetStringSlice(\"sshd.trusted_cas\", []string{})\n\tfor _, caAuthorizedKey := range rawCAs {\n\t\terr := ssh.AddTrustedCA(caAuthorizedKey)\n\t\tif err != nil {\n\t\t\tl.Warn(\"SSH CA had an error, ignoring\", \"error\", err, \"sshCA\", caAuthorizedKey)\n\t\t\tcontinue\n\t\t}\n\t}\n\n\trawKeys := c.Get(\"sshd.authorized_users\")\n\tkeys, ok := rawKeys.([]any)\n\tif ok {","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/ssh.go#L94-L130","documentation":"After loading the host key bytes, configSSH passes them to ssh.SetHostKey. If the sshd library cannot parse or accept the key (bad format, unsupported type, corrupt data), the failure is wrapped as \"error while adding sshd.host_key\". The file was read successfully, but its content is not a usable SSH host key.","triggerScenarios":"ssh.SetHostKey(hostKeyBytes) returns an error in ssh.go's configSSH — e.g. the file contains a public key, an encrypted/unparseable PEM, invalid DER, or an empty/whitespace file.","commonSituations":"Pointing sshd.host_key at a .pub file; keys generated with formats the library doesn't support (e.g. new OpenSSH encapsulation the parser rejects); a truncated download; editing the key file and corrupting the base64 body.","solutions":["Regenerate the key with ssh-keygen -t ed25519 -m PEM -f host_key and use the private key file.","Validate the file content (ssh-keygen -y -f host_key) to confirm it parses as a private key.","Check that SetHostKey in the vendored sshd library supports your key format and re-export accordingly."],"exampleFix":"// before\nhost_key = \"/etc/ssh/host_key.pub\" // public key, unparseable\n// after\nhost_key = \"/etc/ssh/host_key\" // ed25519 private key in PEM","handlingStrategy":"validation","validationCode":"b, err := os.ReadFile(keyPath)\nif err != nil {\n    return err\n}\nif _, err := ssh.ParseRawPrivateKey(b); err != nil {\n    return fmt.Errorf(\"sshd.host_key is not a parseable private key: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"run, err := configSSH(logger, srv, c)\nif err != nil {\n    if strings.Contains(err.Error(), \"error while adding sshd.host_key\") {\n        logger.Error(\"sshd.host_key content rejected by SetHostKey; regenerate the key\")\n        os.Exit(78)\n    }\n    return err\n}","preventionTips":["Point sshd.host_key at a private key, never a .pub file.","Generate keys as ed25519 PEM (ssh-keygen -t ed25519 -m PEM) and verify with ssh-keygen -y.","Add a CI smoke test that configures the SSH server with a freshly generated key."],"tags":["ssh","configuration","host-key","key-format"],"backgroundTag":"invalid-ssh-host-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"}