{"record":{"id":"029fc9cf88133927","repo":"slackhq/nebula","slug":"sshd-listen-must-be-provided","errorCode":null,"errorMessage":"sshd.listen must be provided","messagePattern":"sshd\\.listen must be provided","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ssh.go","lineNumber":84,"sourceCode":"\t\t\t\tssh.Stop()\n\t\t\t}\n\t\t\tif sshRun != nil {\n\t\t\t\tgo sshRun()\n\t\t\t}\n\t\t} else {\n\t\t\tssh.Stop()\n\t\t}\n\t})\n}\n\n// configSSH reads the ssh info out of the passed-in Config and\n// updates the passed-in SSHServer. On success, it returns a function\n// that callers may invoke to run the configured ssh server. On\n// failure, it returns nil, error.\nfunc configSSH(l *slog.Logger, ssh *sshd.SSHServer, c *config.C) (func(), error) {\n\tlisten := c.GetString(\"sshd.listen\", \"\")\n\tif listen == \"\" {\n\t\treturn nil, fmt.Errorf(\"sshd.listen must be provided\")\n\t}\n\n\t_, port, err := net.SplitHostPort(listen)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid sshd.listen address: %s\", err)\n\t}\n\tif port == \"22\" {\n\t\treturn nil, fmt.Errorf(\"sshd.listen can not use port 22\")\n\t}\n\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)","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/ssh.go#L66-L102","documentation":"configSSH reads the \"sshd.listen\" config key and requires a non-empty host:port value before it can configure the SSH server. If the key is missing or set to an empty string, configuration aborts with this error. It is a startup-time configuration validation, so the process cannot begin serving SSH without a listen address.","triggerScenarios":"Running the binary with no sshd.listen in the config file / environment (c.GetString(\"sshd.listen\", \"\") returns \"\"), or explicitly setting sshd.listen=\"\" in ssh.go's configSSH.","commonSituations":"Fresh deployments where the example config was not copied; renaming the key in a config migration; env var not exported so the default empty string is used; YAML indentation putting sshd.listen under the wrong block.","solutions":["Set sshd.listen in your config, e.g. sshd.listen = \"0.0.0.0:2222\".","Set the corresponding environment variable if config comes from env.","Check the config file is actually being loaded (correct path/flag) so the key is present."],"exampleFix":"// before (config)\n[sshd]\n# listen missing\n// after (config)\n[sshd]\nlisten = \"0.0.0.0:2222\"","handlingStrategy":"validation","validationCode":"if cfg.GetString(\"sshd.listen\", \"\") == \"\" {\n    return errors.New(\"sshd.listen is required: set it to e.g. 0.0.0.0:2222\")\n}","typeGuard":null,"tryCatchPattern":"run, err := configSSH(logger, srv, c)\nif err != nil {\n    if strings.Contains(err.Error(), \"sshd.listen must be provided\") {\n        logger.Error(\"missing required config key\", \"key\", \"sshd.listen\")\n        os.Exit(78) // EX_CONFIG\n    }\n    return err\n}","preventionTips":["Fail fast at boot with a startup config check listing all required sshd keys.","Keep a documented example config with every sshd.* key present.","Log the effective config (redacted) at startup to catch silently empty keys."],"tags":["ssh","configuration","startup","missing-config"],"backgroundTag":"missing-config-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"}