slackhq/nebula · error

error while loading sshd.host_key file: %s

Error message

error while loading sshd.host_key file: %s

What it means

When sshd.host_key is a path (not an inline PEM literal), configSSH reads it with os.ReadFile. Any read failure — missing file, permission denied, wrong path — is wrapped as "error while loading sshd.host_key file". The server cannot start without the host key bytes.

Source

Thrown at ssh.go:106

	if err != nil {
		return nil, fmt.Errorf("invalid sshd.listen address: %s", err)
	}
	if port == "22" {
		return nil, fmt.Errorf("sshd.listen can not use port 22")
	}

	hostKeyPathOrKey := c.GetString("sshd.host_key", "")
	if hostKeyPathOrKey == "" {
		return nil, fmt.Errorf("sshd.host_key must be provided")
	}

	var hostKeyBytes []byte
	if strings.Contains(hostKeyPathOrKey, "-----BEGIN") {
		hostKeyBytes = []byte(hostKeyPathOrKey)
	} else {
		hostKeyBytes, err = os.ReadFile(hostKeyPathOrKey)
		if err != nil {
			return nil, fmt.Errorf("error while loading sshd.host_key file: %s", err)
		}
	}

	err = ssh.SetHostKey(hostKeyBytes)
	if err != nil {
		return nil, fmt.Errorf("error while adding sshd.host_key: %s", err)
	}

	// Clear existing trusted CAs and authorized keys
	ssh.ClearTrustedCAs()
	ssh.ClearAuthorizedKeys()

	rawCAs := c.GetStringSlice("sshd.trusted_cas", []string{})
	for _, caAuthorizedKey := range rawCAs {
		err := ssh.AddTrustedCA(caAuthorizedKey)
		if err != nil {
			l.Warn("SSH CA had an error, ignoring", "error", err, "sshCA", caAuthorizedKey)
			continue

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Verify the path exists and is readable by the process user (ls -l / test with os.ReadFile).
  2. Use an absolute path in sshd.host_key instead of a relative one.
  3. If providing an inline key, ensure it includes the "-----BEGIN ... PRIVATE KEY-----" header so it isn't treated as a path.

Example fix

// before
host_key = "host_key" // relative, wrong cwd
// after
host_key = "/etc/myapp/ssh/host_key"
Defensive patterns

Strategy: validation

Validate before calling

hk := cfg.GetString("sshd.host_key", "")
if !strings.Contains(hk, "-----BEGIN") { // treated as a path
    if _, err := os.Stat(hk); err != nil {
        return fmt.Errorf("sshd.host_key file unreadable: %w", err)
    }
}

Try / catch

run, err := configSSH(logger, srv, c)
if err != nil {
    var pe *fs.PathError
    if strings.Contains(err.Error(), "error while loading sshd.host_key file") {
        logger.Error("cannot read sshd.host_key file; check path and permissions")
        os.Exit(78)
    }
    _ = pe
    return err
}

Prevention

When it happens

Trigger: sshd.host_key contains a path and os.ReadFile fails: nonexistent file, unreadable permissions, wrong working directory for a relative path, or the value is a PEM body without "-----BEGIN" so it is treated as a path (ssh.go:106).

Common situations: Container images that never copied the key file; running the binary as a non-root user lacking read permission; relative paths resolving differently under systemd's WorkingDirectory; pasting a public key or truncated key without the BEGIN header.

Related errors


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/4df526d02fc1f300. Report an issue: GitHub.