slackhq/nebula · critical
failed to parse private key: %s
Error message
failed to parse private key: %s
What it means
SetHostKey parses the SSH host private key with golang.org/x/crypto/ssh.ParsePrivateKey and wraps any parse failure as 'failed to parse private key'. This key is what the nebula SSH debug server presents as its host key. Called from configSSH during config load, so a bad key aborts startup.
Source
Thrown at sshd/server.go:107
PublicKeyCallback: cc.Authenticate,
ServerVersion: fmt.Sprintf("SSH-2.0-Nebula???"),
}
s.RegisterCommand(&Command{
Name: "help",
ShortDescription: "prints available commands or help <command> for specific usage info",
Callback: func(a any, args []string, w StringWriter) error {
return helpCallback(s.commands, args, w)
},
})
return s, nil
}
func (s *SSHServer) SetHostKey(hostPrivateKey []byte) error {
private, err := ssh.ParsePrivateKey(hostPrivateKey)
if err != nil {
return fmt.Errorf("failed to parse private key: %s", err)
}
s.config.AddHostKey(private)
return nil
}
func (s *SSHServer) ClearTrustedCAs() {
s.authLock.Lock()
s.trustedCAs = []ssh.PublicKey{}
s.authLock.Unlock()
}
func (s *SSHServer) ClearAuthorizedKeys() {
s.authLock.Lock()
s.trustedKeys = make(map[string]map[string]bool)
s.authLock.Unlock()
}
View on GitHub (pinned to dd8f660c0a)
Solutions
- Generate a supported key: ssh-keygen -t ed25519 -m PEM -N '' -f nebula_host_key (no passphrase)
- Point the ssh.host_key config at the private key file, not the .pub file
- Decrypt or regenerate the key without a passphrase (ParsePrivateKey does not prompt)
- If the key is PKCS#1/OpenSSH-format incompatibility, convert with ssh-keygen -p -m PEM
Example fix
// before ssh-keygen -t ed25519 -f host_key # passphrase-protected, parse fails // after ssh-keygen -t ed25519 -m PEM -N "" -f host_key
Defensive patterns
Strategy: validation
Validate before calling
# Validate the host key parses before deploying nebula
ssh-keygen -y -f host_key > /dev/null && echo OK
# Or in Go:
if _, err := ssh.ParsePrivateKey(hostKeyBytes); err != nil {
log.Fatalf("host key invalid: %v", err)
} Try / catch
try {
loadConfig(path)
} catch (e) {
if (e.message.includes("failed to parse private key")) {
console.error("check ssh.host_key: must be an unencrypted PEM private key, not the .pub file")
}
throw e
} Prevention
- Generate keys with no passphrase: ssh-keygen -t ed25519 -m PEM -N ""
- Point ssh.host_key at the private key file, never the .pub
- Run a parse check (ssh-keygen -y) as part of config CI before rollout
When it happens
Trigger: configSSH passes the raw bytes of ssh.host_key (file contents) to SetHostKey and ParsePrivateKey fails: unsupported key format, encrypted (passphrase-protected) key, empty or truncated file, or the config pointed at a public key / non-key file.
Common situations: Generating an RSA key with new OpenSSH format not supported by an older library version; using a passphrase-protected key without decrypting it; pointing host_key at the .pub file; YAML path mistakes yielding empty bytes.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/d2debf1e3432ece8.
Report an issue: GitHub.